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>
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
---
|
||||
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)
|
||||
Reference in New Issue
Block a user