Monolith vs. Microservices Part 2: Wer hat die bessere Performance
BettercallPaul
BettercallPaul

// //
Lesedauer: 9 Minuten

Monolith vs. Microservices Part 2

Wer hat die bessere performance?


„Monolith first“ – „Don’t start with a monolith”: Ist das eine rein technische Fragestellung? Die Entwicklung von Business-IT-Anwendungen ist in der Regel eine hohe Investition, die auf eine langjährige Nutzung ausgelegt ist. Deswegen spielen wirtschaftliche Aspekte bei der Auswahl der Architektur ebenso eine große Rolle und müssen je nach Geschäftsmodell und Unternehmensziel bei der Architekturbewertung einbezogen werden. Bei einer Neuentwicklung ist einerseits die Time-to-Market ein wichtiges Kriterium, um einen schnellen Return-of-Invest zu erzielen und einen Vorsprung gegenüber der Konkurrenz aufzubauen. Für einen langfristigen Erfolg muss eine dauerhaft hohe Performance bei der Weiterentwicklung und Wartung der IT-Anwendung erzielt werden. Wie spielt ein Monolith in diesen Disziplinen seine Vorteile aus? Und wie besticht eine Microservice-Architektur im wirtschaftlichen Wettbewerb?

Zusammenfassung

Moni und Michi wollen mit ihrem Start-up zu den Elite-Performern eingruppiert werden. Beide wollen einen Big Ball of Mud vermeiden. Moni strebt zur Erreichung der Unternehmensziele einen Modulithen an und Michi die Microservice-Architektur. Mit beiden Ansätzen kann der Erfolg prinzipiell gelingen. Jedoch hat jeder Ansatz Chancen und Risiken:

Der Modulith besticht durch ein schnelles Time-to-Market. Aber der Performance-Vorteil geht mit zunehmender Größe des Projektes verloren. Der Modulith bietet die Chance, Komponenten herauszulösen und als eigene Services weiterzuentwickeln. Man spricht hier von Sollbruchstellen. Werden diese jedoch bei dem Architekturdesign vergessen, besteht die Gefahr, dass der Modulith zu einem monolithischen Big-Ball-of-Mud verkommt.

Eine Microservices-Architektur verlangt zu Beginn eine aufwendigere Planung und einen (zunächst hohen) Overhead bei der Schnittstellenkommunikation. Hat man diese Anfangshürde aber gemeistert, kann die Microservice-Architektur alle Vorteile bei einem Wachstum des Projektes ausspielen und die Performance bleibt hoch. Wurde jedoch zu Beginn ein falscher Schnitt gewählt, kann das Refactoring auch hier enorme Aufwände verursachen. Die Gefahr, einen verteilten Big Ball of Mud zu bauen, darf nicht unterschätzt werden.

Wie spielt ein Monolith in diesen Disziplinen seine Vorteile aus? Und wie besticht eine Microservice-Architektur im wirtschaftlichen Wettbewerb?

Diese Fragen beschäftigen auch Moni und Michi – unsere beiden Unternehmensgründer:innen und IT-Architekt:innen. Moni ist eine Vertreter:in des Ansatzes „Monolith first“. Michi strebt dagegen von Anfang an eine Microservices-Architektur an.

Moni: „Mir ist eine niedrige Time-to-Market wichtig, weil wir dadurch einen Wettbewerbsvorteil gegenüber unseren Mitbewerbern erlangen. Das schnelle Feedback der Anwender wird unser weiteres Vorgehen und weitere Features bestimmen. Deshalb möchte ich unseren MVP so schnell wie möglich auf die Straße bringen. Wir starten am besten mit einem Monolithen – die sind am einfachsten zu entwickeln und zu betreiben. Refactoren können wir später immer noch.“

Michi: „Eine niedrige Time-to-Market ist mir genauso wichtig wie dir, aber nur durch eine solide Architektur kann diese aufrechterhalten werden. Nach jeder Iteration große Teile der Anwendung wegzuwerfen oder aufwendig zu refactoren bringt aus meiner Sicht langfristig wenig. Deshalb sollten wir schon jetzt überlegen, ob es Sinn macht, einzelne Teile der Anwendung in eigene Services auszulagern.“

Sowohl Moni als auch Michi ist eine niedrige Time-to-Market wichtig. Allerdings haben die beiden einen unterschiedlichen Fokus: Moni möchte möglichst schnell mit einem MVP produktiv gehen, um das Produkt zu etablieren und eine solide finanzielle Basis aufzubauen – allerdings werden dafür Abstriche bei der Architekturarbeit in Kauf genommen. Diese Herangehensweise beruht darauf, dass das erste Produkt, das auf den Markt kommt, schneller viral geht und somit etablierter ist als ein Produkt eines Mitbewerbers, das zwar besser ist, aber einen späteren Go-Live hat. Aus der Sicht von Moni können Verbesserungen an der Architektur in einer späteren Phase erfolgen.

