- Authors

- Name
- Phillip Pham
- @ddppham
TL;DR
Einen Monolith zerlegt man nicht auf einen Schlag, sondern schrittweise mit dem Strangler Fig Pattern. Bounded Contexts aus Domain-Driven Design liefern die Schnittgrenzen. Jeder extrahierte Service bekommt ein eigenes Kubernetes Deployment und eine eigene Datenbank. Starte mit dem Service, der den geringsten Kopplungsgrad hat und den hoechsten Business-Value liefert. Drei bis fuenf Services im ersten Schritt reichen.
Wann ein Monolith zum Problem wird
Nicht jeder Monolith muss zerlegt werden. Aber wenn Deployments laenger als 30 Minuten dauern, Teams sich gegenseitig blockieren und ein Fehler im Zahlungsmodul die gesamte Applikation lahmlegt -- dann ist es Zeit.
Typischer Monolith (E-Commerce):
+--------------------------------------------------+
| monolith.jar / monolith-app |
| |
| +------------+ +-----------+ +--------------+ |
| | Bestellung | | Produkt- | | Benutzer- | |
| | (Orders) |--| katalog |--| verwaltung | |
| +------------+ +-----------+ +--------------+ |
| | | | |
| +--------------------------------------------------+
| | Gemeinsame Datenbank (PostgreSQL) |
| +--------------------------------------------------+
+--------------------------------------------------+
Das Kernproblem: Alle Module teilen sich eine Datenbank, einen Deployment-Zyklus und einen Laufzeitprozess. Eine Aenderung am Produktkatalog erfordert ein Deployment der gesamten Applikation.
Schritt 1: Bounded Contexts identifizieren
Domain-Driven Design liefert das Werkzeug fuer die Zerlegung. Ein Bounded Context ist ein fachlicher Bereich mit eigener Sprache, eigenen Regeln und eigenen Daten. Im E-Commerce-Beispiel:
| Bounded Context | Kernentitaeten | Daten-Ownership | Kopplungsgrad |
|---|---|---|---|
| Bestellungen | Order, OrderItem, Payment | Bestellhistorie, Zahlungsstatus | Mittel |
| Produktkatalog | Product, Category, Price | Produktdaten, Preise, Bilder | Niedrig |
| Benutzerverwaltung | User, Role, Address | Login-Daten, Profile, Adressen | Niedrig |
| Versand | Shipment, Tracking | Versandstatus, Adressen | Hoch (zu Bestellungen) |
| Lagerverwaltung | Inventory, Warehouse | Bestaende, Reservierungen | Hoch (zu Produkten) |
Die Faustregel: Wenn zwei Kontexte unterschiedliche Begriffe fuer dasselbe Konzept verwenden, gehoeren sie getrennt. Der Produktkatalog kennt einen "Artikel", die Bestellung kennt eine "Position". Unterschiedliche Sprache = unterschiedlicher Service.
Schritt 2: Strangler Fig Pattern anwenden
Das Strangler Fig Pattern ersetzt den Monolith schrittweise. Neue Requests werden an den neuen Service geroutet, der Monolith wird Stueck fuer Stueck kleiner. Kein Big-Bang-Rewrite.
Phase 1: Proxy vor den Monolith
+------------------+
/-*/}| Produktkatalog |
+--------+ +-----------+ / | (neuer Service) |
| Client |-*/}| API |--/ +------------------+
+--------+ | Gateway / |
| Ingress |--\ +------------------+
+-----------+ \--*/}| Monolith |
| (Bestellungen + |
| Benutzer) |
+------------------+
Phase 2: Weiterer Service extrahiert
+------------------+
/-*/}| Produktkatalog |
+--------+ +-----------+ / +------------------+
| Client |-*/}| API |--/
+--------+ | Gateway / |----*/} +------------------+
| Ingress | | Benutzerverwaltung|
+-----------+--\ +------------------+
\
\-*/}+------------------+
| Monolith |
| (nur Bestellung) |
+------------------+
Der Ingress Controller routet basierend auf URL-Pfaden. Das ist der Kern der Strangler Fig: Der Client merkt nichts von der Migration.
Schritt 3: Kubernetes Deployments erstellen
Jeder extrahierte Service bekommt ein eigenes Deployment, einen eigenen Service und eigene Ressourcen-Limits.
Produktkatalog-Service
apiVersion: apps/v1
kind: Deployment
metadata:
name: product-catalog
namespace: shop
labels:
app: product-catalog
team: catalog-team
spec:
replicas: 2
selector:
matchLabels:
app: product-catalog
template:
metadata:
labels:
app: product-catalog
spec:
containers:
- name: product-catalog
image: registry.internal/product-catalog:1.0.0
ports:
- containerPort: 8080
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 15
env:
- name: DB_HOST
valueFrom:
secretKeyRef:
name: catalog-db-credentials
key: host
---
apiVersion: v1
kind: Service
metadata:
name: product-catalog
namespace: shop
spec:
selector:
app: product-catalog
ports:
- port: 80
targetPort: 8080
Benutzer-Service und Bestell-Service (Monolith-Rest)
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
namespace: shop
spec:
replicas: 2
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: registry.internal/user-service:1.0.0
ports:
- containerPort: 8081
resources:
requests:
cpu: 150m
memory: 256Mi
limits:
cpu: 400m
memory: 512Mi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: shop
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: registry.internal/order-service:1.0.0
ports:
- containerPort: 8082
resources:
requests:
cpu: 300m
memory: 512Mi
limits:
cpu: "1"
memory: "1Gi"
Ingress fuer Strangler Fig Routing
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-routing
namespace: shop
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
- host: shop.example.de
http:
paths:
- path: /api/products(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: product-catalog
port:
number: 80
- path: /api/users(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: user-service
port:
number: 80
- path: /api/orders(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: order-service
port:
number: 80
Schritt 4: Datenbank aufteilen
Die Datenbank-Dekomposition ist der schwierigste Teil. Drei Strategien:
Shared Database (Uebergang): Alle Services lesen noch aus derselben Datenbank. Schnell umgesetzt, aber keine echte Entkopplung. Nur als temporaere Loesung.
Database per Service (Ziel): Jeder Service hat seine eigene Datenbank-Instanz. Der Produktkatalog besitzt die products- und categories-Tabellen, der Bestell-Service die orders-Tabellen.
Database View (Kompromiss): Services greifen ueber Views auf Daten anderer Services zu. Besser als Direct Table Access, aber immer noch gekoppelt.
Zielarchitektur: Database per Service
+------------------+ +------------------+ +------------------+
| product-catalog | | order-service | | user-service |
+------------------+ +------------------+ +------------------+
| | |
+------------------+ +------------------+ +------------------+
| catalog-db | | orders-db | | users-db |
| (PostgreSQL) | | (PostgreSQL) | | (PostgreSQL) |
| - products | | - orders | | - users |
| - categories | | - order_items | | - roles |
| - prices | | - payments | | - addresses |
+------------------+ +------------------+ +------------------+
Fuer Abfragen, die Daten aus mehreren Services benoetigen (z.B. "Zeige alle Bestellungen mit Produktnamen"), gibt es zwei Ansaetze: API Composition (der aufrufende Service fragt beide APIs) oder Event-basierte Datensynchronisation (CQRS).
Entscheidungskriterien: Welchen Service zuerst extrahieren?
| Kriterium | Gewicht | Produktkatalog | Bestellungen | Benutzerverwaltung |
|---|---|---|---|---|
| Geringe Kopplung | Hoch | 9/10 | 5/10 | 8/10 |
| Eigenstaendige Daten | Hoch | 9/10 | 6/10 | 9/10 |
| Team-Autonomie | Mittel | 8/10 | 5/10 | 7/10 |
| Business Value | Mittel | 7/10 | 9/10 | 4/10 |
| Aenderungshaeufigkeit | Niedrig | 8/10 | 6/10 | 3/10 |
| Gesamt | 41 | 31 | 31 |
Der Produktkatalog gewinnt: geringe Kopplung, eigene Daten, haeufige Aenderungen. Starte dort.
FAQ
Wie lange dauert eine Monolith-zu-Microservices-Migration?
Rechne mit 3-6 Monaten pro extrahiertem Service, je nach Kopplungsgrad und Teamgroesse. Eine vollstaendige Migration eines mittelgrossen Monoliths dauert typischerweise 12-24 Monate. Der Strangler Fig Ansatz erlaubt aber produktive Nutzung der neuen Services ab Tag 1.
Wie viele Microservices sollte man haben?
Nicht mehr als ein Team pro Service verantworten kann. Die Faustregel "Two-Pizza Team" gilt: Ein Service gehoert einem Team von 3-8 Personen. 50 Services fuer 10 Entwickler sind ein Anti-Pattern.
Was passiert mit Transaktionen ueber Service-Grenzen hinweg?
Verteilte Transaktionen (Two-Phase Commit) funktionieren in Microservices schlecht. Setze auf Sagas: Eine Kette von lokalen Transaktionen mit kompensierenden Aktionen im Fehlerfall. Der Bestell-Service reserviert Bestand, bucht Zahlung und bestaetigt -- bei Fehler wird jeder Schritt rueckgaengig gemacht.
Brauche ich sofort Database per Service?
Nein. Starte mit Shared Database und trenne die Daten schrittweise. Wichtiger ist, dass der Service-Code keine direkten Tabellenzugriffe auf Daten anderer Services macht. Erzwinge den Zugriff ueber APIs, auch wenn die Datenbank noch gemeinsam ist.
Welche Tools helfen bei der Analyse des Monolithen?
Fuer Java: jDepend, ArchUnit, Structure101. Fuer allgemeine Codeanalyse: CodeScene (Change Coupling), SonarQube (Abhängigkeiten). Fuer Laufzeitanalyse: Distributed Tracing mit Jaeger zeigt, welche Module wie oft miteinander kommunizieren.
Legacy zu Kubernetes migrieren?
Wir begleiten Ihre Migration von VMs zu Containern – ohne Produktionsausfall. Erfahrung aus 50+ Migrationsprojekten.
Kubernetes-Beratung gesucht?
Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.
📖 Verwandte Artikel
Weitere interessante Beiträge zu ähnlichen Themen
Strangler Fig Pattern: Monolith zu Microservices migrieren
Schrittweise Migration vom Monolithen zu Microservices mit dem Strangler Fig Pattern auf Kubernetes inklusive Ingress-Routing und Rollback-Strategie.
Lift-and-Shift vs Refactoring: Migration im Vergleich
Lift-and-Shift oder Refactoring für Kubernetes? Entscheidungsmatrix, praktische Beispiele und das Strangler-Fig-Pattern als Mittelweg für Legacy-Migrationen.
Legacy-Anwendungen auf Kubernetes migrieren: Anleitung
Legacy-Anwendungen auf Kubernetes migrieren: Vier Migrationsmuster von Lift-and-Shift bis Rebuild mit Schritt-für-Schritt-Anleitung und YAML-Beispielen.
Shared Database in Kubernetes: Schema-per-Service richtig umsetzen
Shared Database in Kubernetes mit Schema-per-Service Pattern: PostgreSQL StatefulSet Setup, Flyway-Migrations und Zugriffskontrolle für Microservices.
Kubernetes CQRS Pattern Microservices in Deutschland optimal nutzen
Optimieren Sie Skalierbarkeit und Performance Ihrer komplexen Microservices auf Kubernetes in Deutschland mit dem CQRS Pattern. Erfahren Sie, wie diese zukunftsweisende Architektur Compliance-Anforderungen erfüllt und digitale Souveränität für deutsche Unternehmen sichert.