markk: Ich möchte professioneller an meine Projekte rangehen

Beitrag lesen

Moin,

neben den bereits anderen guten Antworten möchte ich ein paar technische und me­tho­do­lo­gisch Anreize und Tools in den Raum werfen )ohne dabei zu sehr auf deine erwaehnte Probleme einzugehen) - ausgehend von deinem Java-Background und der Aussage, dass du dich gerne weiter entwickeln moechtest.

Ich entwickle seit einigen Jahren ausschließlich testgetrieben. Testgetrieben zu entwickeln bedeutet den Code so zu implementieren, dass er einfacher testbar ist. Allein durch diese Vorgehensweise folgt man recht intuitiv bekannten Pattern und Prinzipien und erhoeht Qualitaet und Wartbarkeit.

SOLID

Beispiele:

a) Code under test darf keine neue Instanzen erstellen oder auf static Methoden zugreifen, da diese nicht (lies: nur sehr schlecht) testbar sind. Spring-Boot liefert hierzu beste Voraussetzungen. Dependencies werden injected, können in den Tests als Mock übergeben werden. --> (Teile von) Inversion of control werden erfüllt.

b) Angenommen man hat einen Converter-Service, der durch eine beliebige Liste von Converter iteriert und Daten von x nach y converted:

if(myConverter.class == XMLConverter)
   myConverter.convertToXml()
else if(myConverter.class == PDFConverter)
   myConverter.convertToPDF()

Dies ist recht aufwendig zu testen, und bei jeder Erweiterung muss der Service modifiziert werden. --> Testgetrieben fällt einem bereits vor der Implementierung auf, dass das Open-close-principle nicht eingehalten wird: Open for extension, close for modification. OCP folgend wird der Code in etwa so aussehen, da alle Converter einem Interface folgen.

myConverters.foreach {
    it.convert()
}

c) Da fuer eine Unit sehr haeufig drei/vier oder mehr Tests (plus Negativtest(s)) erstellt werden, merkt man beim Testen bereits, dass eine Klasse eventuell zu viele Aufgaben hat. Spaetestens wenn dann noch n Services gemockt werden muessen, refactored man die unit under test in mehrere Units. --> single-responsibility, seperation of concerns werden eingehalten.

Ich koennte hier noch recht viele Beispiele nennen, aber ich denke du weiszt worauf ich hinaus moechte..

Lesestoff:

Testing Tools

Testgetrieben zu entwickeln bedeutet, dass man den Spring-Container haeufig lediglich dazu starten muss, dass der Test ausgefuehrt werden kann. Da das ein paar Sekunden in Anspruch nimmt und ein Entwickler ungerne wartet, empfiehlt es sich sehr dem Prinzip der Testing-Pyramide zu folgen:

  • Viele Unit-Tests
  • Einige Integration-Tests
  • Wenige Functional-(E2E)-Tests

Spring-Boot bietet allerdings an, fuer Integration-Tests nicht den gesamten Container starten zu muessen. Das heisst du kannst auf die Annotation @RunWith(SpringRunner.class) verzichten, und dennoch wird der ServletContext initialisiert, sprich Routes werden erstellt, Annotations interpretiert, Beans geladen etc.. Beispiel Request innerhalb eines Tests:

MockHttpServletResponse response = this.mockMvc.perform(
    MockMvcRequestBuilders
        .get(url)
        .accept(MediaType.APPLICATION_JSON))
        .andReturn()
        .response

Ich persönlich nutze (wenn ich die Auswahl habe) in fast allen Projekten das Spock-Framework. Dies ist ein Testing-Framework basierend auf Groovy. Beispiel:

def 'getById: convert businessAreaEntity to DTO and return it'() {
    given:
    businessAreaServiceMock.findById(_, _) >> ANY_BUSINESS_AREA_ENTITY_1

    when:
    BusinessAreaDTO result = underTest.getById(AUTH_USER, ANY_BUSINESS_AREA_ID_1)

    then:
    1 * objectConverterMock.createDto(ANY_BUSINESS_AREA_ENTITY_1) >> ANY_BUSINESS_AREA_DTO_1
    result == ANY_BUSINESS_AREA_DTO_1
}