Michi hat diesbezüglich eine andere Ansicht: Schon zu Beginn soll ein großer Wert auf eine solide Architektur gelegt werden, um auch in späteren Phasen eine niedrige Time-to-Market gewährleisten zu können und damit bei den sogenannten „Elite-Performern“ eingeordnet zu werden. Michi stellt die Frage, ob man dauerhaft „Elite-Performer“ bleiben kann, wenn man zuerst mit einem Monolithen beginnt und später zu viel Aufwand in die Beseitigung der technischen Schulden stecken muss.

Um die Diskussion zu verstehen, werden zunächst vier verschiedene Ausprägungen vorgestellt:

  • der monolithische Big Ball of Mud,
  • ein modularer Monolith,
  • Microservices
  • und der verteilte Big Ball of Mud.

Die vier Ausprägungen können nach ihrer Anzahl der Deployment-Einheiten und nach dem Grad der Modularität unterschieden werden.

Ein Diagramm, das die verschiedenen Arten von Bereitstellungen zeigt.

Monolithischer Big Ball of Mud

Das Extrem beim monolithischen Ansatz ist der Big Ball of Mud. Obwohl bekannt ist, dass ein System als Big Ball of Mud nur schwer weiterentwickelt und gewartet werden kann, entstehen immer wieder solche Systeme. Woran liegt das?

Dadurch, dass das komplette System innerhalb eines Prozesses läuft und die gesamte Code-Basis an einer Stelle liegt, ist es beim Monolith leicht, neue Funktionalität einfach irgendwo dazu zu bauen oder auf Methoden und Klassen zuzugreifen, auf die außerhalb eines Moduls gar nicht zugegriffen werden darf. Mittels Code-Review oder automatischer Prüfung (z.B. über ArchUnit) können solche Zugriffe und Querschläger zwar verhindert werden, aber offensichtlich passieren sie trotzdem.

Das hat Auswirkungen auf die Aspekte für die Performance bei der Software-Auslieferung:

  • Deployment-Häufigkeit: Aufgrund der verworrenen und eng gekoppelten Module können Deployments nicht häufig durchgeführt werden, da ein Deployment immer großen Aufwand bedeutet.
  • Zeit von Code-Commit zum Deployment: Bei Änderungen muss darauf geachtet werden, dass keine Seiteneffekte etc. entstehen. Es dauert außerdem lange, neue Features in Bestandscode zu implementieren, da keine klare Struktur vorhanden ist.
  • Zeit bis zur Wiederherstellung der Anwendung nach einem Fehler: Wenn ein Fehler auftritt, so ist bei einem Monolithen immer das gesamte System betroffen. Die Fehlersuche gestaltet sich aufgrund der verworrenen Architektur als schwerfällig. Fehler können nur schwer nachvollzogen und behoben werden.
  • Gefahr von Fehlern bei Änderungen: Durch die enge Kopplung und unklare Zuständigkeiten ist die Gefahr von Fehlern bei Änderungen sehr hoch. Es kann nicht sichergestellt werden, welche Seiteneffekte auftreten können.

Modulith – das beste aus zwei Welten?

Auch ein Monolith kann aus sauber strukturierten Modulen und Komponenten aufgebaut sein. Zur Unterscheidung des Big Ball of Mud wird für diese Architektur der Begriff „Modulith“ eingeführt. Was hat es damit auf sich? Ein Modulith ist ein sauber strukturierter aus Modulen aufgebauter Monolith. Das heißt, Module oder Komponenten laufen zwar im gleichen Prozess und werden als ein Deployment-Artefakt ausgeliefert, aber sind lose gekoppelt.

Mit diesem Ansatz kann gerade zu Projektbeginn und bis zum MVP eine höhere Performance erzielt werden, als wenn die Anwendung von Anfang an in verschiedenen Microservices entwickelt wird. Deshalb möchte Moni auch unbedingt mit einem Monolith starten: Das Produkt soll möglichst schnell auf den Markt gebracht werden, um Feedback zu sammeln und die gebrachten Investitionen zu schützen.

Bezogen auf die vier Aspekte für Performance bei der Software-Auslieferung ergibt sich folgendes Bild:

  • Deployment-Häufigkeit: Es kann prinzipiell mehrmals pro Tag deployed werden. Es hängt am Ende von der Größe des Gesamtprojekts ab, wie lange ein Deployment dauert.
  • Zeit von Code-Commit zum Deployment: Dadurch, dass Module lose gekoppelt sind, können Änderungen zügig lokal durchgeführt werden. Auch hier kommt es auf die Größe des Gesamtprojekts an, wie schnell Änderungen an einem Modul auf Produktion ausgeliefert werden können.
  • Zeit bis zur Wiederherstellung der Anwendung nach einem Fehler: Trotz Verwendung von lose gekoppelten Modulen, läuft das Gesamtprojekt immer noch als ein Prozess. Tritt ein Fehler auf, so ist immer das gesamte System betroffen. Dennoch unterstützt die lose Kopplung der Module eine schnelle Fehlerlokalisierung, sodass Fehler zügig behoben werden können.
  • Gefahr von Fehlern bei Änderungen: Bei loser Kopplung und sauberer Trennung der Module kann über eine CI/CD-Pipeline und automatisierte Tests sichergestellt werden, dass einzelne Module und das Zusammenspiel von Modulen weiterhin funktionieren.

