Microservices oder modularer Monolith? Ein praktischer Rahmen für die richtige Grenze
Wenn ein Produkt wächst, beginnt die Architekturdiskussion oft mit derselben Frage: „Sollten wir das in Microservices aufteilen?“ Die Frage ist berechtigt, aber unvollständig. Ein Microservice ist keine Technik zur Code-Organisation mit mehr Ordnern. Er ist ein neues Betriebsmodell für Deployment, Datenverantwortung, Observability und Zusammenarbeit im Team.
Der hilfreiche Ausgangspunkt lautet daher nicht: „Wie viele Services brauchen wir?“ Die bessere Frage ist: Welche Geschäftsfähigkeit ist wirklich bereit, sich unabhängig vom Rest zu verändern und zu laufen? Ist die Antwort nicht klar, ist ein modularer Monolith häufig der schnellere, verständlichere und besser rückgängig zu machende erste Schritt.
Was ein modularer Monolith löst
Ein modularer Monolith besitzt klar abgegrenzte Domänenmodule innerhalb einer einzigen auslieferbaren Anwendung. Identität, Suche, Bestellungen, Benachrichtigungen oder Dokumentenverwaltung können jeweils eigene Anwendungsregeln und, soweit sinnvoll, eigene Datenzugriffe haben. Aufrufe zwischen Modulen erfolgen über definierte Schnittstellen und nicht über beliebigen Zugriff auf Tabellen eines anderen Moduls.
„Monolith“ bedeutet dabei nicht ungeordneter Code. Ziel ist es, den Komfort eines einzelnen Deployments zu behalten und gleichzeitig mögliche spätere Extraktionsgrenzen sichtbar zu machen. Zwei Module im selben Prozess haben keine Netzwerklatenz, keine Probleme mit Versionskompatibilität und keine Kosten verteilter Transaktionen. Der Nachteil: Werden Grenzverletzungen nicht früh bemerkt, kann Modularität mit der Zeit zu einer bloßen Ordnerstruktur werden.
Ein modularer Monolith vermeidet die Architekturentscheidung also nicht. Er schafft Raum, sie mit besseren Belegen zu treffen.
Fünf Fragen für eine Service-Grenze
Bevor ein Modul zu einem eigenständigen Service wird, sollten die folgenden Fragen mit konkreten Beispielen beantwortet werden.
1. Gibt es einen klaren Besitzer der Geschäftsregel?
Ein Service sollte eine sinnvolle Geschäftsfähigkeit besitzen, keine technische Schicht. Ein Benachrichtigungsmodul ist zum Beispiel mehr als Code zum Versenden von E-Mails. Es kann Zustellpräferenzen, Wiederholungsregeln, Vorlagen und Versandhistorie verwalten. „Gemeinsame Hilfsfunktionen“ oder „Datenbankzugriff“ sind dagegen selten gute Service-Grenzen.
Die Benennung ist ein guter Test: Lässt sich die Grenze als Verb oder als Geschäftsergebnis beschreiben? Wenn nicht, ist das herausgelöste Element wahrscheinlich noch keine Domäne.
2. Kann Datenverantwortung ohne Teilen entstehen?
Bei Microservices sind Daten meist schwieriger als Netzwerke. Jeder Service sollte seine Daten besitzen; direkt in Tabellen eines anderen Services zu schreiben, ist der kürzeste Weg zu einem verteilten Monolithen. Wenn mehrere Domänen denselben Datensatz in einer Operation verändern müssen, sollte zuerst geklärt werden, ob diese Operation wirklich in einem einzigen Schritt fertig werden muss.
Manche Abläufe tolerieren eventuelle Konsistenz. Nach dem Anlegen eines Datensatzes kann eine Aktualisierung des Suchindex oder eine Benachrichtigung einige Sekunden später erfolgen, ohne die Nutzererfahrung zu beschädigen. Bei Identität, Berechtigung oder monetärem Zustand kann die Erwartung an Konsistenz deutlich höher sein. Die Fehlertoleranz des Geschäfts sollte entscheiden, nicht eine Technologiepräferenz.
3. Ist die Änderungsrate wirklich unterschiedlich?
Wenn zwei Module in derselben Version, vom selben Team und mit derselben Häufigkeit geändert werden, verbessert ihre Trennung die Liefergeschwindigkeit möglicherweise nicht. Ein separater Service rechtfertigt seine Kosten, wenn er einen eigenen Release-Rhythmus, ein eigenes Risikoprofil oder einen unabhängigen Veröffentlichungsbedarf hat.
Besser als Vorhersagen ist der Blick in die Historie. Welche Bereiche änderten sich in den letzten Releases? Welche Änderungen warteten darauf, dass andere Module getestet, gebaut oder bereitgestellt wurden? Wenn ein eigener Veröffentlichungsrhythmus nicht regelmäßig sichtbar ist, ist die Service-Grenze vielleicht noch nicht reif.
4. Sind Skalierung und Fehlerisolation echte Bedürfnisse?
Ein Modul, das deutlich mehr CPU, Speicher oder Netzwerkkapazität als der Rest benötigt, ist ein guter Kandidat für die Extraktion. Doch „es könnte später wachsen“ reicht allein nicht aus. Zuerst sollten Engpass und Kosten gemessen und sichtbar gemacht werden.
Dasselbe gilt für Fehlerisolation. Beeinträchtigt ein verzögerter Reporting-Job die Anmeldung? Funktionieren die Kernabläufe weiter, wenn sich eine Warteschlange zur Dateiverarbeitung staut? Wenn ja, können unabhängige Ressourcenlimits und andere Skalierungsregeln klaren Nutzen bringen.
5. Kann das Team den Service betreiben?
Jeder neue Service bedeutet Pipeline, Konfiguration, Geheimnisverwaltung, Health Checks, Logs, Metriken, Traces, Alarme und Versionskompatibilität. Diese Last hält oft länger an als die erste Implementierung. Für ein kleines Team können drei getrennte Betriebsflächen den Nutzen der fachlichen Trennung leicht überwiegen.
Die Zahl der Services sollte mit echter Teamverantwortung wachsen. Dass eine Grenze technisch möglich ist, bedeutet nicht, dass ihre Trennung heute wirtschaftlich sinnvoll ist.
Auf die Extraktion vorbereiten, ohne zu warten
Die Entscheidung für einen modularen Monolithen verschließt die Tür zu späteren Services nicht. Bewusst umgesetzt, schafft sie einen sicheren Extraktionspfad:
- Befehle und Abfragen, die ein Modul anbietet, hinter expliziten Schnittstellen sammeln.
- Andere Module vom internen Datenzugriff abhalten; das besitzende Modul sollte die benötigten Informationen anbieten.
- Domänenereignisse neben der Geschäftsregel definieren und Muster wie Outbox prüfen, damit Ereignisse zuverlässig veröffentlicht werden.
- Observability, Authentifizierung und Fehlerverträge in der gesamten Anwendung konsistent halten.
- Bei der Extraktion zunächst mit einem Lesemodell oder asynchronen Consumer beginnen; einen kritischen Ablauf nicht in einem Schritt verteilt machen.
Diese Vorbereitungen helfen nicht nur bei einem späteren Wechsel zu Microservices. Auch in einer einzigen Anwendung verringern sie Abhängigkeiten, vereinfachen Tests und erleichtern neuen Entwicklern das Verständnis der Domäne.
Signale, um die Entscheidung erneut zu prüfen
Architektur sollte kein dauerhaftes Etikett sein. Es ist sinnvoll, eine Service-Extraktion erneut zu bewerten, wenn bestimmte Signale regelmäßig auftreten: Unabhängige Änderungen blockieren gegenseitig ihr Release, ein Modul wird zu einem dauerhaften ressourcenhungrigen Nachbarn, eigene Zugriffs- oder Audit-Anforderungen entstehen oder ein Team übernimmt eine bestimmte Fähigkeit Ende zu Ende.
Vor diesen Signalen auf Microservices zu wechseln, bedeutet oft, Probleme verteilter Systeme zu früh zu kaufen. Zu spät zu wechseln bedeutet meist, dass Modulgrenzen von Anfang an nicht sichtbar gemacht wurden.
Der tragfähige Ansatz ist nicht, eine Architektur als Slogan zu wählen. Jede Grenze sollte durch Geschäftswert und Betriebskosten begründet sein. Microservices sind an der richtigen Grenze ein starkes Werkzeug; ein modularer Monolith bietet einen sicheren Arbeitsraum, um diese Grenze zu finden.