Ich sehe es immer wieder: Ein Team beschließt, seinen gewachsenen Monolithen zu zerlegen. Man hat von Microservices gehört, von unabhängigen Deployments, von Teams, die eigenständig liefern können. Nach einem Jahr intensiver Arbeit gibt es jetzt zwölf Services statt einem System. Aber irgendetwas stimmt nicht. Deployments dauern länger als zuvor. Ein Bug in Service C legt Service A lahm. Und wenn ein neues Feature über Teamgrenzen geht, braucht es drei Abstimmungsrunden, bevor auch nur eine Zeile Code geschrieben wird.

Was entstanden ist, hat einen Namen: Distributed Monolith.

Was ist ein Distributed Monolith?

Ein Distributed Monolith ist ein System, das die Komplexität eines Monolithen mit den Nachteilen einer verteilten Architektur kombiniert – ohne die Vorteile beider Welten zu nutzen. Die Dienste sind zwar physisch getrennt, aber konzeptionell eng miteinander verwoben. Sie können nicht unabhängig voneinander deployed, skaliert oder verändert werden.

Das Symptom ist die erzwungene Koordination: Wenn ein Team seinen Service nicht ausliefern kann, ohne dass ein anderes Team gleichzeitig ausliefert, ist das Versprechen von Microservices bereits gebrochen. Es gibt nur noch einen Monolithen – der jetzt zusätzlich über das Netzwerk kommuniziert.

Wie ein Distributed Monolith entsteht

Zerlegung entlang technischer statt fachlicher Grenzen

Der häufigste Fehler: Services werden entlang technischer Schichten oder Datenbankentitäten geschnitten. Es gibt einen UserService, einen OrderService, einen ProductService. Das klingt sauber, bis man merkt, dass eine Bestellbestätigung Daten aus allen drei Services braucht – und jede Anfrage zu einer synchronen Kette wird: A ruft B auf, B ruft C auf, C ruft D auf.

Was fehlt, ist der Schnitt entlang fachlicher Domänen: Wo liegen die echten Grenzen im Geschäftsprozess? Wo kann eine Änderung isoliert stattfinden, ohne Wellen in andere Bereiche zu schlagen? Diese Frage ist schwer. Sie erfordert Domain-Expertise und oft monatelange Auseinandersetzung mit dem tatsächlichen Geschäftsmodell. Teams, die diesen Schritt überspringen, weil er unbequem ist, landen fast zwangsläufig im Distributed Monolith.

Die geteilte Datenbank als versteckte Kopplung

Services, die sich eine Datenbank teilen, sind keine unabhängigen Services. Das ist einer der am häufigsten unterschätzten Fehler. Die physische Trennung der Anwendungslogik täuscht darüber hinweg, dass auf Datenbankebene nach wie vor alles miteinander verknüpft ist. Schemaänderungen erfordern koordinierte Deployments aller Services, die diese Tabellen lesen. Transaktionen, die früher einfach funktionierten, werden zu verteilten Transaktionsproblemen.

Ein Service, der seinen Datenspeicher nicht eigenständig verwaltet, kann seine Schnittstelle nicht eigenständig weiterentwickeln. Das ist keine technische Einschränkung – es ist eine konzeptionelle.

Synchrone Netzwerkkopplung statt loser Methodenaufrufe

Im Monolithen war die enge Kopplung ein Methodenaufruf. Im Distributed Monolith ist sie ein HTTP-Request – mit allem, was dazugehört: Netzwerklatenz, Timeouts, Teilausfälle, Retry-Logik und zirkuläre Abhängigkeiten, die sich im Fehlerfall gegenseitig blockieren. Die Kopplung ist nicht verschwunden. Sie ist teurer geworden.

Ein Monolith, der in Services aufgeteilt wird, ohne dass die Abhängigkeiten aufgelöst werden, ist kein verteiltes System. Es ist ein System, das seine Probleme jetzt über das Netzwerk synchronisiert.

Zu frühe Zerlegung, bevor die Domäne verstanden ist

Microservices sind kein Startpunkt. Sie sind ein Ergebnis – das Ergebnis eines tiefen Verständnisses davon, welche Teile eines Systems wirklich unabhängig voneinander existieren können. Wer ein System zerlegt, bevor dieses Verständnis vorhanden ist, zementiert falsche Grenzen in die Architektur ein. Und falsche Grenzen sind schlimmer als gar keine, weil sie spätere Korrekturen aufwändig machen.

