Architektur entscheiden

Monolith oder Microservices: Wann sich die Zerlegung wirklich lohnt

Microservices lösen ein Organisationsproblem, kein technisches. Wenn sich mehrere Teams am selben Deployment blockieren, hilft die Zerlegung sofort und spürbar. Wenn ein Team ein Produkt baut, kaufst du dir Netzlatenz, doppelten Betriebsaufwand und eine Fehlerklasse ein, die es vorher nicht gab. Der modulare Monolith ist deshalb in den meisten Häusern die bessere erste Antwort, und er verbaut dir den späteren Schnitt nicht.

5 Kapitel mit allen Befehlen
Zwei IT-Entscheider vergleichen Plattformoptionen an einem großen Bildschirm
Seit 1997 am Markt Präsenz & Live-Online 4,9 aus 503 Google-Bewertungen Auch Inhouse für dein Team
Worum es geht

Warum so viele Zerlegungen im verteilten Monolithen enden

Nach zehn Jahren Zerlegungswelle ziehen auffällig viele Teams Dienste wieder zusammen. Das Muster dahinter ist immer dasselbe: Aus einer Anwendung wurden zwanzig Dienste, aber die Grenzen verliefen entlang technischer Schichten statt entlang fachlicher Zuständigkeiten. Am Ende muss für jede neue Funktion trotzdem in vier Repositories geändert werden, und die vier Dienste müssen in einer bestimmten Reihenfolge ausgerollt werden. Das ist ein verteilter Monolith. Er hat alle Nachteile der Zerlegung und keinen ihrer Vorteile, weil die Teams weiterhin gekoppelt sind, nur eben über das Netz statt über den Compiler.

Technisch verschiebt die Zerlegung Komplexität, sie beseitigt sie nicht. Ein Methodenaufruf, der vorher nicht scheitern konnte, wird zu einem Netzaufruf mit Zeitüberschreitung, Wiederholungslogik und der Frage, was passiert, wenn die Antwort zweimal ankommt. Eine Transaktion über zwei Tabellen wird zu einem Ablauf über zwei Dienste, den du fachlich kompensieren musst. Ein Fehlerbild, das vorher in einem Stacktrace stand, verteilt sich auf fünf Protokolldateien. All das ist beherrschbar, aber es kostet Aufwand, und dieser Aufwand fällt jeden Tag an, nicht einmalig im Projekt.

Der dritte Punkt entscheidet in der Praxis am häufigsten: die Voraussetzungen fehlen. Wer Dienste einzeln ausrollen will, braucht eine Pipeline, die das ohne Handarbeit kann. Wer Fehler über Dienstgrenzen hinweg finden will, braucht durchgehende Traces und keine gesammelten Logdateien. Wer zwanzig Dienste betreiben will, braucht eine Plattform, auf der ein Team sie ohne Ticket ausrollen kann. Fehlt eines davon, wird aus der Zerlegung ein Projekt, das den Betrieb überfordert, und die Schuld bekommt anschließend die Architektur statt der fehlenden Grundlage.

Miniatur-Szene: zwei Plattformen nebeneinander mit unterschiedlichen Cloud-Bausteinen, dazwischen eine Waage

Sechs Prüfsteine vor dem ersten Schnitt

  1. 01 Blockieren sich mehrere Teams am selben Deployment?
  2. 02 Lässt sich eine typische Änderung in einem Modul erledigen?
  3. 03 Rollt die Pipeline jeden Dienst einzeln bis in die Produktion?
  4. 04 Gibt es durchgehende Traces über Dienstgrenzen hinweg?
  5. 05 Besitzt jeder künftige Dienst seine Daten allein?
  6. 06 Existiert ein Rückweg, wenn der neue Dienst wackelt?
Was du mitnimmst

Vier Wege, und wann welcher passt

Zwischen einem gewachsenen Monolithen und zwanzig Diensten liegen mehrere Stufen. Die Reihenfolge ist wichtiger als das Ziel.

Modularer Monolith

Eine Anwendung, ein Deployment, aber innen klare Module mit erzwungenen Grenzen: eigene Pakete, eigene Tabellen, Kommunikation nur über definierte Schnittstellen oder Ereignisse. Du bekommst die fachliche Klarheit der Zerlegung, ohne Netzwerk und Betriebsaufwand zu bezahlen. Für die meisten Häuser mit ein bis drei Teams ist das die richtige Antwort, und sie ist keine Zwischenlösung, sondern ein Zielbild.

Zerlegung entlang der Fachlichkeit

