- Generic org/team/member data model (renamed from club-specific naming) - Auth, organizations, invitations with mail + Matrix provisioning - Teams with automatic Matrix room creation, sync-job retry, reconciliation - Departments, seasons, roles (RBAC with privilege-escalation guard), audit log - Dashboard, members list, org switcher on the frontend - Synapse setup docs (Docker + bare-metal) and generalized PRD/architecture docs Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TBf3yfnnHzMQ5qhLV2285A
8.3 KiB
Architektur: TalkTime
Voraussetzung: Starter-Kit noch nicht lokal analysiert. Diese Architektur ist die Zielarchitektur gemäß Master-Prompt; Abgleich mit tatsächlichem Starter-Kit-Code steht aus.
1. Grundarchitektur
┌─────────────────────────┐
│ TalkTime │
│ Web Frontend │
│ React + TypeScript │
└────────────┬─────────────┘
│ HTTPS
▼
┌─────────────────────────┐
│ TalkTime API │
│ Fastify + TypeScript │
│ Auth / Vereine / │
│ Mitglieder / Mannsch. │
│ Rollen / Gruppen / │
│ Invitations / Synapse │
└──────┬───────────┬───────┘
▼ ▼
PostgreSQL Synapse
│
▼
Matrix
Prinzip: PostgreSQL hält Vereins-/Verwaltungsdaten und die Zuordnung Vereinsobjekt ↔ Matrix-Objekt. Nachrichteninhalte liegen ausschließlich in Synapse.
2. Kernabstraktion
Verein → Abteilung → Mannschaft → Mitglied → Kommunikationsgruppe → Matrix Room
Diese Kette zieht sich durch Datenmodell, API, Frontend und Sync-Logik.
3. SynapseService (Adapter-Schicht)
Browser darf niemals direkt mit Synapse Admin API sprechen:
Browser → TalkTime Backend → Synapse Admin API
SynapseService kapselt:
- Benutzer anlegen/suchen/aktivieren/deaktivieren/abrufen
- Räume erstellen/verwalten
- Mitglieder zu Räumen hinzufügen/entfernen
- Health Check
Admin-Token verlässt das Backend nie (nicht in Frontend, Logs, API-Responses, Git).
4. Matrix User Provisioning
Vereinsmitglied → TalkTime Account → Matrix Account
Matrix-ID-Schema: @vorname.nachname:chat.vereinschat.de (Domain pro Deployment konfigurierbar).
Regeln:
- Namenskonflikt: Suffix mit fortlaufender Nummer (
@max.mustermann2:...), Zuordnung bleibt stabil über Lebenszeit des Mitglieds. - Matrix-ID wird nach Erstellung nicht mehr geändert, auch nicht bei Namensänderung (Anzeige-Name in Matrix wird aktualisiert, ID bleibt).
- E-Mail-Änderung: aktualisiert Matrix-Account-Metadaten, nicht die ID.
- Deaktivierung/Reaktivierung: über Synapse Admin API (
deactivate/reactivate), Datensatz insynapse_usersbleibt für Referenzintegrität. - Löschung: Deaktivierung + DSGVO-konforme Datenminimierung, keine harte Matrix-Account-Löschung (Synapse unterstützt das ohnehin nur eingeschränkt), stattdessen Anonymisierung der Metadaten.
5. Chatgruppen / Räume
Automatische Raumerstellung: 1 Mannschaft → 1 Matrix-Raum (Standard). Zusätzlich Abteilungs- und Vereinsräume optional/automatisch je nach Konfiguration.
Mannschaft erstellt → Matrix Raum erstellen → Trainer hinzufügen → Mitglieder hinzufügen
Entfernen:
Mitglied aus Mannschaft entfernt → TalkTime aktualisieren → Matrix Membership aktualisieren (async Job)
6. Synchronisierung
Asynchrone Sync-Jobs statt synchroner Matrix-Calls im Request-Pfad, um UI nicht zu blockieren und Teilausfälle zu isolieren.
- Outbox Pattern: Jede Vereinsdaten-Änderung, die Matrix-Seiteneffekte hat, erzeugt einen Eintrag in
sync_jobs(Typ, Payload, Status) in derselben DB-Transaktion wie die fachliche Änderung. - Worker verarbeitet
sync_jobsidempotent, mit Retry (exponential backoff) und Statusfeldern (pending,processing,failed,done). - Reconciliation: periodischer Abgleich TalkTime-Soll-Zustand vs. Synapse-Ist-Zustand (Health-Check-Job), meldet Abweichungen ins Audit Log, erlaubt manuellen Resync über Admin-UI.
- Fachliche Operation (z. B. Mannschaft erstellen) schlägt NICHT komplett fehl, wenn Matrix-Call fehlschlägt — Datensatz wird angelegt, Sync-Job bleibt
pending/failedund wird retried bzw. manuell resynct.
7. RBAC
Rollenhierarchie im Code (generisch, siehe PRD Abschnitt 0 für die Sportverein/Firma/NGO-Begriffszuordnung): SYSTEM_ADMIN > ORG_ADMIN > ORG_MANAGER > DEPARTMENT_ADMIN > TEAM_MANAGER > TEAM_LEAD/ASSISTANT_LEAD/SUPERVISOR > MEMBER/GUARDIAN.
Prinzipien:
- Rollenvergabe nur durch Rolle mit strikt höherem Rang im selben oder übergeordneten Scope.
- Ein Nutzer kann sich selbst keine höherwertige Rolle zuweisen (serverseitige Prüfung, nicht nur UI).
- Rollen sind scope-gebunden (Verein/Abteilung/Mannschaft) — Prüfung immer
hasRole(user, role, scopeId), nie global außer SYSTEM_ADMIN.
8. Multi-Tenant
Jede Tabelle mit Organisationsbezug trägt organization_id (im Code weiterhin organizationId, Tabellenname organizations — generisch statt clubs, siehe PRD Abschnitt 0). Jede Query wird durch eine zentrale Query-Scoping-Schicht (z. B. Repository-Methode erzwingt WHERE organization_id = :currentOrganizationId) gefiltert — nie durch reine Anwendungslogik im Controller. API-Guards prüfen organization_id aus Session/Token gegen angefragte Ressource (IDOR-Schutz). SYSTEM_ADMIN-Zugriff über explizite, auditierte Ausnahme-Rolle, nicht Standardpfad.
9. Chat-Client-Strategie
Empfehlung (vorläufig, bis UX-Validierung): eigene, schlanke Chat-Oberfläche im TalkTime-Frontend über Matrix Client-Server API (z. B. matrix-js-sdk), kein vollständiger Matrix-Client eingebettet.
Begründung:
- Eingebetteter Client (z. B. Element Web via iframe) ist schnell integriert, aber UX wirkt "fremd" und schwer an Vereinsstruktur/Terminologie anzupassen.
- Eigene Oberfläche mit SDK erlaubt, nur Verein-relevante Räume zu zeigen, Vereins-Terminologie zu verwenden, und Matrix komplett zu verstecken (siehe Produktvision).
- Nachteil: höherer Aufwand, E2EE/Threads/Reaktionen müssen selbst integriert werden — für MVP bewusst nur Basis-Messaging + Bilder/Dateien, Threads/Reaktionen als V1/V2.
- Alternative "Kombination" (Reactions/Threads via SDK, Media via Matrix Content Repo direkt) für V1 vorgesehen.
Detaillierte Abwägung siehe ADR-007.
10. ADRs
ADR-001 — Matrix als Messaging-Backend Begründung: föderationsfähig, offener Standard, self-hostbar, aktive Entwicklung, E2EE-Unterstützung vorhanden. Alternative (eigenes Chat-Backend) verworfen wegen Aufwand für E2EE/Föderation/Mobile-Clients.
ADR-002 — Synapse Admin API nur Backend-seitig Begründung: Admin-Token darf nie ins Frontend. Alternative (Client ruft Synapse direkt mit User-Token) verworfen, da Vereinslogik (RBAC, Tenant-Scoping) dann nicht zentral durchsetzbar wäre.
ADR-003 — PostgreSQL für Vereinsdaten Begründung: passt zu Starter-Kit (Drizzle ORM), relationales Modell passt zur hierarchischen Vereinsstruktur. Keine Duplikation von Matrix-Nachrichteninhalten in Postgres.
ADR-004 — Multi-Tenant-Architektur (Shared DB, organization_id-Spalte)
Begründung: einfacher zu betreiben als DB-pro-Tenant bei erwarteter Vereinsgröße (Hunderte, nicht Zehntausende Mitglieder pro Verein). Alternative (Schema-pro-Tenant) verworfen wegen Migrationsaufwand bei vielen Vereinen.
ADR-005 — Matrix-IDs werden persistent gespeichert und nicht geändert Begründung: Matrix-ID-Änderung ist in Matrix nicht nativ vorgesehen (faktisch neuer Account) und würde Raumhistorie/Mitgliedschaften zerreißen.
ADR-006 — Synchronisierung über asynchrone Jobs (Outbox Pattern) Begründung: entkoppelt Vereinsverwaltung von Synapse-Verfügbarkeit, verhindert inkonsistente Teiltransaktionen zwischen zwei Systemen ohne verteilte Transaktion.
ADR-007 — Chat-Client: eigene Oberfläche mit Matrix Client-Server API Begründung: siehe Abschnitt 9. Re-evaluieren nach MVP-Nutzerfeedback, insbesondere E2EE-Aufwand.
11. Offene Punkte
- Exakte Synapse-Version und API-Kompatibilität noch zu klären (Referenz: aktuelle offizielle Synapse-/Matrix-Doku zum Zeitpunkt der Implementierung).
- Starter-Kit-Auth-Mechanismus (Session vs. JWT) noch unbekannt — beeinflusst Abschnitt "Authentifizierung" in PRD.