10 Learnings aus 10 Jahren Microservice Entwicklung
Wie alles begann
Ich war 2014 Teil eines großen E-Commerce-Projektes. Wie alle Teams damals ist sind wir auf den Microservice-Zug aufgesprungen. Wir haben damals versucht, mit mehr als suboptimalen Frameworks und Tools "Microservices" zu entwickeln. Ein Segen aus heutiger Sicht war damals der Betrieb. Es gab schlichtweg ein hartes Nein! und keine Möglichkeit, dutzende Microservices in eine produktive Umgebung zu deployen.
Grenzen machen kreativ, und ich habe damals sehr sehr viel gelernt.
Wir werden gleich sehen, wie mir das geholfen hat.
10 grundlegende Dinge, die ich für die Entwicklung von (Micro)Services in den letzten 10 Jahren gelernt habe und die sich immer und immer wieder bewähren.
Keiner dieser grundlegenden Learnings hat mit Frameworks oder konkreten Technologien zu tun. Je länger du in der IT arbeitest, desto schneller merkst du wie unwichtig das alles ist. Kommen wir aber zu den versprochenen Learnings..
1) Microservice, Nanoservice, Vertical, Monolith, Modulith...
Die Unterscheidung, was genau du baust, interessiert keinen Menschen. "Wie groß darf dein Microservice sein?" - völlig unwichtig. Das einzig Wichtige? Wie gut ist das, was du da baust, strukturiert. Meine Empfehlung, wenn ich heute auf der grünen Wiese starte: 100% ein sauber modularisierter Monolith (Modulith). Die physikalische Trennung ist fast immer kompletter Overhead. Zurück in 2012 waren wir gezwungen genau das zu machen, das Ergebnis ist auch heute noch in Produktion und wird von tausenden Menschen täglich genutzt. Die Struktur von damals hält auch heute noch.
2) Data Flow...
Der wahrscheinlich wichtigste Punkt dieser Liste. Du musst mit einfachen Worten erklären können, wie die Daten durch deinen Service fließen.
"Input per Kafka → Record wird verarbeitet → Aggregate persistiert → Projektionen über HTTP, Kafka Connect und Kafka Stream nach außen gegeben".
Kannst du das nicht so einfach erklären, stimmt etwas nicht.
3) Unabhängigkeit...
Egal in welcher Konstellation du Services entwickelst, dein Service selbst muss unabhängig von der Umgebung sein. Wenn du erst Kafka, 2 Datenbanken und 4 Fremd-Systeme starten musst, um "schnell mal" ein Problem nachzustellen, dann stimmt etwas nicht.
Auch wenn du nicht die Möglichkeit hast, deine Businesslogik innerhalb von 2 Minuten zu debuggen, stimmt etwas nicht. Wir kommen nachher noch dazu.
Dieses Learning passt perfekt zu Streaming basierten Services mit Kafka. Wenn du mehr dazu erfahren willst, freue ich mich von dir zu hören.
4) Interfaces...
Dein Service basiert auf Interfaces. Die Implementierung ist ein unwichtiges Detail. Wenn ich neue Services oder Module konzipiere, starte ich IMMER mit den Interfaces und stecke die Interfaces wie Legobausteine zusammen. Danach weiß ich genau, welche Bausteine ich brauche. Wenn du nur Interfaces hast, ist es unmöglich, Konzepte, die nicht zusammengehören, zu vermengen. Und du kannst bereits erste Tests nur gegen die Interfaces schreiben. Damit weißt du, dass die Struktur valide ist. Dafür brauchst du keine einzige Implementierung. Wenn die Interfaces richtig strukturiert sind, ist die Implementierung am Ende einfach. Du kannst sie sogar outsourcen. Wenn du nicht weißt, welche Interfaces du brauchst, dann stimmt etwas nicht.
5) DRY...
"LIBRARIES IN DER MICROSERVICE WELT SIND SCHLECHT!"
"WIR WOLLEN ABHÄNGIGKEITEN VERMEIDEN!"
Nonsense.
Was für unnötige Aufwände, Kosten und Bugs! Statt eine sauber strukturierte Library werden Änderungen lieber 3, 4 oder 5 mal gemacht, in nahezu identischen Klassen. Als Architekt und auch als Entwickler kann ich mich hier nur wundern, wie jemand bei klarem Verstand diesen Umstand akzeptieren kann. Gemeinsam genutzte, nicht fachliche Funktionalität gehört in Libraries. Die "Abhängigkeiten" lassen sich über saubere Prozesse problemlos abbilden. Wenn du Änderungen mehr als einmal machen musst, dann stimmt sogar ganz sicher etwas nicht.
Dein Team arbeitet mit Kafka und Streaming Technologien und ihr habt das Gefühl es läuft nicht so richtig rund? Du hast das Gefühl es fehlt an Know How und Erfahrung? Vielleicht können wir helfen. Jetzt mehr erfahren. Du kannst hier natürlich auch gleich einige technische Fragen platzieren.6) Abstraktion...
Dieser Punkt geht Hand in Hand mit der zuvor beschriebenen Verwendung von Interfaces. Alles Externe wird von der Applikation ferngehalten (abstrahiert). In deinem Code hat die Infrastruktur nichts zu suchen. Eine Connector-Klasse aus der Salesforce Library? Musst du wrappen. Bau ein Interface dafür. AWS SDK für Dynamo DB? Muss auf jeden Fall hinter einem Interface versteckt werden. Das "Konzept" ist nicht DynamoDB, das "Konzept" ist etwas anderes. Nenn dein Interface nach dem Konzept. Warum? Dein Sicherheitsnetz gegen Breaking Changes und die Sicherheit, dass du mit dem "Konzept" und nicht mit der "Implementierung" arbeitest. Wenn du direkt in deinen Businessklassen mit dem AWS SDK arbeitest, dann stimmt etwas nicht.
7) Unabhängigkeit
Deine Dependencies müssen sauber gemanagt werden. Und damit meine ich nicht das Ticket, das der Architekt wegen der Arc42 Violation aufgemacht hat. Jedes Modul definiert eine API. Hier sind die Interfaces aus Learning 4 definiert. Die API schreibst du zuerst (kann beispielsweise ein eigenes Maven Modul sein). Damit ist es für andere Module unmöglich, sich auf eine konkrete Implementierung zu verlassen, da zur Compile-Time nur Interfaces verfügbar sind. Die Implementierung kommt erst ganz am Ende dazu. Wenn deine Abhängigkeiten keiner klaren Struktur folgen, dann stimmt etwas nicht.
8) Automatisierung...
Alles, wirklich alles in deinem Service muss automatisiert sein. Automatisierung ist der Schlüssel. Tests... laufen natürlich vollautomatisch. Qualitätssicherung... machst du vollautomatisch. Dokumentation... ist voll automatisiert. Library-Upgrades... machst du vollautomatisch. Deployments... machst du vollautomatisch. Wenn du manuelle Schritte hast, die "jeden Sprint" immer wieder gemacht werden müssen, dann stimmt etwas nicht.
9) Qualität...
Deine Tests sind das A&O. Vor allem zusammen mit Learning 8)
Unit Tests sichern die Grundfunktionalität.
Integration Tests sichern das Zusammenspiel aller Module.
Um die Applikation zu debuggen, reicht es idealerweise, den Integration Test zu starten (das sind die 2 Minuten aus Learning 3).
10) Fail Fast, Fail Early, Fail hard.. Repeat
Der größte Fehler, den du machen kannst, ist "zu warten". Warten mit dem Library-Upgrade, Warten mit dem Modul-Upgrade, Warten mit dem Deployment, Warten mit... egal womit. Wenn etwas schief geht, dann bitte schnell und so, dass wir es sofort sehen. Automatisiere alles. Wenn die Library sich ändert, dann zähl die Version automatisch in jedem Service hoch und lass es krachen. Je früher, desto einfacher. Wenn du in deinem Service 6 Versionen hinter der Library bist, dann stimmt etwas nicht.
Neugierig geworden? Mit unserer Expertise im Bereich Kafka, Kotlin und Spring bieten wir die Möglichkeit ihre Entwicklung zu beschleunigen und produktiver zu machen.
Ein unabhängiges Architektur-Review schafft Klarheit über den Status Quo.
In unserem kostenlosen Kennenlern-Call haben Sie zudem die Möglichkeit, kostenlos erste Fragen zu platzieren.