Microservices? Oder lieber Monolithen?

Continuous Architecture  –  75 Kommentare

Microservices sind ein Hype. So dauert es nicht lange, bis sich die Kritiker melden und wieder zurück zu Monolithen wollen. Aber hilft das weiter?

Microservices teilen ein System in kleine, unabhängig deploybare Module auf. Die Alternative sind Deployment-Monolithen. Dann wird die gesamte Anwendung mit allen Modulen als Ganzes auf einmal deployt. Intern können Deployment-Monolithen aus Modulen wie Java-Packages, Namespaces oder Bibliotheken aufgebaut sein.

Aktuell ziehen Microservices Kritik auf sich, weil Microservice-System oft keine gute Struktur aufweisen. Aber Microservices definieren nur, wie die Struktur umgesetzt wird. Wie gut die Struktur ist, hängt nicht davon ab, ob die Module Microservices sind oder klassische Module. Domain-driven Design (DDD) und Bounded Context sind nützliche Techniken, um eine gute Struktur zu erreichen. Sie erleben dank Microservices gerade eine Renaissance. Wenn aus der Microservices-Diskussionein Fokus auf DDD und besser strukturierte Software entsteht, ist das schon ein wichtiger Erfolg – egal ob als Module Microservices genutzt werden oder nicht.

Ausgerechnet Deployment-Monolithen als Lösung für schlecht strukturierte Systeme zu propagieren, erscheint mir aber widersinnig. Ich habe viele Reviews von Deployment-Monolithen erstellt. Die Strukturen der untersuchten Monolithen ließ fast immer zu wünschen übrig. Meine Erklärung: Zu leicht schleichen sich im Sourcecode unbeabsichtigte Abhängigkeiten zwischen Modulen ein. Die einzige Ausnahme ist, wenn im Projekt ein Architekturmanagement-Werkzeug genutzt wird oder andere Maßnahmen ergriffen werden, um die Modulgrenzen zu erzwingen. Das war aber nur bei wenigen von mir untersuchten Deployment-Monolithen der Fall.

Da Microservices eigene Sourcecode-Projekte haben und Schnittstellen zwischen den Microservices zum Beispiel als REST-Schnittstellen implementiert sind, schleichen sich bei Microservices Abhängigkeiten nicht so einfach ein. Schließlich muss man eine Abhängigkeit zu einem anderen Sourcecode-Projekt erzeugen und eine Schnittstelle bedienen. Das ist viel aufwendiger als beispielsweise die Nutzung einer Klasse aus einem anderen Java-Package. Die Aufteilung in Microservices unterstützt also das Einhalten der Modulgrenzen.

Wenn eine gute Struktur festgelegt worden ist, lässt sie sich mit Microservices umsetzen. Deployment-Monolithen erfordern weitere Maßnahmen, um die Struktur dauerhaft abzusichern.

Es scheint so, als wäre von diesen beiden Alternativen ein Deployment-Monolith wegen der niedrigeren Komplexität die bessere Wahl. Nur ein System muss betrieben und ausgeliefert werden. Aber Microservices sind als Architekturansatz entstanden, um reale Probleme zu lösen:

  • Jeder Microservices lässt sich unabhängig deployen. Das Deployment eines einzelnen, kleinen Microservice ist einfacher und schneller als das Deployment eines vollständigen Deployment-Monolithen. Auch das Risiko sinkt, weil die Änderungen nur ein Microservice und damit ein Modul betreffen. Die Änderungen sind daher nicht so groß. Damit gibt es weniger Möglichkeiten für Fehler. Das Zurückrollen von Änderungen und Strategien wie Blue/Green Deployment oder Canary Releasing sind ebenfalls aufgrund der geringen Größe der Microservices einfacher als bei einem Deployment-Monolithen. Wenn Continuous Delivery für einen Deployment-Monolithen umgesetzte werden soll, lohnt es sich immer, zumindest eine teilweise Migration zu Microservices in Betracht zu ziehen, weil das die Komplexität der Continuous Delivery Pipeline erheblich reduzieren kann.
  • Es gibt Technologiefreiheit. Wenn ein System langfristig weiterentwickelt werden soll, dann steht irgendwann eine Migration auf eine aktuellere Technologie an. Bei einem Deployment Monolithen muss das gesamte System mit einem Schritt auf eine neue technologische Basis gestellt werden. Microservices lassen sich hingegen einzeln migrieren.
  • Wenn jeder Microservice von einem Team entwickelt wird, können die Teams unabhängig voneinander Microservices und damit neue Features deployen. Ebenso können sie weitgehend unabhängig voneinander technische Entscheidungen treffen. Diese Unabhängigkeit erlaubt es den Teams, sehr autonom zu arbeiten. Das ist gerade bei komplexen Projekten sehr willkommen, um Koordinationsaufwand zu sparen.

Letztlich stehen Microservices für eine konsequente Entkopplung der Module: Technische Entscheidungen oder Deployment sind auf ein Microservice begrenzt. Ebenso führt der Ausfall eines Prozesses nur zum Absturz eines Microservices. Zwischen Microservices können beispielsweise durch Firewall zusätzliche Sicherheitsbarrieren errichtet werden und sie können unabhängig voneinander skaliert werden. Es entsteht also eine Entkopplung bezüglich Technologien, Deployment, Ausfall und Sicherheit. Auch die softwaretechnische Entkopplung kann sehr weit gehen, wie ein früherer Blog-Post gezeigt hat.

So unterstützen Microservices Entkopplung und damit ein Hauptziel von Modulen.

Natürlich führen Microservices zu einer erhöhten Komplexität beispielsweise bei Tests oder im Betrieb. Diese zusätzliche Komplexität muss gegen die bessere Modularisierung, die stärkere Entkopplung und das unabhängige Deployment aufgewogen werden. Schließlich ist es nur sinnvoll, einen Architekturansatz zu wählen, wenn er für das Szenario die einfachste Lösung darstellt. Ursprünglich sind Microservices erfunden worden, weil sie nicht nur eine einfach Lösung sind, sondern große Teams und schnelles Deployment nur so möglich waren.

Microservices von Anfang an als "zu komplex" auszuschließen ist dabei genauso wenig hilfreich, wie Deployment-Monolithen grundsätzlich zu verdammen. Und wichtig bleibt, nicht nur die Frage nach der Modularisierungstechnologie zu stellen, sondern vor allem die fachliche Aufteilung vernünftig umzusetzen.

Microservices und Deployment-Monolithen sind unterschiedliche Modularisierungsansätze. Sie haben Vor- und Nachteile und passen zu verschiedenen Szenarien. Noch wichtiger als der Modularisierungsansatz ist, dass Struktur der Software gut ist.