--- type: ADR id: ADR-0015 status: ACTIVE owner: Lead Architect last_update: 2026-03-24 --- # ADR-0015: Context Map & Integration Patterns ## Status Akzeptiert ## Kontext Nach der Definition der 6 Bounded Contexts (→ ADR-0014) müssen die **Beziehungen zwischen den Contexts** explizit dokumentiert werden. Eine Context Map beschreibt: - Welche Contexts miteinander kommunizieren - In welche Richtung Abhängigkeiten fließen - Welches Integration Pattern verwendet wird - Wo Anti-Corruption Layers (ACL) notwendig sind Ohne eine explizite Context Map entstehen implizite Abhängigkeiten, die die Unabhängigkeit der Contexts untergraben und die Offline-First-Strategie gefährden. ## Entscheidung ### Context Map (ASCII-Diagramm) ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ EXTERNE SYSTEME │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ ZNS (Zentrales Nennungs-System / OEPS) [Upstream / Big Ball] │ │ │ └──────────────────────────┬──────────────────────────────────────────┘ │ │ │ ACL (A-Satz / B-Satz Import) │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │ │ Keycloak (Identity Provider) [Upstream / Conformist] │ │ │ └──────────────────────────┬───────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ JWT / OIDC ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ INTERNE CONTEXTS │ │ │ │ ┌──────────────────────┐ ┌──────────────────────────────────────┐ │ │ │ identity-context │◄───────│ (alle Contexts) │ │ │ │ [Generic Domain] │ OHS │ prüfen Berechtigungen via Token │ │ │ └──────────────────────┘ └──────────────────────────────────────┘ │ │ │ │ ┌──────────────────────┐ ACL ┌──────────────────────────────────────┐ │ │ │ actor-context │◄───────│ ZNS (extern) │ │ │ │ [Supporting Domain] │ └──────────────────────────────────────┘ │ │ │ │ │ │ │ DomReiter │ CL/SK ┌──────────────────────────────────────┐ │ │ │ DomPferd │───────►│ registration-context │ │ │ │ DomFunktionär │ │ [Core Domain] │ │ │ │ DomVerein │ │ │ │ │ └──────────────────────┘ │ DomNennung │ │ │ │ DomNennungsTransfer │ │ │ ┌──────────────────────┐ CL/SK │ DomAbteilung │ │ │ │ event-management- │───────►│ │ │ │ │ context │ └──────────────┬───────────────────────┘ │ │ │ [Supporting Domain] │ │ │ │ │ │ │ Domain Events │ │ │ DomVeranstaltung │ │ (NennungErstellt, │ │ │ DomTurnier │ │ NennungStorniert, │ │ │ DomAusschreibung │ │ NennungTransferiert) │ │ └──────────────────────┘ │ │ │ ▼ │ │ ┌──────────────────────┐ ┌──────────────────────────────────────┐ │ │ │ billing-context │◄───────│ competition-context │ │ │ │ [Generic Domain] │ ACL │ [Supporting Domain] │ │ │ │ │ │ │ │ │ │ DomKonto │ │ DomBewerb │ │ │ │ DomGebühr │ │ DomAbteilung │ │ │ │ DomAbrechnung │ │ DomStartliste │ │ │ └──────────────────────┘ │ DomErgebnis │ │ │ └──────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │ │ series-context [Phase 2+ — Architektur vorbereitet, nicht aktiv] │ │ │ └──────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` **Legende:** - `ACL` = Anti-Corruption Layer - `CL/SK` = Customer/Supplier mit Shared Kernel (gemeinsame IDs) - `OHS` = Open Host Service (standardisiertes Interface) - `►` = Abhängigkeitsrichtung (Downstream → Upstream) --- ### Beziehungen im Detail #### 1. ZNS (extern) → `actor-context` **Pattern:** Upstream/Downstream mit **Anti-Corruption Layer (ACL)** | Eigenschaft | Beschreibung | |-------------------|--------------------------------------------------------------------------------------------------------------------------------------------| | Richtung | ZNS ist Upstream (Datenquelle), `actor-context` ist Downstream | | Protokoll | Datei-Import (A-Satz / B-Satz, proprietäres Format) | | ACL-Aufgabe | Übersetzung von ZNS-Datenformaten in interne Domänenmodelle | | Offline-Verhalten | ZNS-Daten werden lokal gecacht; Import läuft asynchron | | Kritische Regel | `Satznummer` ist primärer Schlüssel; `Lebensnummer` und `Kopfnummer` sind **nicht** als DB-Schlüssel geeignet (ZNS-Inkonsistenzen bekannt) | **ACL-Verantwortlichkeiten:** - Normalisierung inkonsistenter Felder (z.B. Farbe `"Braun"` vs. `"Brauner"`) - Generierung interner IDs für ausländische Pferde ohne UELN - Validierung und Ablehnung korrupter ZNS-Datensätze mit Protokollierung --- #### 2. `actor-context` → `registration-context` **Pattern:** Customer/Supplier mit **Shared Kernel (gemeinsame IDs)** | Eigenschaft | Beschreibung | |-------------------|--------------------------------------------------------------------------------| | Richtung | `actor-context` ist Upstream (Stammdaten-Lieferant) | | Shared Kernel | `ReiterId` (Satznummer), `PferdId` (Satznummer) als gemeinsame Referenz-IDs | | Kommunikation | Synchron: Lookup bei Nennungs-Erstellung; Asynchron: Sperrlisten-Updates | | Offline-Verhalten | `registration-context` hält lokale Kopie der benötigten Akteur-Daten | | Kritische Regel | `registration-context` darf Akteur-Daten **nicht** direkt mutieren (nur lesen) | --- #### 3. `event-management-context` → `registration-context` **Pattern:** Customer/Supplier mit **Shared Kernel (gemeinsame IDs)** | Eigenschaft | Beschreibung | |-----------------|---------------------------------------------------------------------| | Richtung | `event-management-context` ist Upstream (Turnier-/Bewerbs-Struktur) | | Shared Kernel | `TurnierId`, `BewerbId` als gemeinsame Referenz-IDs | | Kommunikation | Synchron: Bewerbs-Lookup bei Nennungs-Erstellung | | Kritische Regel | Nennungen können nur für existierende Bewerbe erstellt werden | --- #### 4. `registration-context` → `competition-context` **Pattern:** Upstream/Downstream via **Domain Events** | Eigenschaft | Beschreibung | |--------------------|-----------------------------------------------------------------------------| | Richtung | `registration-context` ist Upstream (Ereignis-Quelle) | | Events | `NennungErstelltEvent`, `NennungStorniertEvent`, `NennungTransferiertEvent` | | Kommunikation | Asynchron (Event Bus / lokale Event Queue) | | Aufgabe Downstream | `competition-context` baut Startlisten aus Nennungs-Events auf | | Offline-Verhalten | Events werden lokal persistiert und bei Verbindung synchronisiert | --- #### 5. `registration-context` → `billing-context` **Pattern:** Upstream/Downstream via **Domain Events** mit ACL | Eigenschaft | Beschreibung | |-----------------|-----------------------------------------------------------------------------------------------------------| | Richtung | `registration-context` ist Upstream (Gebühren-Auslöser) | | Events | `NennungErstelltEvent` (löst Nenngeld aus), `NennungStorniertEvent` (Gutschrift), `GebührenVerzichtEvent` | | ACL-Aufgabe | Übersetzung von Nennungs-Events in Gebühren-Buchungen | | Kritische Regel | Sportförderbeitrag und Tierwohl-Euro fallen **pro Start** an (nicht pro Nennung) | --- #### 6. `competition-context` → `billing-context` **Pattern:** Upstream/Downstream via **Domain Events** mit ACL | Eigenschaft | Beschreibung | |-------------|------------------------------------------------------------------| | Richtung | `competition-context` ist Upstream | | Events | `StartErfolgreich` (löst Sportförderbeitrag + Tierwohl-Euro aus) | | ACL-Aufgabe | Übersetzung von Start-Events in Gebühren-Buchungen | --- #### 7. Keycloak → alle Contexts **Pattern:** Upstream/Downstream, **Conformist** (alle Contexts passen sich Keycloak an) | Eigenschaft | Beschreibung | |-------------------|-----------------------------------------------------------------------------------| | Richtung | Keycloak ist Upstream (Identity Provider) | | Protokoll | OIDC / JWT-Token | | Kommunikation | Synchron: Token-Validierung bei jedem Request | | Offline-Verhalten | Token-Caching mit konfigurierbarer TTL; Offline-Modus mit eingeschränkten Rechten | --- ### Anti-Corruption Layer (ACL) — Implementierungsrichtlinien Jeder ACL wird als **eigenständiges Modul** innerhalb des Downstream-Contexts implementiert: ``` actor-context/ └── infrastructure/ └── zns/ ├── ZnsImportService.kt # Orchestrierung ├── ZnsAkteurMapper.kt # Übersetzung ZNS → Dom* ├── ZnsValidationFilter.kt # Ablehnung korrupter Daten └── ZnsImportProtokoll.kt # Audit-Log aller Imports ``` **Prinzipien:** 1. Der ACL übersetzt **immer** in die interne Ubiquitous Language — niemals umgekehrt 2. Fehlerhafte externe Daten werden **protokolliert und übersprungen** (kein Systemabsturz) 3. Der ACL ist der einzige Ort, der das externe Datenformat kennt --- ### Offline-First Integration Da die Anwendung als Desktop-App (Offline-First) betrieben wird, gelten folgende Regeln: | Szenario | Verhalten | |-----------------------------|------------------------------------------------------------| | ZNS nicht erreichbar | Lokaler Cache wird verwendet; Import-Status wird angezeigt | | Nennungs-Erstellung offline | Lokal gespeichert; Events werden bei Sync übertragen | | Keycloak nicht erreichbar | Gecachter Token wird verwendet (TTL-basiert) | | Konflikt bei Sync | Optimistic Locking (409) + manuelle Auflösung (→ ADR-0013) | --- ## Konsequenzen ### Positive - **Explizite Abhängigkeiten:** Alle Context-Beziehungen sind dokumentiert und nachvollziehbar - **Schutz der Kern-Domäne:** `registration-context` ist durch ACLs von externen Systemen isoliert - **Offline-Fähigkeit:** Jede Integration ist auf Offline-Betrieb ausgelegt - **Erweiterbarkeit:** `series-context` kann in Phase 2+ als reiner Downstream-Consumer hinzugefügt werden ### Negative - **Komplexität:** ACLs und Event-Übersetzungen erhöhen den initialen Implementierungsaufwand - **Eventual Consistency:** Zwischen `registration-context` und `competition-context` gibt es keine sofortige Konsistenz ### Neutral - Die Context Map ist ein **lebendes Dokument** und wird mit jeder neuen Integration aktualisiert ## Betrachtete Alternativen ### Direkte Context-zu-Context-Aufrufe (abgelehnt) Direkte synchrone Aufrufe zwischen Contexts würden enge Kopplung erzeugen und die Offline-Fähigkeit untergraben. ### Shared Database (abgelehnt) Eine gemeinsame Datenbank für alle Contexts würde die Context-Grenzen aufweichen und die unabhängige Entwicklung verhindern. ## Referenzen - [ADR-0014: Bounded Context Mapping](0014-bounded-context-mapping-de.md) - [ADR-0004: Event-Driven Communication](0004-event-driven-communication-de.md) - [ADR-0006: Authentication & Authorization (Keycloak)](0006-authentication-authorization-keycloak-de.md) - [ADR-0013: Tech Stack Stabilization](0013-tech-stack-stabilization-2026.md) - [Ubiquitous Language](../../03_Domain/01_Glossary/Ubiquitous_Language.md) - [MASTER_ROADMAP](../MASTER_ROADMAP.md) - Vaughn Vernon: „Implementing Domain-Driven Design", Kapitel 3 (Context Maps) - ÖTO 2026, ZNS-Schnittstellen-Spezifikation (A-Satz / B-Satz)