meldestelle/docs/01_Architecture/adr/0015-context-map-de.md
Stefan Mogeritsch 354bd49de6 feat: integrate new desktop shell and extend backend & ADRs
- Added `meldestelle-desktop` module using JVM/Compose Desktop, registered in `settings.gradle.kts`.
- Integrated new screens and desktop navigation into core: `Veranstaltungen`, `TurnierDetail`, etc.
- Expanded backend with `ExposedFunktionaerRepository` in `officials-infrastructure`.
- Completed ADRs for bounded context mapping (`ADR-0014`) and context map (`ADR-0015`).
- Updated and extended project documentation with session logs and architecture decisions.

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
2026-03-24 18:22:15 +01:00

276 lines
17 KiB
Markdown

---
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)