1-6). Vielleicht haben Sie mehrere Instanzen dieses Prozesses, damit das System robuster ist oder besser skaliert, aber im Prinzip befindet sich der gesamte Code in einem einzelnen Prozess. In der Realität kann es sich bei diesen Ein-Prozess-Systemen um einfache verteilte Systeme handeln, da sie so gut wie immer Daten aus einer Datenbank lesen oder in diese schreiben.
Abbildung 1-6: Ein Ein-Prozess-Monolith: Der gesamte Code befindet sich in einem einzelnen Prozess.
Diese Ein-Prozess-Monolithen repräsentieren vermutlich die große Mehrheit der monolithischen Systeme, mit denen die Menschen meiner Beobachtung nach hadern, daher werden wir uns vor allem darum kümmern. Nutze ich ab jetzt den Begriff »Monolith«, rede ich über diese Art von Monolithen, es sei denn, ich erwähne bewusst einen anderen Typ.
Und der modulare Monolith
Als Untermenge der Ein-Prozess-Monolithen handelt es sich beim modularen Monolithen um eine Abwandlung: Der eine Prozess besteht aus separaten Modulen, an denen jeweils unabhängig voneinander gearbeitet werden kann. Zum Deployen müssen aber alle wieder kombiniert werden (siehe Abbildung 1-7). Das Konzept, Software in Module aufzuteilen, ist nicht neu – wir werden auf seine Geschichte weiter unten in diesem Kapitel noch zu sprechen kommen.
Der modulare Monolith kann für viele Organisationen eine ausgezeichnete Wahl sein. Sind die Modulgrenzen gut definiert, wird damit ein hoher Grad an Parallelität beim Arbeiten ermöglicht, während gleichzeitig die Herausforderungen der verteilteren Microservices-Architektur durch viel einfachere Deployment-Überlegungen ersetzt werden können. Shopify ist ein gutes Beispiel einer Organisation, die diese Technik als Alternative zur Microservices-Aufteilung eingesetzt hat – und es scheint für sie sehr gut zu funktionieren.4
Abbildung 1-7: Ein modularer Monolith: Der Code innerhalb des Prozesses ist in Module aufgeteilt.
Eine der Herausforderungen eines modularen Monolithen ist, dass die Datenbank meist nicht den Grad der Zerlegung aufweist, den wir auf Codeebene vorfinden, was zu merklichen Problemen führen kann, wenn Sie den Monolithen fit für die Zukunft machen wollen. Ich habe bei manchen Teams gesehen, wie sie versuchen, die Idee des modularen Monolithen weiterzuentwickeln und die Datenbank ebenfalls entlang der Grenzen der Module aufzuteilen (siehe Abbildung 1-8). Eine solche Änderung an einem bestehenden Monolithen kann aber selbst dann herausfordernd sein, wenn Sie den Code in Ruhe lassen – viele der Muster, die wir in Kapitel 4 behandeln, können Ihnen dabei helfen, wenn Sie so etwas selbst ausprobieren wollen.
Abbildung 1-8: Ein modularer Monolith mit einer aufgeteilten Datenbank
Der verteilte Monolith
Ein verteiltes System ist eines, in dem der Ausfall eines Rechners, von dessen Existenz Sie überhaupt nichts wussten, Ihren eigenen Rechner unbenutzbar machen kann.5
– Leslie Lamport
Ein verteilter Monolith ist ein System, das aus mehreren Servern besteht, das aber aus welchen Gründen auch immer gemeinsam deployt werden muss. Ein verteilter Monolith kann die Definition einer serviceorientierten Architektur sehr gut erfüllen, schafft es aber häufig nicht, die Versprechen der SOA zu halten. Meiner Erfahrung nach haben verteilte Monolithen alle Nachteile eines verteilten Systems und dazu noch die Nachteile eines Ein-Prozess-Monolithen, ohne von beiden Ansätzen viele Vorteile mitzunehmen. Meine Erfahrungen mit verteilten Monolithen waren ein wichtiger Grund für mein Interesse an der Microservices-Architektur.
Verteilte Monolithen entstehen typischerweise in einer Umgebung, in der man sich nicht ausreichend mit Konzepten wie Information Hiding oder der Kohäsion von Businessfunktionalität befasst hat. Das führt zu eng gekoppelten Architekturen, in denen sich Änderungen über Servicegrenzen hinweg ausbreiten und scheinbar harmlose Anpassungen, die eigentlich nur lokale Auswirkungen haben sollten, andere Teile des Systems stören.
Black-Box-Systeme von Fremdherstellern
Wir können auch so manche Software von Fremdherstellern als Monolithen ansehen, die wir bei einer Migration mit »auseinandernehmen« wollen. Dazu gehören zum Beispiel Abrechnungssysteme, CRM-Systeme oder HR-Systeme. Gemeinsam ist all diesen Systemen, dass es sich um Software handelt, die von anderen Leuten entwickelt wurde und bei der Sie den Code nicht ändern können. Es kann sich um fertige Software handeln, die Sie in Ihrer eigenen Infrastruktur deployt haben, oder um ein Software-as-a-Service-(SaaS-)Produkt, das Sie einsetzen. Viele der Aufteilungstechniken, die wir uns in diesem Buch anschauen werden, können auch für Systeme zum Einsatz kommen, bei denen Sie den zugrunde liegenden Code nicht anpassen können.
Herausforderungen von Monolithen
Der Monolith – egal ob als Ein-Prozess-Monolith oder als verteilter Monolith – ist häufig den Gefahren einer Kopplung ausgesetzt, insbesondere einer Implementierungs- oder Deployment-Kopplung (worauf wir noch eingehen werden).
Wenn Sie mehr und mehr Menschen am selben Ort haben, kommen sie sich in die Quere. Verschiedene Entwickler wollen denselben Code verändern, unterschiedliche Teams Funktionalität zu unterschiedlichen Terminen produktiv bringen (oder Deployments verschieben). Es gibt Verwirrung, wer für was zuständig ist und wer Entscheidungen trifft. Viele Studien zeigen die Herausforderungen nicht eindeutiger Ownership-Definitionen.6 Ich nenne dieses Problem den Deployment-Streit.
Mit einem Monolithen müssen Sie nicht zwingend die Probleme eines Deployment-Streits haben – und genauso wenig werden Sie ihnen mit einer Microservices-Architektur niemals begegnen. Aber eine Microservices-Architektur liefert Ihnen konkretere Ownership-Grenzen in einem System, womit Sie mehr Flexibilität zum Reduzieren dieses Problems haben.
Vorteile von Monolithen
Der Ein-Prozess-Monolith hat aber auch diverse Vorteile. Seine deutlich einfachere Deployment-Struktur kann viele der Fallstricke vermeiden, die es bei verteilten Systemen gibt. Die Workflows der Entwickler können viel einfacher sein, Monitoring, Fehlerbehebung und andere Aktivitäten wie End-to-End-Tests lassen sich ebenfalls stark vereinfachen.
Monolithen können ein Wiederverwenden von Code innerhalb des Monolithen selbst erleichtern. Wollen wir Code in einem verteilten System wiederverwenden, müssen wir uns entscheiden, ob wir den Code kopieren, in Bibliotheken auslagern oder seine Funktionalität als Service bereitstellen wollen. Bei einem Monolithen sind unsere Entscheidungen einfacher, und die Menschen mögen diese Einfachheit – der ganze Code ist da, also nutzen wir ihn!
Leider sind viele der Ansicht, dass Monolithen irgendwie zu vermeiden seien – als etwas, das inhärent problematisch ist. Ich habe viele Menschen kennengelernt, für die der Begriff Monolith ein Synonym für Altlast ist. Das ist ein Problem. Eine monolithische Architektur ist eine Wahl, die durchaus valide ist. Sie mag nicht in allen Situationen die richtige Wahl sein, aber das gilt auch für Microservices – eine Wahl ist sie auf jeden Fall. Verrennen wir uns mit einer