Architektur-Überblick
Leitprinzip
Die Architektur dieses Projekts ist eine Konsequenz der TDD-Praxis, nicht ihr Ausgangspunkt (siehe TDD für Architekten). Jede strukturelle Entscheidung lässt sich auf die Frage zurückführen: "Wie bleibt dieser Code in Millisekunden, ohne Anwendungscontext, testbar?"
Zuschnitt: Microservices nach Bounded Context
Sechs fachliche Services (siehe Domain-Karte), je mit eigener
Datenbank (ADR-0003). Kein API-Gateway, kein Config-Server, kein Service-Discovery-Server — bewusst
schlank gehalten (siehe ADR-0004), um den Fokus auf TDD-Tiefe statt Infrastruktur-Breite zu legen;
lokale Entwicklung läuft über docker-compose mit festen Ports pro Service.
Innerer Aufbau eines Service
Jeder Domain-Service folgt demselben Schichtschnitt (Ports-und-Adapter-Variante):
<service>/
├── domain/ Aggregate, Value Objects, Domain Events, Ports (Interfaces)
│ - KEINE Spring-/Jakarta-Persistenz-Annotationen
├── application/ Anwendungsfaelle (Use-Case-Klassen), orchestrieren Domain + Ports
├── api/ REST-Controller, DTOs, OpenAPI
└── infrastructure/
├── persistence/ JPA-Entities + Repository-Adapter (implementiert domain.Port)
└── messaging/ Kafka-Producer/-Consumer
Abhängigkeitsrichtung: api → application → domain ← infrastructure. infrastructure
implementiert die von domain definierten Port-Interfaces (Dependency Inversion) — domain weiß
nichts von JPA oder Kafka. Durchgesetzt durch ArchUnit-Regeln in architecture-tests (siehe
Pattern-Katalog).
Gemeinsame Module
| Modul | Inhalt |
|---|---|
common-domain |
Framework-freier DDD-Kern: AggregateRoot, DomainEvent, Money, DomainException-Hierarchie |
common-events |
Stabile Integrations-Event-Verträge (Kafka-Payloads) + Topic-Namenskonstanten |
common-web |
Cross-Cutting-Web-Belange: GlobalExceptionHandler, Correlation-Id-Filter (ab Phase 1) |
common-security |
Keycloak/OIDC-Resource-Server-Integration (ab Phase 7, siehe ADR-0008) |
common-testing |
Testcontainers-Fabrikmethoden, Test-Data-Builder (test-scope, ohne eigene Tests) |
architecture-tests |
ArchUnit-Fitness-Functions über alle Module |
Kommunikation zwischen Services
- Synchron (REST): für Anfragen, die eine sofortige Antwort brauchen und für die ein
zeitweiser Ausfall des Zielservices bedeutet, dass der Anfragende (noch) nicht fortfahren kann
(z. B.
accounts-servicefragt beicustomer-service, ob ein Kunde verifiziert ist). - Asynchron (Kafka-Integrationsevents): für Tatsachen, die andere Services irgendwann
erfahren müssen, ohne dass der Publisher auf eine Antwort wartet (z. B.
CustomerRegisteredEvent→notification-service). - Saga mit Kompensation: für Abläufe, die mehrere Services' Zustand konsistent halten müssen,
ohne eine verteilte Transaktion zu verwenden (Überweisung in
payments-service, siehe ADR-0005, ab Phase 4).
Details und Begründungen: siehe ADRs.