Empfohlene Lesereihenfolge
01-systemueberblick.md- große fachliche und technische Sicht.02-entwicklerhandbuch.md- wie man im Code arbeitet.03-betriebshandbuch.md- wie man das System lokal und produktionsnah betreibt.04-lokaler-start-und-troubleshooting.md- Start, Checks und typische Fehler.05-architekturentscheidungen.md- wichtige Entscheidungen und Alternativen.06-security-handbuch.md- Keycloak, Rollen, JWT und API-Schutz.07-observability-handbuch.md- Metriken, Health, Prometheus, Grafana.08-resilience-handbuch.md- Retry, Timeout, Circuit Breaker, Rate Limiting.09-datenbankbetrieb-handbuch.md- Migration, Backup, Restore, Retention.10-cicd-deployment-handbuch.md- CI/CD, Quality Gates, Docker, Kubernetes, OpenShift.11-teststrategie-handbuch.md- Testpyramide und Testcontainers.12-fehlerkatalog-und-runbooks.md- konkrete Fehlerfälle und Reaktion.13-glossar.md- zentrale Begriffe.14-dateien-und-ordnerkarte.md- wo liegt was?15-lernplan.md- wie man das Projekt als Lernpfad nutzt.
Wichtige Einstiegspunkte
| Datei | Zweck |
|---|---|
index.html | Schöne Startseite mit Links auf alle wichtigen Artefakte |
OPEN_THIS_V2_REAL_SYSTEMS.html | identischer Schnellstart für lokales Öffnen |
README.md | kompakter Projektüberblick |
START-HIER.md | praktische Startanleitung |
lehrbuch/codex-enterprise-maven-v2-real-systems-doku-final.pdf | finale Dokumentation als PDF |
DOKUMENTATION-GESAMT.html | finale HTML-Gesamtdokumentation |
Dokumentationsprinzip
Die Dokumentation folgt drei Ebenen:
- Lern-Ebene: Begriffe, Architektur und Entscheidungen verstehen.
- Praxis-Ebene: Container starten, REST Flow testen, Metriken ansehen.
- Betriebs-Ebene: Fehler erkennen, Runbooks nutzen, Security und Backup verstehen.
---
Zielbild
Das Projekt simuliert eine realistische Enterprise-Plattform rund um Order, Billing, Security, Messaging, Observability und Betrieb. Es ist bewusst nicht als Mini-Beispiel gedacht, sondern als kompaktes Labor, in dem typische Enterprise-Schnittstellen und Betriebsfragen sichtbar werden.
Fachlicher Kern
Der fachliche Kern ist ein Order Acceptance Flow:
- Ein Client ruft einen REST-Endpunkt auf.
- Die Runtime prüft über Spring Security, ob der JWT die richtige Rolle enthält.
- Der Application Service erstellt oder akzeptiert eine Bestellung.
- Der Payment Gateway Adapter ruft einen Mock Payment Provider auf.
- Die Bestellung wird in PostgreSQL gespeichert.
- Gleichzeitig wird ein Outbox Event geschrieben.
- Ein Relay veröffentlicht das Event nach RabbitMQ.
- Ein Billing Consumer reagiert auf das Event.
- Observability-Komponenten messen Zustand, Fehler und Durchsatz.
Technische Systemgrenzen
| System | Verantwortung | Warum im Lab enthalten? |
|---|---|---|
| Spring Boot Runtime | REST, Security, Use Cases, Adapter Verdrahtung | ausführbare Anwendung |
| PostgreSQL | fachliche Daten und Outbox | echte relationale Persistenz |
| RabbitMQ | asynchrone Event-Kommunikation | lose Kopplung und DLQ lernen |
| Keycloak | Identity Provider | OAuth2/OIDC realistisch üben |
| WireMock | externe Provider simulieren | Payment/Fraud ohne echte Anbieter testen |
| MailHog | E-Mail Simulation | technische Benachrichtigung lokal testen |
| Prometheus | Metriken sammeln | Betriebssicht und Alerts |
| Grafana | Dashboards | Metriken visuell verstehen |
Architekturidee
Die Struktur folgt Ports and Adapters:
Domain
keine Framework-Abhängigkeit
Application
koordiniert Use Cases
Ports
fachliche Verträge für Infrastruktur
Adapters
PostgreSQL, RabbitMQ, WireMock, Payment, Outbox
Runtime
Spring Boot, Security, Scheduling, REST, Actuator
Der wichtigste Lernpunkt: Fachlogik soll nicht von PostgreSQL, RabbitMQ, Keycloak oder Spring Security abhängig sein. Diese Dinge sitzen am Rand.
Kritische Flows
Synchronous Flow
Client -> REST -> Security -> Use Case -> Payment Provider -> PostgreSQL -> Response
Dieser Flow ist direkt sichtbar. Wenn Payment langsam oder fehlerhaft ist, muss Resilience greifen.
Asynchronous Flow
Use Case -> Outbox -> Relay -> RabbitMQ -> Billing Consumer
Dieser Flow zeigt lose Kopplung. Billing hängt nicht direkt im REST Request, sondern reagiert später auf Events.
Operational Flow
Runtime -> Actuator -> Prometheus -> Grafana -> Alert/Runbook
Dieser Flow zeigt, wie Betrieb und Entwicklung zusammenarbeiten.
---
Arbeitsweise im Code
Das Maven-Projekt ist modular aufgebaut. Änderungen sollen immer in der passenden Schicht landen.
maven-project/
├── domain
├── ports
├── application
├── adapters
├── runtime
└── integration-tests
Regel: Fachlogik zuerst
Neue fachliche Regeln gehören zuerst in domain. Beispiel: Eine Bestellung darf nicht ohne Position angenommen werden. Diese Regel darf nicht in einem REST Controller oder Repository versteckt werden.
// Pattern: Aggregate Root - Order schützt fachliche Invarianten zentral.
public final class Order {
private final OrderId id;
private final List<OrderLine> lines;
private OrderStatus status;
private Order(OrderId id, List<OrderLine> lines) {
if (lines == null || lines.isEmpty()) {
throw new DomainRuleViolation("Eine Bestellung braucht mindestens eine Position.");
}
this.id = id;
this.lines = List.copyOf(lines);
this.status = OrderStatus.DRAFT;
}
}
Regel: Abhängigkeiten zeigen nach innen
Die Runtime darf Application, Ports und Adapters kennen. Die Domain darf nichts von Runtime, Spring, PostgreSQL oder RabbitMQ kennen.
| Modul | Darf kennen | Darf nicht kennen |
|---|---|---|
domain | eigene Value Objects, Entities | Spring, JDBC, RabbitMQ |
ports | Domain | Adapter-Implementierungen |
application | Domain, Ports | PostgreSQL, Keycloak, HTTP |
adapters | Ports, Domain, technische Libraries | REST Controller Logik |
runtime | alle Module zur Verdrahtung | Fachregeln implementieren |
Neuer Use Case: Vorgehen
- Fachliche Sprache notieren.
- Domain-Regeln ergänzen.
- Port definieren, falls Infrastruktur nötig ist.
- Application Service schreiben.
- Adapter implementieren.
- Runtime verdrahten.
- Tests ergänzen.
- Doku und Runbook aktualisieren.
Beispiel: neuer Provider
Wenn ein Fraud Provider ergänzt wird, sollte die Application nur einen Port sehen:
public interface FraudGateway {
FraudDecision check(Order order);
}
Die konkrete HTTP-Kommunikation gehört in einen Adapter:
// Pattern: Gateway Adapter - externer Fraud Provider wird gekapselt.
public final class WireMockFraudGateway implements FraudGateway {
private final RestClient restClient;
public FraudDecision check(Order order) {
return restClient.post()
.uri("/fraud/check")
.body(order.id().value())
.retrieve()
.body(FraudDecision.class);
}
}
Coding-Konventionen
- Entwurfsmuster im Code kurz kommentieren.
- Keine Business-Regeln im Controller verstecken.
- Keine Datenbankdetails in Domain-Klassen.
- Keine langen Monster-Methoden ohne Zerlegung.
- Adapter dürfen technisch sein, Application Services sollen lesbar bleiben.
- Jede neue externe Integration braucht ein Runbook und ein Fehlerbild.
Lokale Entwicklerbefehle
docker compose up -d
cd maven-project
mvn -q -DskipTests package
mvn -pl runtime spring-boot:run
Typische Entwicklungsfehler
| Fehler | Ursache | Besser |
|---|---|---|
| Business-Regel im Controller | schneller Hack | Regel in Domain verschieben |
| Repository direkt im Controller | Schichten vermischt | Use Case nutzen |
| RabbitTemplate im Use Case | Broker-Leak | Publisher Port nutzen |
| JWT Claims in Domain | Security-Leak | Security am Runtime-Rand halten |
| Test nur mit Mock | Integration bleibt unsichtbar | kritischen Pfad mit Testcontainers testen |
---
Betriebsziel
Das System soll lokal nachvollziehbar, in Kubernetes/OpenShift übertragbar und operativ beobachtbar sein. Betrieb bedeutet hier nicht nur "Container läuft", sondern: Start, Health, Logs, Metriken, Backup, Restore, Security und Incident Response sind dokumentiert.
Betriebsmodi
| Modus | Zweck | Startpunkt |
|---|---|---|
| Lokal | Lernen und Entwicklung | docker compose up -d |
| CI | Qualität prüfen | .github/workflows/ci.yml |
| Kubernetes | generisches Deployment | deploy/kubernetes |
| OpenShift | Enterprise Deployment | deploy/openshift |
| Helm | paketiertes Deployment | deploy/helm/order-runtime |
Mindestchecks nach Start
docker compose ps
curl -fsS http://localhost:8082/realms/order-platform/.well-known/openid-configuration
curl -fsS http://localhost:8089/__admin/mappings
curl -fsS http://localhost:9090/-/ready
Laufende Betriebsindikatoren
| Indikator | Bedeutung | Reaktion |
|---|---|---|
| Outbox Backlog steigt | Events werden nicht veröffentlicht | RabbitMQ/Relay prüfen |
| Payment Timeout steigt | Provider langsam | Circuit Breaker und Provider prüfen |
| 401/403 steigt | Token/Rollenproblem | Keycloak und Clients prüfen |
| DB Connections hoch | Pool/Queries problematisch | SQL und Connection Pool prüfen |
| DLQ wächst | Consumer kann Nachrichten nicht verarbeiten | Fehlerpayload analysieren |
Log-Prinzip
Jeder Request soll eine Correlation ID erhalten. Dadurch kann man REST-Aufruf, Payment-Aufruf, Outbox-Eintrag und Consumer-Log zusammenführen.
X-Correlation-Id: 7f4d1c...
Betriebsrollen
| Rolle | Verantwortung |
|---|---|
| Entwickler | Code, Tests, fachliche Fehleranalyse |
| Plattform-Team | Container, Netzwerk, Secrets, Ressourcen |
| Betrieb | Monitoring, Incident Response, Backup |
| Security | Rollen, Token, Policies, Audits |
| Product/Fachbereich | fachliche Priorisierung bei Incidents |
Wartungsfenster
Für Datenbankmigrationen, große Konfigurationsänderungen und Keycloak-Realm-Änderungen sollte ein Wartungsfenster geplant werden. Kleine Runtime-Releases können bei Kubernetes/OpenShift über Rolling Updates erfolgen.
---
Schnellstart
cp .env.example .env
docker compose up -d
cd maven-project
mvn -q -DskipTests package
mvn -pl runtime spring-boot:run
Wichtige URLs
| System | URL | Login |
|---|---|---|
| Runtime | http://localhost:8080 | Bearer Token |
| Adminer | http://localhost:8081 | order_user / order_pass |
| Keycloak | http://localhost:8082 | admin / admin |
| RabbitMQ | http://localhost:15672 | order_user / order_pass |
| WireMock | http://localhost:8089 | kein Login |
| MailHog | http://localhost:8025 | kein Login |
| Prometheus | http://localhost:9090 | kein Login |
| Grafana | http://localhost:3000 | admin / admin |
Geschützten Flow testen
./scripts/security/call-secured-order-flow.sh
Event Flow testen
curl -X POST http://localhost:8080/api/orders/ORD-LOCAL-1/accept \
-H "Authorization: Bearer ${TOKEN}"
Danach prüfen:
select event_id, aggregate_id, event_type, status
from outbox.events
order by event_id desc;
Fehler: Port ist belegt
Symptom:
bind: address already in use
Prüfen:
docker ps
lsof -i :5432
lsof -i :8082
Lösung:
docker compose down
Oder Ports in docker-compose.yml ändern.
Fehler: Keycloak Token funktioniert nicht
Mögliche Ursachen:
- Realm wurde noch nicht importiert.
- Keycloak ist noch nicht vollständig gestartet.
- Falscher Client
order-runtime. - User oder Passwort falsch.
- Runtime erwartet einen anderen Issuer.
Prüfung:
curl http://localhost:8082/realms/order-platform/.well-known/openid-configuration
Fehler: Outbox bleibt NEW
Ursachen:
- Runtime Scheduler läuft nicht.
- RabbitMQ ist nicht erreichbar.
- Routing Key ist falsch.
- Publisher wirft Exception.
Prüfung:
select * from outbox.events order by event_id desc;
RabbitMQ prüfen:
http://localhost:15672
Fehler: Metriken fehlen
Prüfen:
curl http://localhost:8080/actuator/prometheus
curl http://localhost:9090/targets
Wenn Prometheus die Runtime nicht erreicht, ist host.docker.internal auf der Plattform eventuell anders zu konfigurieren.
---
Entscheidung 1: Maven Multi-Module statt einzelnes Projekt
Entscheidung: Das Projekt nutzt domain, ports, application, adapters, runtime und integration-tests.
Grund: Die Modulgrenzen machen Abhängigkeiten sichtbar. Ein einzelnes Projekt wäre schneller, aber fachliche und technische Klassen würden leichter vermischt.
Konsequenz: Mehr Struktur, mehr POM-Dateien, aber bessere Lernwirkung.
Entscheidung 2: Ports and Adapters
Entscheidung: Externe Systeme werden über Ports abstrahiert.
Alternativen: Direkt JdbcTemplate, RabbitTemplate oder RestClient im Use Case verwenden.
Bewertung: Direkte technische Abhängigkeiten sind am Anfang schneller, führen aber zu schlechter testbarer Fachlogik.
Entscheidung 3: Transactional Outbox
Entscheidung: Events werden zuerst in PostgreSQL gespeichert und danach durch ein Relay publiziert.
Grund: Vermeidung des Dual-Write-Problems zwischen Datenbank und Message Broker.
Konsequenz: Zusätzliche Tabelle, Relay-Logik und Monitoring nötig.
Entscheidung 4: Keycloak als echter Identity Provider
Entscheidung: OIDC/JWT wird nicht nur simuliert, sondern über Keycloak bereitgestellt.
Grund: Rollen, Claims, Issuer und Tokenfehler sind in Enterprise-Projekten zentrale Fehlerquellen.
Konsequenz: Mehr Container-Setup, aber deutlich realistischer.
Entscheidung 5: WireMock statt echter Payment Provider
Entscheidung: Payment und Fraud werden als Mock Provider umgesetzt.
Grund: Externe Anbieter sind teuer, instabil oder nicht lokal verfügbar. WireMock erlaubt klare Verträge.
Entscheidung 6: Docker Compose als lokales Systemlabor
Entscheidung: Alle Systeme laufen lokal per Compose.
Grund: Ein Lernprojekt braucht reproduzierbaren Start ohne Cloud-Abhängigkeit.
Entscheidung 7: Kubernetes/OpenShift ergänzen, aber Compose behalten
Entscheidung: Compose bleibt Lern- und Entwicklungsmodus. Kubernetes/OpenShift sind Deployment-Profile.
Grund: Beide Welten haben andere Ziele. Compose ist schnell, Kubernetes ist produktionsnäher.
Entscheidung 8: Observability als Code
Entscheidung: Prometheus-Regeln und Grafana Dashboards liegen als Dateien im Projekt.
Grund: Monitoring darf kein manuelles Klickprodukt sein.
Entscheidung 9: Resilience als Boundary
Entscheidung: Retry, Timeout und Circuit Breaker sitzen um externe Calls, nicht in der Domain.
Grund: Resilience ist technische Schutzlogik. Die Fachlogik soll nicht wissen, ob ein Provider gerade instabil ist.
---
Security-Ziel
Die Anwendung nutzt Keycloak als Identity Provider. Die Runtime ist ein OAuth2 Resource Server. REST-Endpunkte werden über Rollen geschützt.
Rollenmodell
| Keycloak Rolle | Spring Authority | Zugriff |
|---|---|---|
order-reader | ROLE_ORDER_READER | lesende Order-Funktionen |
order-admin | ROLE_ORDER_ADMIN | Bestellung annehmen |
billing-user | ROLE_BILLING_USER | Billing-Funktionen |
Tokenfluss
Client -> Keycloak Token Endpoint -> JWT -> Runtime -> Spring Security -> Controller
Wichtige Regel
Security bleibt in der Runtime-Schicht. Domain und Application sollen keine JWT Claims auswerten.
Token holen
TOKEN="$(
curl -s -X POST "http://localhost:8082/realms/order-platform/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=order-runtime" \
-d "grant_type=password" \
-d "username=order-admin" \
-d "password=admin" | python -c "import sys,json; print(json.load(sys.stdin)['access_token'])"
)"
Positivtest
curl -i -X POST "http://localhost:8080/api/orders/ORD-SEC-1/accept" \
-H "Authorization: Bearer ${TOKEN}"
Erwartung: 200 OK.
Negativtest
Mit order-reader darf der POST nicht erlaubt sein. Erwartung: 403 Forbidden.
Häufige Security-Fehler
| Symptom | Ursache | Lösung |
|---|---|---|
| 401 Unauthorized | Token fehlt/ungültig | Token neu holen |
| 403 Forbidden | Token gültig, Rolle fehlt | Keycloak Rolle prüfen |
| Issuer mismatch | Runtime erwartet anderen Issuer | application.yml prüfen |
| Rollen leer | Converter findet Claim nicht | realm_access.roles prüfen |
| Prometheus kann nicht scrapen | Endpoint geschützt | /actuator/prometheus permitAll lassen |
Produktionshinweise
- Keine Default-Passwörter in Produktion.
- Realm-Konfiguration versionieren.
- Client Secrets und Admin-Passwörter als Secrets verwalten.
- Token-Lebensdauer bewusst wählen.
- Rollen nicht direkt aus UI-Wünschen ableiten, sondern fachlich modellieren.
---
Ziel
Observability beantwortet drei Fragen:
- Ist das System erreichbar?
- Funktionieren fachliche Flows?
- Wo entsteht ein Fehler oder Engpass?
Metrikarten
| Metrikart | Beispiel | Zweck |
|---|---|---|
| technische Metrik | HTTP Latenz | Performance prüfen |
| fachliche Metrik | akzeptierte Bestellungen | Business Flow beobachten |
| Integrationsmetrik | Payment Fehler | Provider-Probleme sehen |
| Betriebsmetrik | Outbox Backlog | Event-Stau erkennen |
Health Checks
Actuator Health zeigt technische Bereitschaft. Ein Health Check darf nicht nur sagen, dass der Prozess läuft. Wichtig sind Abhängigkeiten:
- PostgreSQL erreichbar?
- Outbox Backlog im Rahmen?
- RabbitMQ erreichbar?
- Payment Provider erreichbar oder Circuit offen?
Correlation ID
Jeder Request soll mit einer Correlation ID nachvollziehbar sein:
Client Request
-> Runtime Log
-> Payment Call
-> Outbox Event
-> Consumer Log
Prometheus
Prometheus scrapt:
/actuator/prometheus
Wichtige PromQL-Ideen:
rate(http_server_requests_seconds_count[5m])
sum(order_outbox_backlog)
Grafana
Dashboards sollten nicht nur technische Kurven zeigen, sondern Fragen beantworten:
- Kommen Bestellungen rein?
- Werden Events publiziert?
- Gibt es Backlog?
- Steigt die Fehlerrate?
- Ist der Payment Provider langsam?
Alert-Regeln
Gute Alerts sind handlungsorientiert. Ein Alert soll ein Runbook verlinken und nicht nur "rot" sein.
| Alert | Bedeutung | Runbook |
|---|---|---|
| OutboxBacklogHigh | Relay/Broker Problem | Outbox/RabbitMQ Runbook |
| PaymentErrorRateHigh | Provider instabil | Resilience Runbook |
| RuntimeDown | Anwendung nicht erreichbar | Betriebs-Runbook |
| DbConnectionHigh | DB/Pool Problem | Datenbank-Runbook |
Logging-Grundregeln
- Keine Tokens loggen.
- Keine Passwörter loggen.
- Fachliche IDs erlaubt, personenbezogene Daten vermeiden.
- Correlation ID immer mitschreiben.
- Fehler mit Ursache und Kontext, aber ohne Secrets.
---
Ziel
Resilience schützt das System vor instabilen Abhängigkeiten. Im Projekt ist besonders der Payment Provider relevant.
Muster
| Muster | Zweck | Beispiel |
|---|---|---|
| Timeout | nicht endlos warten | Payment Provider langsam |
| Retry | temporäre Fehler abfangen | kurzer Netzwerkfehler |
| Circuit Breaker | kaputte Abhängigkeit entlasten | Provider down |
| Bulkhead | Ressourcen isolieren | Payment blockiert nicht alles |
| Rate Limiting | Überlast begrenzen | zu viele API Requests |
Reihenfolge
Eine sinnvolle Kette:
Rate Limit -> Bulkhead -> Timeout -> Retry -> Circuit Breaker -> Fallback/Error Response
Timeout
Timeout verhindert, dass Threads oder Verbindungen hängen bleiben. Ein Timeout muss kleiner sein als der Client-Timeout vor der Runtime.
Retry
Retry nur bei Fehlern verwenden, die wirklich transient sind. Nicht jeder Fehler darf wiederholt werden.
Nicht sinnvoll:
400 Bad Request -> Retry
Sinnvoll:
503 Service Unavailable -> Retry mit Backoff
Circuit Breaker
Der Circuit Breaker öffnet, wenn zu viele Fehler auftreten. Danach werden Calls kurzfristig abgelehnt, damit sich der Provider erholen kann.
Fehlerantworten
APIs sollen stabile Fehlerformate liefern:
{
"code": "PAYMENT_TEMPORARILY_UNAVAILABLE",
"message": "Payment Provider ist aktuell nicht erreichbar.",
"correlationId": "..."
}
Runbook bei Payment-Ausfall
- Grafana Payment Dashboard prüfen.
- Circuit Breaker Status prüfen.
- WireMock/Provider Health prüfen.
- Logs mit Correlation ID suchen.
- Outbox Backlog prüfen.
- Falls Provider down: Incident klassifizieren.
- Nach Wiederherstellung Retry/Backlog beobachten.
---
Ziel
PostgreSQL ist im Projekt nicht nur Datenspeicher, sondern auch Teil des Integrationsmusters durch die Outbox. Deshalb braucht die Datenbank eigene Betriebsdoku.
Schemata
| Schema | Zweck |
|---|---|
order_platform | fachliche Order-Daten |
outbox | Event-Zwischenspeicher |
Migrationen
Migrationen laufen über Flyway. Jede Änderung am Schema soll als neue Migration versioniert werden.
V1__create_order_and_outbox.sql
V2__outbox_relay_indexes.sql
Backup
Backup-Script:
./scripts/database/backup-postgres.sh
Grundregel:
- Backup regelmäßig testen.
- Restore regelmäßig üben.
- Backup ohne Restore-Test ist nur eine Hoffnung.
Restore
Restore-Script:
./scripts/database/restore-postgres.sh backup-file.sql
Outbox Retention
Outbox Events dürfen nicht unbegrenzt wachsen. Publizierte Events können nach einer definierten Zeit archiviert oder gelöscht werden.
Beispiel:
delete from outbox.events
where status = 'PUBLISHED'
and published_at < now() - interval '30 days';
Wichtige SQL Checks
select status, count(*)
from outbox.events
group by status;
select count(*)
from order_platform.orders;
select installed_rank, version, description, success
from flyway_schema_history
order by installed_rank desc;
Produktionshinweise
- Keine manuellen Schemaänderungen ohne Migration.
- Indizes für Outbox Status und Event ID prüfen.
- Connection Pool überwachen.
- Lange Transaktionen vermeiden.
- Backup-Verschlüsselung und Aufbewahrung definieren.
- Restore-Zeit messen und dokumentieren.
---
CI/CD-Ziel
Die Pipeline soll nicht nur bauen, sondern Qualität sichtbar erzwingen.
Quality Gates
| Gate | Zweck |
|---|---|
| Maven Verify | Kompilierung und Tests |
| Dependency Review | riskante Abhängigkeiten erkennen |
| SBOM | Softwarebestand dokumentieren |
| Lizenzprüfung | rechtliche Risiken sichtbar machen |
| Docker Build | Runtime Image baubar halten |
| Release Check | Paketstruktur prüfen |
Lokale Gates
./scripts/quality/run-quality-gates.sh
Docker Image
./scripts/deploy/build-runtime-image.sh
Kubernetes Deployment
kubectl apply -k deploy/kubernetes/overlays/local
OpenShift Deployment
oc apply -k deploy/kubernetes/overlays/openshift
Helm Deployment
helm upgrade --install order-runtime deploy/helm/order-runtime
Release-Regel
Ein Release ist erst dann fertig, wenn:
- Tests grün sind.
- SBOM erzeugt wurde.
- Lizenzbericht geprüft wurde.
- Docker Image gebaut wurde.
- Deployment Manifest gerendert wurde.
- Runbooks aktualisiert wurden.
- Version und Release Notes stimmen.
Typischer Pipeline-Ablauf
checkout
-> setup java
-> mvn verify
-> testcontainers/e2e optional
-> sbom
-> license report
-> docker build
-> manifest render
-> release artifact
---
Testziel
Tests sollen nicht nur Code Coverage erzeugen. Sie sollen Risiken absichern.
Testpyramide
Viele Domain Tests
-> Application Tests
-> Adapter Tests
-> Contract Tests
-> Security Tests
-> wenige E2E Tests
Welche Tests gehören wohin?
| Risiko | Testart |
|---|---|
| Fachregel falsch | Domain Test |
| Use Case ruft falschen Port | Application Test |
| SQL falsch | Adapter Test mit PostgreSQLContainer |
| Payment Vertrag falsch | WireMock Contract Test |
| Rolle falsch | Security Test |
| Systemfluss kaputt | E2E Test |
Testcontainers
Testcontainers ist für Infrastrukturtests gedacht. Nicht jeder Test sollte Container starten.
Gute Kandidaten:
- PostgreSQL Schema/Migration.
- RabbitMQ Verbindung und Routing.
- WireMock Providervertrag.
- E2E Happy Path.
Schlechte Kandidaten:
- einfache Value Object Regeln.
- reine Berechnung.
- Mapping ohne Infrastruktur.
E2E Testumfang
Ein guter E2E-Test prüft einen kritischen Pfad:
REST -> Security -> Use Case -> Payment -> DB -> Outbox -> RabbitMQ -> Consumer
Aber er soll nicht alle fachlichen Varianten abdecken. Sonst wird er langsam und fragil.
Testdaten
Testdaten müssen reproduzierbar sein. Keine versteckten lokalen Datenbanken. Keine Abhängigkeit von vorheriger Testreihenfolge.
Fehlerdiagnose
Wenn ein E2E-Test fehlschlägt:
- Container Logs prüfen.
- Ports und Health prüfen.
- Testdaten prüfen.
- WireMock Requests prüfen.
- DB Zustand prüfen.
- RabbitMQ Queue prüfen.
---
Fehlerklasse: REST 401
Symptom: API antwortet mit 401 Unauthorized.
Wahrscheinliche Ursachen:
- Kein Token gesendet.
- Token abgelaufen.
- Issuer stimmt nicht.
- Keycloak nicht erreichbar.
Runbook:
curl http://localhost:8082/realms/order-platform/.well-known/openid-configuration
./scripts/security/get-keycloak-token.sh order-admin admin
Fehlerklasse: REST 403
Symptom: Token gültig, Zugriff verboten.
Ursache: Rolle fehlt.
Runbook:
- JWT Claims prüfen.
realm_access.rolesprüfen.- Mapping zu
ROLE_*prüfen. - Security-Regel für Endpunkt prüfen.
Fehlerklasse: Outbox Backlog
Symptom: Viele Events bleiben NEW.
Ursachen:
- Relay läuft nicht.
- RabbitMQ down.
- Routing falsch.
- Publisher wirft Exception.
Runbook:
select status, count(*) from outbox.events group by status;
RabbitMQ Management prüfen:
http://localhost:15672
Fehlerklasse: Payment Timeout
Symptom: Bestellannahme langsam oder fehlerhaft.
Runbook:
- WireMock Mapping prüfen.
- Resilience Dashboard prüfen.
- Circuit Breaker Status prüfen.
- Correlation ID in Logs verfolgen.
Fehlerklasse: Migration fehlgeschlagen
Symptom: Runtime startet nicht, Flyway Fehler.
Runbook:
select * from flyway_schema_history order by installed_rank desc;
- Fehlgeschlagene Migration nicht manuell überschreiben.
- Ursache analysieren.
- Korrektur als neue Migration erstellen.
Fehlerklasse: Prometheus Target down
Symptom: Grafana zeigt keine Daten.
Runbook:
curl http://localhost:8080/actuator/prometheus
curl http://localhost:9090/targets
Fehlerklasse: Docker Compose startet nicht
Symptom: Container bleiben unhealthy oder starten nicht.
Runbook:
docker compose ps
docker compose logs postgres
docker compose logs keycloak
docker compose logs rabbitmq
Incident-Klassifikation
| Klasse | Beispiel | Reaktion |
|---|---|---|
| P1 | Bestellungen können nicht angenommen werden | sofortiger Incident |
| P2 | Billing Consumer hängt, Outbox wächst | priorisiert behandeln |
| P3 | Dashboard fehlerhaft, System läuft | normaler Fix |
| P4 | Doku/Minor Issue | Backlog |
---
Aggregate Root
Zentrale fachliche Einheit, die Invarianten schützt. Im Projekt ist Order ein Aggregate Root.
Application Service
Koordiniert einen Use Case. Er enthält Ablaufsteuerung, aber keine technischen Details.
Adapter
Technische Implementierung eines Ports. Beispiele: PostgreSQL Repository, RabbitMQ Publisher, WireMock Gateway.
Port
Fachlicher Vertrag, den die Application benötigt. Der Port kennt keine konkrete Infrastruktur.
Transactional Outbox
Muster, bei dem Events zuerst in derselben Datenbanktransaktion gespeichert und später veröffentlicht werden.
Relay
Job oder Prozess, der Outbox Events liest und an einen Message Broker publiziert.
RabbitMQ Exchange
Verteiler für Nachrichten. Im Projekt: order.events.
Routing Key
Schlüssel, mit dem RabbitMQ entscheidet, welche Queue eine Nachricht erhält.
DLQ
Dead Letter Queue. Dort landen Nachrichten, die nicht verarbeitet werden konnten.
OAuth2 Resource Server
Anwendung, die Bearer Tokens validiert und Zugriffe schützt.
JWT
JSON Web Token. Enthält Claims wie Subject, Rollen und Ablaufzeit.
Keycloak Realm
Isolierter Sicherheitsbereich mit Clients, Rollen und Usern.
Prometheus
Zeitreihen-Datenbank und Scraper für Metriken.
Grafana
Dashboard-System zur Visualisierung von Metriken.
Circuit Breaker
Resilience-Muster, das bei vielen Fehlern weitere Aufrufe temporär verhindert.
Bulkhead
Resilience-Muster zur Isolation von Ressourcen.
SBOM
Software Bill of Materials. Liste verwendeter Software-Komponenten.
ADR
Architecture Decision Record. Dokumentiert wichtige Architekturentscheidungen.
---
Root
| Datei/Ordner | Bedeutung |
|---|---|
index.html | Hauptstartseite |
OPEN_THIS_V2_REAL_SYSTEMS.html | alternativer Startpunkt |
README.md | Projektüberblick |
START-HIER.md | schnelle Anleitung |
docker-compose.yml | lokaler Container-Stack |
.env.example | Beispielumgebung |
Dockerfile.runtime | Runtime Image Build |
Maven Projekt
| Pfad | Bedeutung |
|---|---|
maven-project/domain | Fachmodell |
maven-project/ports | Port-Schnittstellen |
maven-project/application | Use Cases |
maven-project/adapters | technische Adapter |
maven-project/runtime | Spring Boot Runtime |
maven-project/integration-tests | Testcontainers und E2E Tests |
Container
| Pfad | Bedeutung |
|---|---|
containers/postgres | Init SQL |
containers/rabbitmq | Exchange, Queue, DLQ Definitionen |
containers/keycloak | Realm Import |
containers/wiremock | Mock Provider Mappings |
containers/prometheus | Prometheus Konfiguration und Alerts |
containers/grafana | Dashboard Provisioning |
Dokumentation
| Pfad | Bedeutung |
|---|---|
docs/architecture | Architekturdetails |
docs/runbooks | konkrete Betriebsanleitungen |
docs/adr | Architekturentscheidungen |
docs/security | Security Modell |
docs/testing | Teststrategie |
dokumentation | finales ausgiebiges Handbuch |
lehrbuch | Lernbuch Markdown/HTML/PDF |
berichte | Prüfberichte und Release Notes |
prompts | verwendbare Weiterarbeit-Prompts |
Deployment
| Pfad | Bedeutung |
|---|---|
deploy/kubernetes | Kubernetes Manifeste |
deploy/helm | Helm Chart |
scripts/deploy | Deployment-Hilfsskripte |
Betriebsskripte
| Pfad | Bedeutung |
|---|---|
scripts/security | Token und Security Flow |
scripts/tests | Test/E2E Checks |
scripts/database | Backup/Restore/Checks |
scripts/observability | Metrikchecks |
scripts/resilience | Fehler- und Timeout-Szenarien |
scripts/quality | Quality Gates |
---
Ziel
Dieser Lernplan hilft, das Projekt nicht nur zu öffnen, sondern systematisch zu verstehen.
Woche 1: Struktur verstehen
- Root
index.htmlöffnen. README.mdlesen.- Maven Module ansehen.
domain,ports,application,adapters,runtimevergleichen.
Übung:
cd maven-project
mvn -q -DskipTests package
Woche 2: Lokale Systemlandschaft starten
- Docker Compose starten.
- Adminer, RabbitMQ, Keycloak, Grafana öffnen.
- WireMock Mapping ansehen.
Übung:
docker compose up -d
docker compose ps
Woche 3: REST und Security
- Token holen.
- geschützten Flow testen.
order-adminundorder-readervergleichen.
Übung:
./scripts/security/call-secured-order-flow.sh
Woche 4: Outbox und Messaging
- Outbox Tabelle ansehen.
- Relay Job verstehen.
- RabbitMQ Queue prüfen.
Übung:
select status, count(*) from outbox.events group by status;
Woche 5: Observability und Resilience
- Actuator Prometheus Endpoint öffnen.
- Grafana Dashboard ansehen.
- Timeout/Fehlerfall über WireMock simulieren.
Woche 6: Tests und CI/CD
- Testcontainers Struktur verstehen.
- E2E Tests ausführen.
- GitHub Actions Pipeline lesen.
Übung:
./scripts/tests/run-phase4-e2e.sh
./scripts/quality/run-quality-gates.sh
Woche 7: Deployment und Betrieb
- Dockerfile verstehen.
- Kubernetes/Kustomize lesen.
- Helm Chart lesen.
- Backup/Restore Runbook durcharbeiten.
Abschlussaufgabe
Ergänze einen Fraud Provider:
FraudGatewayPort definieren.- WireMock Fraud Mapping erstellen.
- Application Service erweitern.
- Resilience für Fraud Call ergänzen.
- Metrik
fraud_checks_totalergänzen. - Testcontainers/Contract Test ergänzen.
- Doku und Runbook aktualisieren.