Microservices

Microservices sind ein Architekturmuster, bei dem komplexe Anwendungssoftware aus kleinen, unabhängigen Prozessen komponiert wird, die untereinander mit sprachunabhängigen Schnittstellen kommunizieren. Die einzelnen Teile sind klein, weitgehend entkoppelt und erledigen eine dedizierte Aufgabe. Der Gedanke hinter Microservices entspricht weitgehend dem der Unix-Philosophie („Do one thing and do it well“).

Die vier Aspekte bezüglich der Performance bei Software-Auslieferungen stellen sich bei einer Microservice-Architektur so dar:

  • Deployment-Häufigkeit: Jede der kleinen Einheiten kann mehrmals pro Tag deployed werden. Einzelne Teile der Anwendung können leicht ausgetauscht werden.
  • Zeit von Code-Commit zum Deployment: Durch lose Kopplung zwischen Microservices und hoher Kohäsion innerhalb eines Microservices mit überschaubarer Größe können neue Features zügig implementiert und ausgerollt werden. Dadurch, dass nur ein kleiner Teil der Anwendung ausgetauscht werden muss, erfolgt die Auslieferung in unter einem Tag.
  • Zeit bis zur Wiederherstellung der Anwendung nach einem Fehler: Durch die Unabhängigkeit der einzelnen Microservices und Schnittstellen, die als Sollbruchstellen betrachtet werden, kann bei dem Ausfall eines Microservices die Anwendung trotzdem weiterverwendet werden. Fehler können auf einen einzelnen Service eingeschränkt werden, sodass eine schnelle Fehlersuche und -behebung möglich ist.
  • Gefahr von Fehlern bei Änderungen: Dadurch, dass Änderungen nur einen Microservice betreffen, muss nur dieser auf mögliche Seiteneffekte geprüft werden. Über automatisierte Tests kann die Funktionalität innerhalb des Services sowie die Funktionalität der Schnittstellen sichergestellt werden.

Verteilter Big Ball of Mud

Das Beste kommt zum Schluss? In diesem Fall leider nicht. Ein verteilter Big Ball of Mud hat die gleichen Eigenschaften wie ein monolithischer Big Ball of Mud – mit den zusätzlichen Herausforderungen eines verteilten Systems:

  • Deployment-Häufigkeit: Durch die starke Abhängigkeit zwischen den Microservices kann die Anwendung nur selten deployed werden, weil ein Deployment einen sehr großen Aufwand bedeutet.
  • Zeit von Code-Commit zum Deployment: Es dauert deutlich länger, weil neue Features teilweise über Service-Grenzen hinweg ergänzt werden müssen. Diese Änderungen zu testen und auszuliefern ist sehr aufwendig.
  • Zeit bis zur Wiederherstellung der Anwendung nach einem Fehler: Durch die enge Kopplung steht bei einem Fehler das Gesamtsystem still. Die Suche nach Fehlern gestaltet sich schwierig, da Informationen aus unterschiedlichen Services zusammengesucht werden müssen. Außerdem können Netzwerkfehler auftreten. Gegebenenfalls entstehen im Fehlerfall inkonsistente Stände bei Daten in der verteilten Datenhaltung, die aufwendig zurückgerollt werden müssen. Es kann also sehr lange dauern, bis die Anwendung wieder läuft.
  • Gefahr von Fehlern bei Änderung: Es kann nicht sichergestellt werden, dass keine Seiteneffekte auftreten. Die enge Kopplung zwischen den einzelnen Microservices erhöht die Gefahr, dass sich Fehler einschleichen.

Dieser Architekturansatz bedeutet sowohl für Entwickler:innen als auch den Betrieb die Hölle und sollte tunlichst vermieden werden. In der Software-Performance-Matrix muss der Ansatz bei den Low-Performern eingruppiert werden.

Man kann nun die Performance der Architekturansätze über mehrere Release-Zyklen betrachten.

Der State of DevOps-Report – eine Studie, die seit 2013 durchgeführt wird – gruppiert die Performance von Architekturen in „Low-Performer“, „Medium-Performer“, „High-Performer“ und „Elite-Performer“.

Eine Microservice-Architektur erfüllt alle Bedingungen, die an Elite-Performer geknüpft sind, auch wenn die Performance zu Beginn geringer ist als bei monolithischen Ansätzen. Dies liegt vor allem an der aufwendigeren Schnittstellen-Kommunikation (Aufrufe über das Netzwerk statt Methodenaufruf). Mit einem Modulith werden mindestens die Anforderungen an einen High-Performer erfüllt. Je nach Größe des Gesamtsystems können auch die Anforderungen an einen Elite-Performer erfüllt werden.  Der Big Ball of Mud ist in jedem Fall als Low-Performer einzustufen und muss daher unbedingt vermieden werden.