Microservices: Die verteilte Monolithfalle
Die Aufteilung Ihrer App in 10 Dienste macht sie nicht schneller. Es erschwert das Debuggen. Ein technischer Leitfaden zu modularen Monolithen, Domain-Driven Design (DDD) und dem Saga-Muster.
Im Jahr 2015 lautete das absolute Mantra der Branche: „Monolithen sind Dinosaurier. Microservices sind die Zukunft.“ Im Jahr 2025 hat der Kater eingesetzt. Unternehmen, die ihre Anwendungen blind aufteilen, ertrinken nun in dem, was wir den verteilten Monolithen nennen: ein System mit der ganzen Komplexität verteilter Datenverarbeitung und ohne die Vorteile der Entkopplung.
Bei Maison Code Paris fungieren wir als architektonisches Gewissen für unsere Kunden. Wir erben oft „Microservices“-Projekte, bei denen eine einfache Benutzeranmeldung sechs verschiedene Dienste berührt, eine Latenz von 2 Sekunden hat und 5.000 €/Monat an Cloud-Ingress-Gebühren kostet.
Dieser Artikel ist ein nüchterner, technischer Leitfaden dazu, wann man Microservices einführen sollte und, was noch wichtiger ist, wann man vor ihnen davonlaufen sollte.
Warum Maison Code darüber spricht
Bei Maison Code Paris fungieren wir als das architektonische Gewissen unserer Kunden. Wir übernehmen oft „moderne“ Stacks, die ohne grundlegendes Verständnis für Skalierung gebaut wurden.
Wir diskutieren dieses Thema, weil es einen kritischen Wendepunkt in der technischen Reife darstellt. Die korrekte Implementierung unterscheidet ein fragiles MVP von einer widerstandsfähigen Plattform auf Unternehmensniveau.
Das Argument für den modularen Monolithen
Bevor Sie „mkdir service-user“ eingeben, denken Sie über den Modularen Monolithen nach. Hierbei handelt es sich um eine einzelne bereitstellbare Einheit (eine Binärdatei/einen Container), bei der der Code streng in Module (Pakete) unterteilt ist, die Geschäftsdomänen widerspiegeln.
Struktur ist wichtig
Bei einem „Spaghetti-Monolith“ importieren sich Dateien zufällig gegenseitig. Ein modularer Monolith verfügt über strenge Grenzen, die durch Linting-Regeln oder Kompilierungsziele durchgesetzt werden.
„Text src/ Module/ auth/ # Begrenzter Kontext: Authentifizierung api/ # Öffentliche Schnittstelle internal/ # Private Implementierung (Datenbank, Logik) Inventar/ # Begrenzter Kontext: Inventar API/ intern/ billing/ # Begrenzter Kontext: Abrechnung API/ intern/ „
Das Demeter-Gesetz: Code in „Billing“ kann „Inventar/intern“ nicht importieren. Es kann nur „inventory/api“ aufgerufen werden. Wenn Sie dieser Validierung folgen, erhalten Sie 90 % der Vorteile von Microservices (Teamtrennung, sauberer Code) bei 0 % der Nachteile (Netzwerklatenz, letztendliche Konsistenz, Komplexität der Bereitstellung).
Wann man teilen sollte (Die Randbedingungen)
Wir empfehlen die Aufteilung eines Moduls in einen separaten Microservice nur dann, wenn es eines dieser strengen Kriterien erfüllt:
-
Heterogene Hardwareanforderungen:
- Die Core-API ist E/A-gebunden. Es benötigt 1 GB RAM und 0,5 CPU.
- Der Bildprozessor ist CPU-gebunden. Es benötigt 16 GB RAM und GPU-Zugriff.
- Entscheidung: Extrahieren Sie den Bildprozessor. Die Bereitstellung von GPU-Knoten für die API ist Geldverschwendung.
-
Unabhängige Freigabegeschwindigkeit:
- Das Checkout-Team verschickt den Code stündlich.
- Das Ledger-Team (Buchhaltung) führt einmal im Monat nach strenger Prüfung einen Push durch.
- Entscheidung: Checkout extrahieren, um eine Blockierung des Hochgeschwindigkeitsteams zu vermeiden.
-
Tödliche Isolation (Der „Explosionsradius“):
- Wenn der PDF-Generator einen Speicherverlust aufweist und abstürzt, sollte er das Payment Gateway nicht herunterfahren.
- Entscheidung: Instabile oder riskante Komponenten isolieren.
Kommunikationsmuster: REST vs. gRPC vs. Events
Sobald Sie über mehrere Dienste verfügen, ist die Kommunikation das größte Problem. „Funktionsaufrufe“ werden zu „Netzwerkpaketen“.
Synchron: Die HTTP-Falle
Dienst A ruft Dienst B über HTTP/REST auf.
GET /users/123.
Dies koppelt die Verfügbarkeit von A an B. Wenn B ausfällt, fällt A aus. Wenn B langsam ist, bleibt A hängen. Wenn A B anruft, der C anruft, der D anruft, haben Sie eine Anrufkette. Wenn jeder Dienst eine Verfügbarkeit von 99,9 % aufweist, hat eine Kette von 4 Diensten eine Verfügbarkeit von 0,999^4 € = ca. 99,6 %. Sie entwickeln einen gültigen Fehler.
gRPC: Die Hochleistungsvariante
Für die interne Kommunikation bevorzugen wir gRPC (Protobufs) gegenüber REST/JSON.
- Binär verpackt: 10x kleinere Nutzlasten.
- Stark typisiert: Durch die Codegenerierung wird sichergestellt, dass Service A genau weiß, was Service B erwartet.
- Multiplexing: HTTP/2-Unterstützung sofort einsatzbereit.
Asynchron: Die ereignisgesteuerte Architektur
Das ist der heilige Gral der Entkopplung. Dienst A ruft Dienst B nicht auf. Dienst A gibt ein Ereignis aus.
- Benutzerregister (Dienst A).
- Dienst A veröffentlicht das Ereignis „USER_REGISTERED“ in Kafka/RabbitMQ.
- Dienst B (E-Mail) verbraucht Ereignis -> Sendet Willkommens-E-Mail.
- Service C (Analytics) verbraucht Ereignis -> Updates-Dashboard.
Wenn Dienst B offline ist (Wartung, Absturz), funktioniert Dienst A weiterhin. Die Nachricht befindet sich in der Warteschlange. Wenn Dienst B wieder online ist, verarbeitet er den Rückstand. Das ist Resilienz.
Datenkonsistenz: Der schwierigste Teil
In einem Monolith gibt es ACID-Transaktionen. „TRANSAKTION BEGINNEN; Benutzer einfügen; Konto einfügen; COMMIT;` Entweder passiert beides, oder keines von beiden passiert.
In Microservices gibt es Datenbank pro Dienst. Dienst A hat „users_db“. Dienst B hat „accounts_db“. Sie können eine Transaktion nicht über zwei verschiedene Datenbankserver hinweg ausführen.
Das Saga-Muster
Wie gehen Sie mit einer verteilten Transaktion um? Szenario: Der Benutzer gibt eine Bestellung auf.
- Inventarservice: Lagerbestand reservieren.
- Zahlungsdienst: Kreditkarte.
- Versandservice: Etikett drucken.
Was passiert, wenn „Charge Card“ fehlschlägt? Sie haben bereits Lagerbestände reserviert. Sie müssen Schritt 1 „rückgängig machen“. Dies ist eine Ausgleichstransaktion.
„Meerjungfrau Sequenzdiagramm Teilnehmer O als Order Orchestrator Teilnehmer I als Inventar Teilnehmer P als Zahlung
O->>I: Reservebestand (Position X)
I-->>O: Erfolg
O->>P: 100 € berechnen
P-->>O: Fehler (nsf)
O->>I: Lager freigeben (kompensieren)
I-->>O: Erfolg
O-->>Benutzer: Bestellung fehlgeschlagen
„
Sagas richtig umzusetzen ist unglaublich komplex. Sie benötigen einen State-Machine-Orchestrator (wie Standard-AWS Step Functions oder Temporal.io). Wenn Sie diese Komplexität nicht benötigen, entwickeln Sie keine Microservices.
Beobachtbarkeit: Sehen im Dunkeln
In einem Monolithen erfolgt das Debuggen mit „tail -f /var/log/app.log“. In Microservices kann eine einzelne Benutzeranfrage zehn verschiedene Container auf zehn verschiedenen Knoten treffen.
Sie müssen Distributed Tracing (OpenTelemetry) implementieren. Jede Anfrage erhält eine „TraceID“ beim Ingress Load Balancer. Diese ID wird in HTTP-Headern („X-B3-TraceId“) an jeden Downstream-Dienst weitergegeben.
Tools wie Jaeger, Datadog oder Honeycomb visualisieren dann den „Wasserfall“ der Anfrage.
- „Warum waren diese 500 ms langsam?“
- „Oh, Service D hat für eine DB-Abfrage 450 ms benötigt.“
Ohne Rückverfolgung fliegen Sie im Blindflug.
Infrastruktur: Codebeispiel
Wir nutzen Terraform zur Bereitstellung der Infrastruktur für unabhängige Dienste.
„hcl
service-zahlung.tf
Ressource „kubernetes_deployment“ „Zahlung“ { Metadaten { name = “Zahlungsdienst” } Spezifikation { Repliken = 3 Selektor { match_labels = { app = „Zahlung“ } } Vorlage { Metadaten { Etiketten = { app = „Zahlung“ } } Spezifikation { Container { image = “maisoncode/zahlung:v1.2” Name = „Zahlung“
# Die goldene Regel: Ressourcen begrenzen
Ressourcen {
Grenzen = {
CPU = „500m“
Speicher = „512Mi“
}
Anfragen = {
CPU = „250m“
Speicher = „256Mi“
}
}
env {
name = "DB_HOST"
value_from {
Secret_key_ref {
name = "zahlung-db-creds"
Schlüssel = „Host“
}
}
}
}
}
}
} } „
Die Zersetzungsstrategie: The Strangler Fig
Wenn Sie einen alten Monolithen haben, schreiben Sie ihn nicht von Grund auf neu. Verwenden Sie das Würgefeigenmuster.
- Platzieren Sie einen Proxy (API-Gateway) vor dem Monolith.
- Leiten Sie den gesamten Datenverkehr an Monolith weiter.
- Identifizieren Sie einen begrenzten Kontext (z. B. „Rezensionen“).
- Erstellen Sie einen „Bewertungsdienst“.
- Proxy ändern: Wenn der Pfad „/api/reviews“ lautet, zum neuen Dienst weiterleiten. Ansonsten Route zu Monolith.
- Wiederholen, bis der Monolith verschwunden ist.
11. Die Rolle des API-Gateways
Sie möchten nicht, dass 10 Dienste die Authentifizierung unabhängig voneinander durchführen. Sie platzieren ein Gateway (Kong, Tyk oder AWS API Gateway) am Edge. Es behandelt:
- Auth: JWT-Validierung.
- Ratenbegrenzung: 100 Anforderungen/Minute pro IP.
- Caching: Öffentliche GET-Anfragen zwischenspeichern.
- Routing:
/api/v1/users-> Benutzerdienst. Dadurch bleiben Ihre internen Dienste „dumm“ und konzentrieren sich auf die Geschäftslogik.
12. Service Mesh (Istio / Linkerd)
Wenn Sie 50 Dienste haben, benötigt „Dienst A“ für die Kommunikation mit „Dienst B“ eine Verschlüsselung (mTLS). Die manuelle Verwaltung von Zertifikaten ist die Hölle. Ein Service Mesh fügt neben jedem Container einen „Sidecar Proxy“ (Envoy) ein. Der Proxy übernimmt:
- mTLS: Automatische Verschlüsselung.
- Wiederholungen: „Bei Fehlschlagen dreimal mit exponentiellem Backoff versuchen.“
- Stromkreisunterbrechung: „Wenn Dienst B zu 50 % fehlerhaft ist, unterbrechen Sie den Aufruf für 1 Minute.“ Es entkoppelt die Netzwerklogik vom Anwendungscode.
13. Vertragstests (Pakt)
Wie testen Sie Service A, ohne Service B hochzufahren? Vertragstests. Dienst A (Verbraucher) schreibt eine „Paktdatei“: „Ich erwarte, dass „GET /user“ „{ id: string }“ zurückgibt.“ Dienst B (Anbieter) führt einen Test für diese Pact-Datei in seiner CI-Pipeline durch. Wenn Service B „id“ in „userId“ ändert, schlägt der Build fehl. Dadurch werden wichtige Änderungen vor der Bereitstellung erfasst.
14. Fazit
Microservices sind ein organisatorisches Skalierungsmuster, keine Leistungsoptimierung. Sie führen Netzwerklatenz, Konsistenzprobleme und Betriebskomplexität im Austausch für Teamgeschwindigkeit und Unabhängige Skalierbarkeit ein.
Wenn Sie ein Team von 5 Entwicklern sind, bauen Sie einen Monolithen. Man könnte es Microservices nennen, aber Sie bauen lediglich eine verteilte Schmerzmaschine.
Skalieren Sie Ihre Codestruktur, bevor Sie Ihre Infrastruktur skalieren.
**[Beauftragen Sie unsere Architekten](/contact)**.