Woran erkenne ich einen Distributed Monolith?

  • Services können nicht unabhängig deployed werden – es braucht immer eine Abstimmung mit anderen Teams.
  • Ein Ausfall in einem Service zieht regelmäßig andere Services mit sich.
  • Neue Features erfordern Änderungen in mehreren Services gleichzeitig.
  • Services teilen sich eine Datenbank oder ein gemeinsames Datenbankschema.
  • Integrationstests sind aufwändiger als Unit-Tests, weil die Services zu eng miteinander verzahnt sind.
  • Die Latenz ist höher als im Monolithen, ohne dass die Skalierbarkeit besser geworden ist.

Wann ist ein gut strukturierter Monolith die bessere Wahl?

Die Microservices-Bewegung hat eine wichtige Lektion verdrängt: Ein sauber strukturierter Monolith ist einer schlecht geschnittenen Microservice-Architektur in fast jeder Hinsicht überlegen. Schnellere lokale Entwicklung, einfacheres Debugging, keine verteilten Transaktionsprobleme, geringere Infrastrukturkomplexität.

Ein Monolith ist die richtige Wahl, wenn:

  • Das Team klein ist und keine klaren Teamgrenzen existieren, die sich in Servicegrenzen übersetzen lassen.
  • Die Domäne noch nicht gut genug verstanden ist, um stabile fachliche Grenzen zu ziehen.
  • Unabhängige Deployments kein echtes Geschäftsproblem lösen – weil das Team ohnehin gemeinsam ausliefert.
  • Die Skalierungsanforderungen keine Aufteilung erfordern, weil das Gesamtsystem problemlos auf einer handvoll Server läuft.

Der Zwischenschritt, den ich in vielen Projekten empfehle, ist der modulare Monolith: Ein System, das intern in klar abgegrenzte Module mit definierten Schnittstellen unterteilt ist – aber als ein Prozess deployed wird. Die Modulstruktur erlaubt es, später gezielt einzelne Bereiche herauszulösen, wenn die Domänengrenzen verstanden sind und ein echter Bedarf nach Unabhängigkeit entsteht.

Mono-Repo: Struktur, die Disziplin erzwingt

Ein Mono-Repo bedeutet nicht, dass alles ein Monolith ist. Es bedeutet, dass der gesamte Code in einem gemeinsamen Repository liegt – ob das nun ein einzelner Service, ein modularer Monolith oder mehrere unabhängige Dienste sind.

In der Praxis hat das Mono-Repo einen unterschätzten Vorteil: Es macht Abhängigkeiten sichtbar. Wenn Service A Code aus Modul B importiert, ist das im Mono-Repo sofort erkennbar – und kann durch Tooling aktiv verhindert werden, wenn die Grenze nicht überschritten werden soll. In Poly-Repos ist dieselbe Abhängigkeit ein versioniertes Package, das sich leise akkumuliert, bis niemand mehr genau weiß, wer was warum einbindet.

Teams wie Google, Meta oder Shopify betreiben Mono-Repos mit tausenden von Entwicklern – nicht weil sie keine unabhängigen Services haben, sondern weil sie Abhängigkeiten kontrollieren wollen. Das Mono-Repo ist keine Architekturentscheidung. Es ist ein Werkzeug für Sichtbarkeit und strukturelle Disziplin.

Für Legacy-Modernisierungen ist das besonders relevant: Wer einen Monolithen in Services zerlegt und dabei auf Poly-Repos wechselt, verliert oft genau die Übersicht, die er für den nächsten Refactoring-Schritt braucht. Das Mono-Repo hält die Gesamtperspektive aufrecht, auch wenn einzelne Teile bereits unabhängig geworden sind.

Fazit

Der Distributed Monolith ist kein Missgeschick. Er ist das fast unvermeidliche Ergebnis, wenn Teams Microservices als Selbstzweck betrachten und die eigentliche Arbeit – das Verstehen von Domänengrenzen – überspringen. Die Architektur ist nicht das Problem. Die fehlende Domänenanalyse ist es.

Wer heute einen Monolithen modernisiert, sollte die ehrliche Frage stellen: Was löse ich damit eigentlich? Wenn die Antwort "wir wollen Microservices haben" ist, ist das kein ausreichender Grund. Wenn die Antwort "Teams A und B müssen unabhängig voneinander liefern können, weil sie unterschiedliche Zyklen haben" lautet, dann ist das eine echte Anforderung – und eine, die es wert ist, gut umgesetzt zu werden.

Bis dahin gilt: Ein sauber strukturierter Monolith ist besser als ein schlecht geschnittenes verteiltes System. Und ein modularer Monolith mit klaren Grenzen ist der beste Ausgangspunkt für alles, was danach kommt.