Some checks failed
Build and Publish Docker Images / build-and-push (., backend/infrastructure/gateway/Dockerfile, api-gateway, api-gateway) (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., backend/services/ping/Dockerfile, ping-service, ping-service) (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., config/docker/caddy/web-app/Dockerfile, web-app, web-app) (push) Has been cancelled
Build and Publish Docker Images / build-and-push (., config/docker/keycloak/Dockerfile, keycloak, keycloak) (push) Has been cancelled
6.0 KiB
6.0 KiB
| type | status | owner | last_update | sources | |||
|---|---|---|---|---|---|---|---|
| Architecture Reference | ACTIVE | 🧹 Curator & 🏗️ Lead Architect | 2026-04-02 |
|
Tenant‑Konzept — Eine Veranstaltung = eine Datenbank (ein Tenant)
Dieses Dokument erklärt in einfacher Sprache, wie wir Daten pro Veranstaltung trennen. Grundlage ist ADR‑0021 (Schema‑per‑Tenant). Kurz gesagt: Für jede Veranstaltung gibt es eine eigene „Datenbank‑Schublade“. Nichts aus Veranstaltung A landet versehentlich in Veranstaltung B.
Weitere Details in der technischen Entscheidung:
- ADR‑0021:
docs/01_Architecture/adr/0021-tenant-resolution-strategy-de.md
1) Idee in Alltagssprache
- Stell dir jede Veranstaltung wie einen eigenen Ordner vor. In diesem Ordner liegen alle Tabellen und Daten nur für genau diese Veranstaltung.
- Öffnest du eine andere Veranstaltung, arbeitest du automatisch in einem anderen Ordner. Es gibt keine Vermischung.
- Dadurch sind Archivierung, Backup, Wiederherstellung oder Löschen pro Veranstaltung einfach und sicher.
Technisch heißt das: „Schema‑per‑Tenant“. Ein „Schema“ ist wie ein separater Ordner in derselben Datenbank. Optional können sehr große/sensible Veranstaltungen sogar eine ganz eigene physische Datenbank bekommen.
2) Was bedeutet das fürs Datenbank‑Schema?
- Pro Veranstaltung existiert ein eigenes Schema mit denselben Tabellen (z. B.
veranstaltungen,turniere,bewerbe,abteilungen, …). - Es gibt KEINE
tenant_id‑Spalte in jeder Tabelle. Die Trennung passiert über das eigene Schema. - Eine zentrale Registry im
control‑Schema verwaltet alle Veranstaltungen/Tenants, z. B. Tabellecontrol.tenants(event_id, schema_name, db_url, status, version, created_at). - Migrationen (Flyway) laufen je Schema. Jedes Schema hat eine eigene
flyway_schema_history.
Vorteile:
- Geringeres Risiko von Datenleckagen.
- Leichtere, DSGVO‑konforme Löschung: Ein Schema lässt sich vollständig entfernen/archivieren.
- Unabhängige Migration und Versionierung je Veranstaltung möglich.
3) Auswirkungen auf die API (Backend)
- Jeder Request muss wissen, „in welchem Veranstaltungs‑Ordner“ er arbeiten soll.
- Primär über den HTTP‑Header
X-Event-Id: <event_slug>(kanonisch). Alternativ kann die Subdomain/Host dies ausdrücken, z. B.<event>.meldestelle.local. - Das Backend prüft
X-Event-Idgegen die Registry (control.tenants). Nur aktive Veranstaltungen sind beschreibbar; archivierte sind schreibgeschützt. - Autorisierung: Tokens enthalten erlaubte
events(Scopes). Der Zugriff auf eine Veranstaltung wird gegen diese Liste geprüft. - Admin/Synchronisations‑Endpunkte dürfen ausnahmsweise einen expliziten
eventId‑Pfadparameter verwenden (Fallback).
Fehlerbilder (vereinheitlicht):
- Unbekannte Veranstaltung →
404 Unknown event. - Veranstaltung gesperrt/archiviert →
423 Locked(oder 403 je Endpoint‑Policy).
Praktische Hinweise für API‑Clients:
- Immer
X-Event-Idmitsenden, sobald es fachlich um eine konkrete Veranstaltung geht. - Admin‑Operation „Veranstaltung anlegen“ erzeugt Schema + Registry‑Eintrag. Erst danach sind Fach‑Endpunkte nutzbar.
4) Auswirkungen auf das Frontend (Navigation V3, Offline‑First)
- V3 Routen führen den Kontext über IDs (z. B.
eventId,tournamentId, …). Diese IDs bestimmen implizit den aktiven Tenant. - Beim Öffnen eines Deep‑Links inkl.
eventIdsetzt der Client denX-Event-Id‑Header automatisch für Backend‑Aufrufe. - Wechsel der Veranstaltung im UI entspricht einem Tenant‑Wechsel. Daraus folgen klare Regeln:
- Der aktuelle Navigations‑Stack (V3) wird auf die Root der neu gewählten Veranstaltung zurückgesetzt (kein Cross‑Event‑State).
- Datenansichten, Caches und ViewModel‑States sind pro Veranstaltung getrennt zu halten.
- Kassen‑Sichten: „Veranstaltungs‑Kassa“ aggregiert nur über Turniere derselben Veranstaltung (nie übergreifend).
Offline‑First:
- Lokal wird je Veranstaltung eine eigene Datenbasis geführt (analog zur Backend‑Trennung). Ein Sync arbeitet immer bezogen auf den aktuellen Event‑Kontext.
UX‑Hinweise:
- In Breadcrumbs/TopBar ist die aktive Veranstaltung sichtbar und schnell wechselbar.
- Bei ungültigem Kontext (z. B.
eventIdexistiert nicht mehr) zeigt die App einen Hinweis und bietet einen Rücksprung zur Veranstaltungs‑Auswahl an.
5) Entwickler‑Leitfaden (kurz)
-
Backend
- In Gateways/Clients stets
X-Event-Idsetzen, sobald ein Event‑Kontext vorhanden ist. - Keine gemeinsamen Queries über mehrere Veranstaltungen im selben Request.
- Flyway‑Migrationsskripte tenant‑sicher halten (keine absoluten Schema‑Namen in DDL, sofern sie dynamisch sein müssen).
- In Gateways/Clients stets
-
Frontend
- Routen enthalten die relevanten IDs; keine globalen, veranstaltungsübergreifenden Stores für Event‑Daten.
- Beim Event‑Wechsel alle abhängigen ViewModels/Stores invalidieren bzw. neu initialisieren.
- Deep‑Links enthalten
eventId; beim Öffnen wird der Navigationspfad synthetisch aufgebaut (siehe V3‑Dokument).
6) Grenzen & Trade‑offs
- Cross‑Event‑Suche/Auswertung erfordert separate Aggregation (bewusste Entscheidung zugunsten Offline‑First und Sicherheit).
- Datenmigration (z. B. Zusammenlegen/Teilen von Veranstaltungen) braucht Tools/Assistenten.
7) Beziehung zur Domäne
- Ubiquitous Language:
Veranstaltungist die Root. Kassen und TeilnehmerKonten sind auf Event‑Ebene verankert. - Zahlvorgänge können mehrere Rechnungen/Belege aus verschiedenen Turnieren derselben Veranstaltung ausgleichen.
8) Querverweise
- Technische Details/Begründung:
docs/01_Architecture/adr/0021-tenant-resolution-strategy-de.md - Navigation V3 Regeln:
docs/06_Frontend/Navigation_V3_Screen-Baum_und_Back-Stack.md - Event‑First‑Workflow:
docs/02_Guides/Event-First-Workflow.md