Wenn zerlegt wird, dann entlang von Domänengrenzen, nicht entlang von Schichten. Ein Dienst besitzt seine Daten vollständig, hat eine fachliche Verantwortung und lässt sich unabhängig ausrollen. Faustregel: Wenn eine typische Änderung mehr als einen Dienst berührt, war der Schnitt falsch. Diese Regel ist unbequem, aber sie ist der einzige verlässliche Test für die Qualität einer Zerlegung.

Strangler-Muster statt Neubau

Die Zerlegung passiert im laufenden Betrieb, Funktion für Funktion. Vor die alte Anwendung kommt ein Router, der einzelne Pfade auf den neuen Dienst umleitet, zunächst mit wenigen Prozent des Verkehrs. Der große Neubau auf der grünen Wiese hat eine schlechte Erfolgsquote, weil er zwei Jahre lang keinen Wert liefert und die alte Anwendung sich in dieser Zeit weiterentwickelt.

Laufzeit als Kostenhebel

Viele kleine Dienste bedeuten viele Laufzeiten, und die kosten Speicher und Startzeit. Genau hier lohnen sich Go mit sehr kleinem Fußabdruck oder Java als nativ übersetztes Programm über GraalVM, wo Startzeiten von Sekunden auf Millisekunden fallen. Das ist kein Detail: Bei zwanzig Diensten entscheidet der Speicherbedarf je Instanz direkt über die Größe deiner Knoten.

Die Voraussetzungen zuerst

Vor dem ersten herausgezogenen Dienst brauchst du drei Dinge: eine Pipeline, die jeden Dienst einzeln bis in die Produktion bringt, durchgehende Traces über Dienstgrenzen hinweg und eine Plattform, auf der ein Team ohne Ticket ausrollen kann. Wer diese drei nicht hat, sollte sie zuerst bauen. Die Zerlegung ist danach eine ruhige Übung statt eines Kraftakts.

Tutorial

Vom gewachsenen Monolithen zum ersten eigenständigen Dienst

Microservices lösen ein Organisationsproblem und kein technisches, deshalb ist der modulare Monolith in den meisten Häusern die bessere erste Antwort. Vor dem ersten herausgezogenen Dienst müssen ohnehin eine Pipeline je Dienst, durchgehende Traces und eine Plattform ohne Ticketweg stehen. Genau in dieser Reihenfolge geht es weiter: messen, wo es wirklich klemmt, die Modulgrenzen im Monolithen erzwingen, den ersten Schnitt festlegen und den Dienst im laufenden Betrieb herausziehen.

01

Schritt 1: Messen, wo es wirklich klemmt

Bevor du Architektur änderst, brauchst du Belege. Zwei Messungen reichen: Wo im Code ändert sich ständig etwas, und wie lange braucht eine Änderung bis in die Produktion.

Änderungshäufigkeit und Kopplung aus der Versionsgeschichte lesen
# Welche Dateien aendern sich am haeufigsten?
git log --since='12 months ago' --name-only --pretty=format: \
  | grep -E '\.(java|go|ts)$' \
  | sort | uniq -c | sort -rn | head -20

# Welche Verzeichnisse aendern sich immer gemeinsam?
# Ausgabe: pro Commit die beteiligten Modulordner, danach Paare zaehlen
git log --since='12 months ago' --pretty=format:'---%h' --name-only \
  | awk '/^---/{print ""; next} {split($0,a,"/"); if (a[1]=="modules") printf "%s ", a[2]}' \
  | sort | uniq -c | sort -rn | head -20

Die zweite Abfrage ist die wichtigere. Modulordner, die in fast jedem Commit gemeinsam auftauchen, sind fachlich ein Ding und dürfen nicht getrennt werden. Wer sie trotzdem trennt, baut die erste Hälfte eines verteilten Monolithen.

02

Schritt 2: Die Grenzen erst im Monolithen erzwingen

Modulgrenzen, die nur in einer Zeichnung existieren, halten nicht. Erzwinge sie im Build, dann merkst du am Testlauf, wenn jemand sie verletzt, und nicht erst in der Produktion.

src/test/java/de/example/shop/ModulTest.java
import org.springframework.modulith.core.ApplicationModules;
import org.junit.jupiter.api.Test;

class ModulTest {

    static final ApplicationModules MODULE = ApplicationModules.of(ShopApplication.class);

    @Test
    void modulgrenzenSindEingehalten() {
        // Schlaegt fehl, sobald ein Modul auf interne Klassen eines anderen zugreift
        MODULE.verify();
    }