Durch die benannten Methoden und given-when-then-Syntax sind die Tests sehr einfach zu lesen und zu verstehen.

Fuer das Mocking von 3rd-Party-Diensten (oder generell Requests, die nach "drauszen" gehen) kann ich Wiremock empfehlen. Hierueber kann man ausgehende Request abfangen und beliebige Responses emulieren.

Lesestoff:

Module Design

  1. Wie Definiere ich welche Module sinnvoll wären? Und Kapsle ich den Code per Api-Server-Client Architektur oder macht das wenig Sinn, weil ich die ganze Anwendung besser als ein Module definieren sollte? Je mehr Module ich hab desto beschissener wird es den Code zu implementieren, weil ich dann Kreuz und Quer durch die Module klicken muss... andererseits wäre es besser die richtigen Module sofoert anzulegen als später per Refactoring zu modularisieren... könnt ihr mir hierzu ein paar Tipps geben?

Stateless RESTful-JSON-APIs auf Microservice-Basis sind so ziemlich Standard in der heutigen Web-Welt. Das Trennen von Frontend-Logik und Backend auch. Wegen der von dir erwaehnten Gefahr bezgl. einer kreuz-und-queren-Kommunikation gibt es wohl definierte software designs. Je nachdem, fuer welche du dich entscheidest, klaeren sich diese Probleme eigentlich recht schnell.

Beispiel (vereinfacht) anhand DDD (domain-driven-design) Es ist klar definiert, wo welche Logik zu implementieren ist. Frontend im UI-Layer, Handling im Application-Layer, Business-Logik im Domain-Layer, Repositories (Persistence) im Infrastructure-Layer. Wird eine Domain zu grosz, wird sie in einzelne Module unterteilt und es greift das Aggregation pattern: Mehrere Module werden von einem root-Modul orchestriert.

Beispiel (vereinfacht) anhand SOA (service-oriented-architecture) Jedes Modul ist self-contained, also in sich selbst gekapselt, ist eine Blackbox nach auszen, und spiegelt eine eindeutige Business-Activity mit wohl-definiertem Output wieder. Die Kommunikation zwischen Modulen findet ueber Contracts statt. D.h. auch hier findet eine explizite Definition von Modulen statt.

Natuerlich gibt es noch einige andere Loesungsansaetze in der Software-Architektur.

Lesestoff:

Monitoring

Wie auch immer man sich entscheidet, durch distributed services kommen ein paar Komplikation auf einen zu, vor allem wenn man mitbekommen moechte, was genau wo und wann geschieht.

Hier hat sich der ELK-Stack (Elasticsearch, Logstash, Kibana) sehr etabliert. (Neuerdings ist da noch Beat hinzugekommen).

Der Ablauf des ELK-Stacks kurz skizziert:

  • Jeder Microservice loggt in sein eigenes Logfile.
  • Filebeat pushed changes nach Logstash
  • Logstash parsed anhand definierter Grok-Patterns die unterschiedlichen Logfiles und sendet sie an Elasticsearch
  • Kibana liest die Daten aus Elasticsearc aus und visualisiert sie entsrepchend.

Lesestoff:

Continuous deployment, Containers

Auch zum mittlerweiligen Standard gehoert das kontinuierliche Deployment der Anwendungen. Hier gibt es diverse Anbieter - von kostenfrei bis zu Enterprise, name-dropping: Teamcity, Jenkins, Gitlab CI.

Lesestoff:

Diverses

Da du ja sehr interessiert bist, neue Dinge zu lernen, moechte ich an dieser Stelle (kontextlos) noch Histrix, Docker und Kubernetes, so wie AWS serverless erwaehnen.

Cheers, markk