Die Ausgangssituation: Ein Monolith am Limit
Ein mittelständischer Maschinenbauer aus Baden-Württemberg – nennen wir ihn "TechMach GmbH" – stand vor einer kritischen Herausforderung. Das zentrale Produktionssystem, über 12 Jahre gewachsen, war zum Flaschenhals geworden.
Die Symptome waren typisch:
- Deployments nur noch alle 6-8 Wochen möglich
- Ein kleiner Fehler konnte das gesamte System lahmlegen
- Neue Features brauchten Monate statt Wochen
- Entwickler verbrachten mehr Zeit mit Firefighting als mit Innovation
- Die Dokumentation war veraltet, Wissen nur in einzelnen Köpfen
Die Kennzahlen sprachen eine deutliche Sprache:
- 2,3 Millionen Zeilen Java-Code
- 47 verschiedene Datenbanktabellen in einer Oracle-DB
- Durchschnittlich 4 kritische Incidents pro Monat
- Time-to-Market für neue Features: 4-6 Monate
Die Entscheidung: Warum Microservices?
Die Geschäftsführung stand vor der Frage: Weitermachen wie bisher, das System komplett neu schreiben oder schrittweise modernisieren?
Option 1: Weitermachen (Business as Usual)
- Kurzfristig günstig
- Langfristig unhaltbar
- Risiko steigt kontinuierlich
Option 2: Big Bang Rewrite
- Hohes Risiko (70% der Rewrites scheitern)
- Lange Zeit ohne neue Features
- Parallelbetrieb zweier Systeme
Option 3: Strangler Fig Pattern
- Schrittweise Migration
- Kontinuierlicher Geschäftsbetrieb
- Lerneffekte fließen in spätere Phasen ein
Die Wahl fiel auf Option 3 – und das erwies sich als richtige Entscheidung.
Phase 1: Analyse und Domänenmodellierung (8 Wochen)
Bevor eine Zeile Code geschrieben wurde, investierten wir in das Verständnis des Systems.
Domain-Driven Design Workshop
Mit allen Stakeholdern – Entwicklung, Produktion, Vertrieb, Support – erarbeiteten wir eine gemeinsame Sprache und identifizierten Bounded Contexts:
flowchart TB
subgraph Domain["TechMach Domain"]
subgraph Row1[" "]
subgraph Auftrags["Auftragsmanagement"]
A1["Angebote"]
A2["Aufträge"]
A3["Kunden"]
end
subgraph Produktion["Produktionssteuerung"]
P1["Maschinensteuerung"]
P2["Qualität"]
P3["Wartung"]
end
subgraph Logistik["Logistik-Context"]
L1["Wareneingabe"]
L2["Lager"]
L3["Versand"]
end
end
subgraph Row2[" "]
subgraph Stammdaten["Stammdaten-Context"]
S1["Produkte"]
S2["Materialien"]
S3["Lieferanten"]
end
subgraph Reporting["Reporting & Analytics"]
R1["KPIs"]
R2["Dashboards"]
R3["Prognosen"]
end
subgraph Integration["Integration-Context"]
I1["ERP-Anbindung"]
I2["Lieferanten"]
I3["EDI"]
end
end
endAbhängigkeitsanalyse
Mit Tools wie JDepend und ArchUnit analysierten wir die tatsächlichen Abhängigkeiten im Code – und entdeckten unerwartete Verflechtungen:
- Der Reporting-Code griff direkt auf Produktionstabellen zu
- Auftragslogik war mit Lagerlogik vermischt
- Keine klare API-Schicht, direkter Datenbankzugriff überall
Phase 2: Infrastruktur und Plattform (12 Wochen)
Parallel zur weiteren Analyse bauten wir die Zielplattform auf.
Kubernetes-Cluster
Wir entschieden uns für eine On-Premise Kubernetes-Installation auf bestehender Hardware:
- 3 Master Nodes für Hochverfügbarkeit
- 12 Worker Nodes
- Harbor als Container Registry
- GitLab CI/CD für Automatisierung
Warum On-Premise? Produktionsdaten durften aus Compliance-Gründen das Firmengelände nicht verlassen. Mit Cloud-Anbindung für nicht-kritische Workloads.
Observability Stack
Von Tag 1 an implementierten wir umfassende Beobachtbarkeit:
- Prometheus + Grafana für Metriken
- Loki für Log-Aggregation
- Jaeger für Distributed Tracing
- PagerDuty für Alerting
Phase 3: Der erste Service – Proof of Concept (6 Wochen)
Für den Proof of Concept wählten wir bewusst einen Service mit begrenztem Risiko: das Benachrichtigungssystem.
Warum dieser Service?
- Klar abgegrenzte Funktionalität
- Keine kritischen Geschäftsprozesse
- Überschaubare Integration
- Schnell messbare Ergebnisse
Technologie-Stack
flowchart TB
subgraph NS["Notification Service"]
Runtime["Runtime: Node.js + TypeScript"]
Framework["Framework: NestJS"]
Queue["Queue: RabbitMQ"]
Database["Database: PostgreSQL"]
Container["Container: Docker"]
Orchestration["Orchestration: Kubernetes"]
endErgebnis nach 6 Wochen
- Service in Produktion
- 10x schnellere Deployments als im Monolithen
- Keine Ausfälle durch andere System-Teile
- Team hatte Kubernetes-Erfahrung gesammelt
Phase 4: Kritische Services migrieren (9 Monate)
Mit den Erkenntnissen aus dem PoC begannen wir die Migration der Kerndomänen.
Strangler Fig in der Praxis
Das Pattern funktioniert so: Ein Proxy vor dem Monolithen leitet Requests entweder an den alten oder neuen Code weiter.
flowchart TB
Proxy["Proxy
(Nginx)"]
Auftrags["Neuer
Auftrags-
Service"]
Logistik["Neuer
Logistik-
Service"]
Monolith["Monolith
(Legacy)"]
Proxy --> Auftrags
Proxy --> Logistik
Proxy --> MonolithAnti-Corruption Layer
Um die neuen Services vor dem Legacy-Datenmodell zu schützen, implementierten wir einen Anti-Corruption Layer:
// Beispiel: Legacy-Auftrag zu neuem Modell
class OrderAntiCorruptionLayer {
translateLegacyOrder(legacyOrder: LegacyOrderEntity): Order {
return {
id: OrderId.create(legacyOrder.AUFTR_NR),
customerId: CustomerId.create(legacyOrder.KD_NR),
items: legacyOrder.POSITIONEN.map(this.translateLineItem),
status: this.mapLegacyStatus(legacyOrder.STATUS_CD),
createdAt: this.parseGermanDate(legacyOrder.ANL_DATUM),
};
}
}
Event-Driven Communication
Für die Kommunikation zwischen Services implementierten wir ein Event-System:
- Apache Kafka als Event-Backbone
- Jeder Service published Domain Events
- Lose Kopplung, keine direkten Abhängigkeiten
- Event Sourcing für kritische Aggregates
Die Herausforderungen
Herausforderung 1: Data Consistency
Mit verteilten Services wurde Datenkonsistenz komplex. Unsere Lösungen:
- Saga Pattern für verteilte Transaktionen
- Eventual Consistency wo möglich akzeptieren
- Compensating Transactions für Fehlerfall
Herausforderung 2: Organisatorischer Widerstand
Nicht alle Entwickler waren begeistert. Wir investierten in:
- Intensive Schulungen (2 Wochen pro Entwickler)
- Pair Programming mit erfahrenen Consultants
- Brown Bag Sessions zum Wissensaustausch
- Erfolge sichtbar machen und feiern
Herausforderung 3: Performance
Die erste Version hatte Performance-Probleme durch zu viele Service-Calls. Optimierungen:
- Caching-Strategie mit Redis
- Bulk-APIs für häufige Abfragen
- Asynchrone Verarbeitung wo möglich
Herausforderung 4: Debugging in verteilten Systemen
"Wo ist der Fehler?" wurde komplexer. Lösungen:
- Correlation IDs durch alle Services
- Distributed Tracing obligatorisch
- Runbooks für häufige Fehlerszenarien
Die Ergebnisse nach 18 Monaten
Quantitative Verbesserungen
| Metrik | Vorher | Nachher | Verbesserung |
|---|---|---|---|
| Deployment-Frequenz | 8/Jahr | 50/Woche | 325x |
| Lead Time for Changes | 4-6 Monate | 2-5 Tage | 40x schneller |
| Mean Time to Recovery | 4-8 Stunden | 15 Minuten | 20x schneller |
| Kritische Incidents | 4/Monat | 0,3/Monat | 93% weniger |
| Entwickler-Zufriedenheit | 4,2/10 | 7,8/10 | +85% |
Qualitative Verbesserungen
- Autonome Teams: Jedes Team verantwortet seine Services vollständig
- Innovation: Neue Features in Wochen statt Monaten
- Recruitment: Attraktiverer Tech-Stack zieht Talente an
- Resilienz: Ausfall eines Services betrifft nicht mehr das Gesamtsystem
Lessons Learned
Was wir richtig gemacht haben
- Früh in Observability investiert – unerlässlich für verteilte Systeme
- Klein angefangen – PoC hat Vertrauen geschaffen
- Domain-Driven Design ernst genommen – klare Bounded Contexts
- Kulturellen Wandel nicht unterschätzt – Menschen vor Technik
Was wir anders machen würden
- Früher automatisierte Tests – haben uns am Anfang zu sehr auf manuelle Tests verlassen
- Mehr Kapazität für Platform Team – Kubernetes-Expertise war Engpass
- Klarere API-Verträge – Consumer-Driven Contracts von Anfang an
Fazit: Die Investition lohnt sich
Die Migration von einem Monolithen zu Microservices ist kein Spaziergang. Es ist ein Marathon, der Geduld, Investition und organisatorischen Wandel erfordert.
Aber die Ergebnisse sprechen für sich. TechMach GmbH ist heute ein anderes Unternehmen:
- Schneller am Markt als der Wettbewerb
- Attraktiver Arbeitgeber für IT-Talente
- Resilient gegen Störungen
- Vorbereitet auf zukünftige Anforderungen
Der wichtigste Rat: Unterschätzen Sie nicht den organisatorischen Aspekt. Technologie ist das einfache. Menschen mitzunehmen ist die wahre Herausforderung.
Stehen Sie vor einer ähnlichen Herausforderung? Wir begleiten Sie gerne auf Ihrem Weg – mit Erfahrung aus zahlreichen Migrationsprojekten.