    @Test
    void dokumentationErzeugen() {
        new org.springframework.modulith.docs.Documenter(MODULE)
            .writeModulesAsPlantUml()
            .writeIndividualModulesAsPlantUml();
    }
}

Spring Modulith leitet Module aus den Paketen unterhalb der Hauptklasse ab. Alles, was nicht direkt im Modulwurzelpaket liegt, gilt als intern und ist für andere Module tabu. Für Projekte ohne Spring leistet ArchUnit dasselbe mit selbst geschriebenen Regeln.

Module einzeln bauen und testen
# Nur ein Modul samt seiner Abhaengigkeiten bauen
./mvnw -pl modules/billing -am test

# Laufzeit-Abhaengigkeiten pruefen: wer haengt an wem?
./mvnw dependency:tree -Dincludes=de.example.shop

# Gradle-Aequivalent
./gradlew :modules:billing:test

Wenn ein Modul sich nicht allein bauen und testen lässt, lässt es sich auch nicht allein ausrollen. Diese Übung ist damit der billigste Vorabtest für jede geplante Zerlegung.

03

Schritt 3: Den ersten Schnitt auswählen

Der erste herausgezogene Dienst ist eine Lernübung, kein Wertversprechen. Wähle ihn danach aus, wie viel du dabei lernst und wie wenig kaputtgehen kann.

Kandidaten für den ersten Dienst

KandidatWarum er sich eignetGegenanzeige
Versand von BenachrichtigungenKlare Grenze, eigene Daten, asynchron, Ausfall ist selten kritischWenn Zustellgarantien vertraglich zugesichert sind
Dateiablage und DokumentenerzeugungRessourcenhungrig, skaliert anders als der RestWenn Dokumente in der Kerntransaktion entstehen müssen
Suche und KatalogauslieferungLesend, gut zwischenspeicherbar, eigene SkalierungWenn der Index nur aus Kerntabellen live gefüllt wird
Abrechnung und FakturierungFachlich sauber abgrenzbar, eigenes Team möglichHohe Anforderungen an Nachvollziehbarkeit und Konsistenz
BenutzerverwaltungWirkt naheliegendMeist die schlechteste Wahl, weil fast jede Anfrage sie berührt
Zugriffsschicht auf die DatenbankWirkt naheliegendTechnischer Schnitt, führt direkt in den verteilten Monolithen

Zwei Fragen entscheiden über die Auswahl. Erstens: Besitzt der Kandidat seine Daten allein, oder muss er für jede Anfrage in fremden Tabellen lesen? Zweitens: Was passiert, wenn er zehn Minuten ausfällt? Ein Dienst, der synchron in der Bestellstrecke hängt, ist der falsche Anfang. Wenn du am Ende feststellst, dass alle Kandidaten synchron gebraucht werden und keiner eigene Daten hat, ist das ein belastbares Ergebnis: Der modulare Monolith bleibt, und die Diskussion ist vorerst beendet.

04

Schritt 4: Im laufenden Betrieb herausziehen

Der Umzug passiert über den Router vor der Anwendung, nicht über einen Stichtag. Der neue Dienst bekommt zuerst wenige Prozent des Verkehrs, und der Weg zurück ist eine einzige Änderung.

k8s/ingress-billing-canary.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop-billing-canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "5"
spec:
  ingressClassName: nginx
  rules:
    - host: shop.example.de
      http:
        paths:
          - path: /api/billing
            pathType: Prefix
            backend:
              service:
                name: billing-service
                port:
                  number: 8080

Der Anteil wird schrittweise erhöht: fünf, zwanzig, fünfzig, hundert Prozent, mit mindestens einem vollen Werktag dazwischen. Baust du neu, sieh dir statt der Ingress-Annotationen die Gateway API an, denn ingress-nginx wird eingestellt. Was das bedeutet, steht unter ingress-nginx ablösen.

Anteil steuern und in Sekunden zurücknehmen
# Anteil erhoehen
kubectl annotate ingress shop-billing-canary \
  nginx.ingress.kubernetes.io/canary-weight=20 --overwrite

# Rueckweg: gesamter Verkehr wieder auf den Monolithen
kubectl annotate ingress shop-billing-canary \
  nginx.ingress.kubernetes.io/canary-weight=0 --overwrite

# Fehlerrate des neuen Dienstes vor jeder Erhoehung pruefen
kubectl logs -l app=billing-service --since=1h --tail=-1 \
  | grep -c ' 5[0-9][0-9] '

