- 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>
276 lines
17 KiB
Markdown
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)
|