Das eigentliche Problem
Als wir uns diese Finanzdienstleistungs-Plattform zum ersten Mal anschauten, hatte sie diesen vertrauten Geruch. Ein Monolith der 50.000 tägliche Nutzer bediente, Deployments die nächtliche Wartungsfenster erforderten, und eine Codebase bei der das Anfassen von Modul A irgendwie Modul Z kaputt machte.
Das Business wollte niedrigere Infrastrukturkosten, mehr Entwicklungsflexibilität, schnellere Time-to-Market und die Möglichkeit einzelne Features unabhängig zu skalieren. Klassiker - aber der Teufel steckt im Detail.
Microservices: Hype vs Realität
Seien wir ehrlich - Microservices lösen spezifische Probleme, nicht alle Probleme. Das hat unsere Entscheidung wirklich getrieben:
- Deployment-Unabhängigkeit - das Payment-Modul shippen ohne User Auth anzufassen
- Blast Radius Containment - wenn (nicht falls) Dinge kaputt gehen, gehen sie klein kaputt
- Polyglotte Persistenz - Postgres für Transaktionen, Redis für Sessions, MongoDB für Dokumente
- Team Ownership - klare Grenzen bedeuten klare Verantwortung
- Gezieltes Skalieren - den Search-Service bei Peak skalieren, nicht die ganze App
Architektur Deep Dive
Wir haben auf Domain-Driven Design aufgebaut, aber nicht die akademische Version. Bounded Contexts entstanden aus echten Team-Gesprächen, nicht aus Whiteboard-Übungen. Unsere Prinzipien:
- Aggregate-Grenzen definieren Service-Grenzen - wenn es eine Transaktion ist, ist es ein Service
- Events über Sync Calls - Choreographie schlägt Orchestrierung in den meisten Fällen
- API-Verträge als First-Class Citizens - brich den Vertrag, brich den Build
- Shared-Nothing-Architektur - jeder Service besitzt seine Daten, Punkt
- Observability ist nicht optional - wenn du es nicht tracen kannst, ship es nicht
Der Stack (und warum)
Jede Tool-Wahl war ein Tradeoff. Hier ist worauf wir gelandet sind:
Infrastructure:
├── Kubernetes (EKS) → Deklarative Deployments, Self-Healing
├── Istio service mesh → mTLS, Traffic Shaping, Circuit Breaking
├── Kong API Gateway → Rate Limiting, Auth, Request Transformation
│
Messaging:
├── Kafka → Event Backbone, 7-Tage Retention
├── Redis Streams → Lightweight Pub/Sub, ephemere Daten
│
Data Layer:
├── PostgreSQL → ACID Transaktionen, JSONB für Flexibilität
├── MongoDB → Document Store für Audit Logs, Activity Feeds
├── Redis Cluster → Session Store, Distributed Caching
├── Elasticsearch → Volltextsuche, Log Aggregation
│
Observability:
├── OpenTelemetry → Vendor-agnostische Instrumentierung
├── Prometheus + Thanos → Metriken mit Langzeitspeicherung
├── Grafana → Dashboards, Alerting
├── Jaeger → Distributed Tracing
│
CI/CD:
├── GitLab CI → Build, Test, Security Scanning
├── ArgoCD → GitOps Deployments
├── Sealed Secrets → K8s-natives Secret Management Patterns die uns gerettet haben
Theorie ist nett. Hier ist was uns wirklich aus der Patsche geholfen hat:
**Transactional Outbox** - Statt Dual-Writes (Datenbank + Message Broker) schreiben wir Events in eine Outbox-Tabelle in derselben Transaktion. Ein separater Prozess publiziert sie. Atomar. Zuverlässig. Keine verteilten Transaktions-Albträume.
**Event Sourcing (wo es zählt)** - Für Payment-Flows und audit-kritische Pfade speichern wir Events, nicht State. Jede Mutation ist ein unveränderliches Event. Production-Issues debuggen durch Replay exakter Sequenzen. Compliance-Teams lieben es.
**CQRS mit Projections** - Write-Models optimiert für Validierung, Read-Models optimiert für Queries. Eventual Consistency ist ok für Read Views. Das Reporting-Team bekommt ihre denormalisierten Tabellen ohne den Write-Path zu verschmutzen.
**Saga Orchestration** - Langlaufende Business-Prozesse (Onboarding, Payment Settlement) als explizite State Machines. Kompensierende Transaktionen bei Failure. Keine verwaisten Partial States.
**Circuit Breaker + Bulkhead** - Hystrix ist tot, aber die Patterns nicht. Resilience4j handhabt Circuit Breaking, Rate Limiting und Retry mit Backoff. Separate Thread Pools für externe Integrationen.
Der Payment Flow: Echte Architektur
So bewegt sich echtes Geld durch das System:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ API Gateway │──────│ Payment │──────│ Fraud │
│ (Kong) │ gRPC │ Service │ Event│ Detection │
└─────────────┘ └──────┬──────┘ └──────┬──────┘
│ │
PaymentInitiated FraudCheckCompleted
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Outbox │ │ Risk │
│ Table │ │ Scoring │
└──────┬──────┘ └──────┬──────┘
│ │
Debezium CDC RiskAssessed
│ │
▼ ▼
┌─────────────────────────────────┐
│ Kafka Topics │
│ payments.initiated │
│ fraud.checked │
│ risk.assessed │
│ payments.completed │
└─────────────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Ledger │ │ Order │ │Notification │
│ Service │ │ Service │ │ Service │
└─────────────┘ └─────────────┘ └─────────────┘ Testing-Strategie die wirklich funktioniert
Vergiss die Testing-Pyramide für einen Moment. In verteilten Systemen brauchst du:
- Contract Tests (Pact) - Services reden mit Stubs, nicht echten Dependencies. Contracts brechen in CI, nicht Production
- Consumer-Driven Contracts - Consumer definieren was sie brauchen, Provider beweisen dass sie liefern
- Chaos Testing (Chaos Monkey, Litmus) - Pods zufällig killen. Latenz injizieren. Resilienz beweisen
- Synthetic Monitoring - Kontinuierliche Production-Probes für kritische User Journeys
- Load Testing als Validierung - Wir haben validiert dass die Architektur 80x den ursprünglichen Traffic handeln kann durch rigorose Load Tests vor Launch
- Canary Deployments - 1% Traffic zu neuen Versionen, automatisches Rollback bei Error-Spike
Was wirklich passiert ist
Am Anfang haben wir uns auf das Zerlegen des Monolithen fokussiert - Nähte identifizieren, das alte System Service für Service strangulieren. Zu Beginn war es holprig. Distributed Debugging Skills fehlten, Traces waren unvollständig, und Network Partitions legten Consistency Bugs offen.
Nach einigen Monaten hat es klick gemacht. Teams besaßen ihre Services end-to-end. Deployments wurden zu Non-Events. Das Platform Engineering Team hatte genug Golden Paths gebaut dass das Hochfahren eines neuen Services Stunden dauerte, nicht Wochen.
Als Zeit vergangen war und die Architektur reifte, sprachen die Ergebnisse für sich:
- Response Times fielen von 850ms p99 auf unter 120ms p99
- Zero-Downtime Deployments - Wartungsfenster wurden zur Erinnerung
- Infrastrukturkosten 35% runter trotz höherem Traffic
- Deployment-Frequenz: von monatlich zu 50+ täglichen Deploys
- Mean Time to Recovery: unter 5 Minuten für die meisten Incidents
Die harten Lektionen
Nicht alles lief glatt. Hier ist was wehgetan hat:
**Eventual Consistency ist ein Feature, kein Bug** - Aber erklär das dem PM der sich wundert warum das Dashboard veraltete Daten zeigt. Design dafür. Kommunizier es.
**Distributed Tracing oder Tod** - Ohne Correlation IDs und saubere Trace Context Propagation ist Debugging Archäologie. OpenTelemetry Auto-Instrumentation ist dein Freund.
**Schema Evolution ist schwer** - Avro mit Schema Registry. Nur backwards-kompatible Änderungen. Breaking Changes erfordern ein neues Topic.
**Kubernetes ist ein Betriebssystem** - Kämpf nicht dagegen an. Lern es. Resource Limits, Liveness Probes, Pod Disruption Budgets - die existieren aus Gründen.
**Platform Team ist nicht verhandelbar** - Jemand muss die Infrastruktur-Abstraktionen ownen. Sonst erfindet jedes Team das Rad neu.
Wann KEINE Microservices
Klartext - Microservices sind teuer. Erwäge Alternativen wenn:
- Dein Team ist klein - Koordinations-Overhead killt Velocity
- Domain-Grenzen sind unklar - du zeichnest sie falsch und leidest unter Migrations-Schmerzen
- Du hast keine Platform Engineering Kapazität - Infrastruktur-Komplexität explodiert
- Latenz-Anforderungen sind extrem - Network Hops summieren sich
- Dein Monolith braucht nur bessere Modularisierung - probier zuerst einen modularen Monolithen
Takeaways
Am Ende dieser Reise hatten wir eine Plattform die kontinuierlich deployete, on-demand skalierte und Teams echte Ownership gab. Das Business bekam was sie wollten: niedrigere Kosten, schnellere Delivery und die Flexibilität sich weiterzuentwickeln.
War es das wert? Für diese Größenordnung und diese Anforderungen, absolut. Aber wir haben mit einem modularen Monolithen angefangen und Services nur extrahiert als der Schmerz real war.
Denkst du über so eine Transformation nach? Fang mit dem Problem an, nicht der Lösung.
Key Takeaways
Microservices-Architektur geht nicht darum Trends zu folgen - es geht darum spezifische Skalierungs- und Organisations-Herausforderungen zu lösen. Die Patterns die wir behandelt haben (Transactional Outbox, Event Sourcing, CQRS, Saga Orchestration) sind keine theoretischen Übungen; sie sind kampferprobte Lösungen für echte Probleme verteilter Systeme.
Wir haben validiert dass die Architektur 80x den ursprünglichen Traffic aushalten kann durch umfassendes Load Testing. Deployments gingen von monatlichen Events zu Non-Events die dutzende Male täglich passieren. Das ist die Auszahlung wenn man die Fundamentals richtig macht.
Hast du Architektur-Herausforderungen mit denen du ringst? Lass uns über Patterns reden.
Engineering Team
Senior Solutions Architects
Wir bauen verteilte Systeme seit bevor 'Microservices' ein Begriff war. Unsere Narben erzählen Geschichten.