Der Rückweg funktioniert nur, solange der alte Pfad im Monolithen noch existiert und noch funktioniert. Lösche ihn erst, wenn der neue Dienst mehrere Wochen den vollen Verkehr getragen hat, und nicht am selben Tag.

05

Schritt 5: Betrieb vorbereiten, bevor der zweite Dienst kommt

Mit zwei Diensten kommst du noch ohne Werkzeug aus, mit acht nicht mehr. Alles, was ab hier fehlt, kostet später ein Vielfaches.

Durchgehende Traces mit OpenTelemetry aktivieren
# Java: Agent anhaengen, keine Codeaenderung noetig
export OTEL_SERVICE_NAME=billing-service
export OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector.observability:4317
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1
export OTEL_RESOURCE_ATTRIBUTES=deployment.environment=prod,service.version=2.4.1

java -javaagent:/opt/otel/opentelemetry-javaagent.jar -jar billing.jar

# Go: Endpunkt und Dienstname werden vom SDK aus denselben Variablen gelesen
OTEL_SERVICE_NAME=catalog-service \
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector.observability:4317 \
  ./catalog-service

Wichtig ist parentbased_traceidratio: Damit entscheidet der erste Dienst über die Stichprobe und alle folgenden übernehmen diese Entscheidung. Sonst bekommst du lauter halbe Traces, die genau an der Stelle abreißen, an der du hinsehen willst. Das Thema im Ganzen behandeln wir unter Observability mit OpenTelemetry.

Was vor dem dritten Dienst stehen muss

  • Jeder Dienst rollt einzeln bis in die Produktion, ohne Handarbeit.Wenn ein Ausrollen koordiniert werden muss, hast du die Kopplung nur vom Compiler in den Kalender verschoben und nichts gewonnen.
  • Durchgehende Traces mit einheitlichen Dienstnamen und Umgebungen.Ohne Trace über Dienstgrenzen hinweg dauert die Fehlersuche in einer verteilten Anwendung ein Vielfaches, weil niemand die Reihenfolge der Aufrufe kennt.
  • Schnittstellenverträge versioniert und automatisch geprüft.Ein Dienst, der seine Antwort ohne Vorwarnung ändert, legt alle Aufrufer lahm. Der Vertrag im Repository fängt das im Testlauf ab statt in der Produktion.
  • Zeitüberschreitungen, Wiederholungen und Sicherungsschalter überall gesetzt.Ohne Zeitüberschreitung wartet ein Aufrufer unbegrenzt und reißt die eigenen Threads mit. So wird aus dem Ausfall eines Randdienstes ein Komplettausfall.
  • Jeder Dienst besitzt seine Daten allein, keine geteilten Tabellen.Geteilte Tabellen koppeln Dienste über das Schema. Dann kann keiner mehr unabhängig ausgerollt werden, und die Zerlegung war umsonst.
  • Ein Team ist für Bau und Betrieb desselben Dienstes zuständig.Wenn ein anderes Team betreibt, was ein Team gebaut hat, wandern Betriebsprobleme in eine Warteschlange statt in die nächste Version.

Erst wenn diese sechs Punkte stehen, ist der Weg zu weiteren Diensten ruhig. Das ist auch der Grund, warum die Plattform meistens vor der Architektur kommt: Ein Team, das ohne Ticket ausrollen, messen und zurückrollen kann, zerlegt in einem Quartal, wofür andere zwei Jahre brauchen. Wie ein solcher Unterbau entsteht, beschreiben wir unter Platform Engineering einführen. Ob dafür überhaupt Kubernetes nötig ist, ist eine eigene Frage, und die Antwort lautet bei zwei Diensten oft nein.

Gut zu wissen

Häufige Fragen zu Cloud-Native-Architektur und Microservices

Noch etwas offen? Wir sind ohne Warteschleife für dich da.

Frag uns direkt
Ab wie vielen Entwicklern lohnen sich Microservices?
Die Zahl allein entscheidet nicht, die Teamstruktur schon. Solange ein Team an einer Anwendung arbeitet, gewinnst du durch die Zerlegung nichts, was ein modularer Monolith nicht auch könnte. Interessant wird es ab drei bis vier eigenständigen Teams, die sich gegenseitig am Ausrollen hindern, unterschiedliche Freigabezyklen haben oder ihre Dienste sehr unterschiedlich skalieren müssen. Der Auslöser ist also organisatorisch. Wer bei zwei Teams zerlegt, zahlt den Betriebsaufwand vollständig und bekommt den Hauptnutzen nicht.
Was genau ist ein modularer Monolith?
Eine Anwendung, die als ein Programm ausgeliefert wird, innen aber in fachliche Module getrennt ist, deren Grenzen der Build erzwingt. Jedes Modul hat eigene Pakete, eigene Tabellen und eine schmale öffentliche Schnittstelle, alles andere ist intern und für andere Module unerreichbar. Werkzeuge wie Spring Modulith oder ArchUnit prüfen das im Testlauf. Der Vorteil: Du bekommst die fachliche Klarheit, die eine Zerlegung bringen soll, ohne Netzaufrufe, verteilte Transaktionen und den doppelten Betriebsaufwand zu bezahlen.
Woran erkenne ich einen verteilten Monolithen?
An drei Symptomen. Erstens muss für eine typische fachliche Änderung in mehreren Diensten gleichzeitig gearbeitet werden. Zweitens müssen diese Dienste in einer festgelegten Reihenfolge ausgerollt werden, sonst bricht etwas. Drittens greifen mehrere Dienste auf dieselbe Datenbank oder dieselben Tabellen zu. Trifft eines davon zu, sind die Dienste nicht unabhängig, und du trägst den Betriebsaufwand der Zerlegung, ohne ihren Nutzen zu haben. Die Antwort ist dann meistens Zusammenlegen, nicht weiter zerlegen.
Können wir Dienste wieder zusammenlegen, wenn die Zerlegung zu weit ging?
Ja, und das ist häufiger die richtige Entscheidung, als es öffentlich zugegeben wird. Zusammenlegen ist technisch meist einfacher als Trennen, weil du Netzaufrufe durch Methodenaufrufe ersetzt und dabei Fehlerbehandlung wegfällt statt hinzuzukommen. Der Aufwand liegt in den Daten, wenn beide Dienste eigene Datenbanken haben. Vorgehen wie beim Herausziehen: schrittweise über den Router, mit Rückweg, und die Datenzusammenführung als eigener, geplanter Schritt.
Brauchen wir für Microservices zwingend Kubernetes?
Nein. Bis etwa fünf bis acht Dienste reichen virtuelle Maschinen mit einer ordentlichen Auslieferungskette, ein Container-Dienst wie AWS ECS oder Azure Container Apps oder auch Funktionen, wenn die Last stark schwankt. Kubernetes lohnt sich, wenn viele Dienste, mehrere Teams und ein echter Bedarf an einheitlicher Plattform zusammenkommen. Wer mit drei Diensten startet und gleichzeitig einen Cluster aufbaut, hat zwei Lernkurven parallel und wird von beiden ausgebremst.

Zuletzt geprüft am 26. Juli 2026.

Das sagen Teilnehmer

Echte Stimmen aus unseren IT-Kursen

Qualitativ sehr guter Kurs. Ruhiger und wertschätzender Umgang. Keine Informationsüberlastung.
Rückmeldung aus dem Kurs „Ansible Kompaktkurs“
Der Kurs hat sein Ziel erreicht. Der Trainer konnte die Inhalte gut rüberbringen und der Praxisanteil und die Übungsumgebung war sehr gut.
Rückmeldung aus dem Kurs „Terraform mit Azure – Der kompakte Praxiskurs für Ihren Cloud-Erfolg“
Persönlich für dich da

Deine Ansprechpartner

Du bist dir nicht sicher, welcher Kurs oder welches Level zu dir passt? Wir beraten dich persönlich und kostenlos.

Yves Hoppe

Yves Hoppe

Weiterbildung & Beratung

Hilft dir, aus dem Cloud-Programm den passenden Kurs oder Lernpfad zu finden.

Norbert Jansen

Norbert Jansen

Beratung & Inhouse

Plant mit dir Inhouse-Trainings, die exakt auf eure Systemlandschaft und Distributionen zugeschnitten sind.

Nächster Schritt

Den Schnitt gemeinsam mit dem Team lernen

Architekturentscheidungen halten nur, wenn das Team sie tragen kann. Der Kurs Vom Monolith zum Cloud Native System arbeitet genau an dieser Frage, Legacy Java EE zu Cloud Native Spring Boot deckt den Weg aus alten Anwendungsservern ab, Microservices mit Spring und Cloud-Native Go zeigen die Umsetzung, Cloud-Native Java mit GraalVM den Fußabdruck im Betrieb. Sprich uns an, wenn du unsicher bist, welcher Einstieg zu eurer Ausgangslage passt.