From 7043addd15a03bb955345d66a1c1a80f6a525d4a Mon Sep 17 00:00:00 2001 From: Lars Date: Thu, 14 May 2026 06:42:13 +0200 Subject: [PATCH] feat(docs): update architecture documentation references and enhance handover details - Added references to the architecture target image, refactor roadmap, and binding Shinkan rules in CLAUDE.md and HANDOVER.md for better project clarity. - Updated the Dashboard component to improve user authentication handling and optimize data loading, enhancing overall performance and user experience. Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 1 + docs/HANDOVER.md | 1 + docs/architecture/README.md | 21 +++ docs/architecture/SCHULDEN_UND_REMEDIATION.md | 131 +++++++++++++++++ docs/architecture/UMSETZUNGSPLAN_ROADMAP.md | 132 ++++++++++++++++++ .../VERBINDLICHE_REGELN_SHINKAN.md | 62 ++++++++ docs/architecture/ZIELBILD_ARCHITEKTUR.md | 78 +++++++++++ frontend/src/pages/Dashboard.jsx | 60 +++----- 8 files changed, 446 insertions(+), 40 deletions(-) create mode 100644 docs/architecture/README.md create mode 100644 docs/architecture/SCHULDEN_UND_REMEDIATION.md create mode 100644 docs/architecture/UMSETZUNGSPLAN_ROADMAP.md create mode 100644 docs/architecture/VERBINDLICHE_REGELN_SHINKAN.md create mode 100644 docs/architecture/ZIELBILD_ARCHITEKTUR.md diff --git a/CLAUDE.md b/CLAUDE.md index f48905a..664905e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,6 +14,7 @@ > | Medien-Archiv, Lifecycle, Inline (Plan §11) | `.claude/docs/technical/MEDIA_ASSETS_AND_ARCHIVE_SPEC.md` | > | Handover / nächste Session | **`docs/HANDOVER.md`** | > | Fachlicher Nutzerüberblick (Design/Product) | **`docs/FACHLICHE_NUTZERFUNKTIONEN.md`** | +> | Architektur-Zielbild, Refaktor-Roadmap, verbindliche Shinkan-Regeln | **`docs/architecture/README.md`** | ## Projekt-Übersicht diff --git a/docs/HANDOVER.md b/docs/HANDOVER.md index 6ad7843..5ffc6fb 100644 --- a/docs/HANDOVER.md +++ b/docs/HANDOVER.md @@ -20,6 +20,7 @@ Das Schema ist gegenüber dem Code zurück: Migration **`022_skills_schema_compl | Thema | Pfad | |--------|------| +| **Architektur-Zielbild, Refaktor, verbindliche Regeln (nach MVP)** | **`docs/architecture/README.md`** | | Projekt-Setup, Domain grob | `.claude/docs/working/SHINKAN_PROJECT_SETUP.md` | | **Projekt-Status (aktuell)** | `.claude/docs/PROJECT_STATUS.md` | | **Medien-Archiv, Lifecycle, Inline-Plan (§11)** | `.claude/docs/technical/MEDIA_ASSETS_AND_ARCHIVE_SPEC.md` | diff --git a/docs/architecture/README.md b/docs/architecture/README.md new file mode 100644 index 0000000..e96a33b --- /dev/null +++ b/docs/architecture/README.md @@ -0,0 +1,21 @@ +# Architektur: Zielbild, Refaktor, Regeln (Shinkan Jinkendo) + +Dieses Bündel ist die **Leitlinie für die große Refaktorierung** nach dem MVP. Es ergänzt die bestehenden Pflichtdokumente (`.claude/rules/ARCHITECTURE.md`, `CODING_RULES.md`, Zugriffsschicht, Media-Spec) und ist für **Wartbarkeit, Performance und sichere Erweiterung** verbindlich, soweit hier ausdrücklich festgelegt. + +## Inhalt + +| Datei | Zweck | +|--------|--------| +| [ZIELBILD_ARCHITEKTUR.md](./ZIELBILD_ARCHITEKTUR.md) | Zielarchitektur (Frontend, API, Daten), Qualitätsziele, Einbindung neuer Features | +| [SCHULDEN_UND_REMEDIATION.md](./SCHULDEN_UND_REMEDIATION.md) | Erfasste Architekturschuld, Reihenfolge und Massnahmen zur Behebung | +| [UMSETZUNGSPLAN_ROADMAP.md](./UMSETZUNGSPLAN_ROADMAP.md) | Phasen, Meilensteine, Abnahmekriterien, Aufwandsschwerpunkte | +| [VERBINDLICHE_REGELN_SHINKAN.md](./VERBINDLICHE_REGELN_SHINKAN.md) | **Verbindliche** Shinkan-spezifische Regeln (Ergänzung zu den globalen Rules) | + +## Pflege + +- Bei abgeschlossenen Phasen: Roadmap und Remediation-Dokument aktualisieren; bei Regeländerungen: nur mit **expliziter Projektfreigabe** (gleiches Verfahren wie bei `.claude/rules/ARCHITECTURE.md`). +- Querschnitt: **`docs/HANDOVER.md`** soll auf die aktive Roadmap-Phase verweisen. + +## Bezug MVP + +Die aktuelle Codebasis ist funktional MVP-tauglich; strukturell bestehen bekannte Schwerpunkte (grosse Seiten-Monolithen, API-Monolith im Client, redundante Lesepfade, schwere Listenqueries). Dieses Bündel definiert, wie nach **dem** MVP weitergebaut wird, ohne jedes neue Feature erneut mit **architektonischer Schuld** zu überfrachten. diff --git a/docs/architecture/SCHULDEN_UND_REMEDIATION.md b/docs/architecture/SCHULDEN_UND_REMEDIATION.md new file mode 100644 index 0000000..facffc4 --- /dev/null +++ b/docs/architecture/SCHULDEN_UND_REMEDIATION.md @@ -0,0 +1,131 @@ +# Architekturschuld – Erfassung und Behebungsschritte + +Dieses Dokument listet **bewusst** die aus MVP und Code-Review bekannten strukturellen Themen auf und ordnet **konkrete Massnahmen** zu. Reihenfolge ist an die Roadmap gekoppelt; hier die inhaltliche Detailierung. + +--- + +## A. Frontend + +### A1 – „God Pages“ (Training, Übungsformular, Vereine) + +**Schuld:** Sehr grosse Dateien (tausende Zeilen) mit viel State, vielen Effekten und eingebetteten Modals. + +**Risiko:** Hohe Re-Render-Kosten, schwerer zu testen, hoher RAM auf schwachen Geräten, neue Features vergrössern die Datei weiter. + +**Behebungsschritte:** + +1. **Inventar:** pro Page kurze Gliederung (Abschnitte) und Ziel-Komponenten benennen. +2. **Extrahieren:** Zuerst isolierbare Blöcke (Listen, Modals, Sidebar, Form-Sektionen) in Unterkomponenten; Props/Oberfläche dokumentieren. +3. **Hooks:** wiederkehrende Logik (`useEffect`-Ketten, Filter-State) in `useXxx`-Hooks pro Domäne. +4. **Optional `features/training/` o. ä.:** wenn 3+ zusammengehörige Komponenten entstehen. + +**Erfolgskriterium:** Page-Datei unter dem in `VERBINDLICHE_REGELN_SHINKAN.md` genannten Soft-Limit oder dokumentierte Ausnahme. + +--- + +### A2 – Monolithischer API-Client (`utils/api.js`) + +**Schuld:** Eine Datei bündelt alle Endpoints; erschwert Tree-Shaking, Navigation und domänenweise Ownership. + +**Behebungsschritte:** + +1. Verzeichnisstruktur festlegen, z. B. `frontend/src/api/` mit `client.js` (Token, `request`), `exercises.js`, `planning.js`, … +2. Bestehende `api.js` schrittweise zur **Facade** (`export * from …`) degradieren oder re-exportieren. +3. Neue Features **nur** in domänenspezifischen Dateien implementieren. + +**Erfolgskriterium:** Kein Wachstum des Monolithen über bestehende Endpoint-Anzahl hinaus; mittelfristig dominieren kleine Module. + +--- + +### A3 – Redundante und „chatty“ Client-Requests + +**Schuld (Beispiele):** Dashboard lädt Profil erneut trotz Auth; mehrere nahezu gleiche `listTrainingUnits`-Aufrufe; doppelte `listExercises` für KPIs. + +**Risiko:** Mehr Last auf API/DB, schlechtere UX auf langsamen Geräten. + +**Behebungsschritte:** + +1. **Profil:** eine kanonische Quelle (Auth-Profil reicht für Anzeige; fehlende Felder gezielt nachladen oder Auth-Check erweitern – fachlich klären). +2. **Dashboard:** einen **Summary-Endpoint** spezifizieren und implementieren (siehe Backend B1) oder Client auf einen aggregierten Aufruf reduzieren. +3. **Org-Inbox / globale Fetches:** Ladestrategie definieren (on-demand vs. TTL vs. sichtbarkeitsabhängig) und `OrgInboxContext` entsprechend umbauen. + +**Erfolgskriterium:** Dashboard-Initialisierung ohne redundanten `getCurrentProfile`; ohne drei parallele fast gleiche Trainingslisten (oder dokumentierte Ausnahme). + +--- + +### A4 – Schwere Abhängigkeiten + +**Schuld:** PDF/Markdown/Canvas-Pfade ziehen grosse Chunks. + +**Behebungsschritte:** Strikte `import()` an Nutzeraktion; keine statischen Top-Level-Imports schwerer Libs in gemeinsamen Einstiegspfaden. + +**Erfolgskriterium:** Lighthouse / Bundle-Analyse zeigt schwere Libs nur auf betroffenen Routen. + +--- + +## B. Backend + +### B1 – Aggregations- und Summary-APIs + +**Schuld:** Bildschirme holen mehrere Listen und aggregieren im Client. + +**Behebungsschritte:** + +1. Endpoint(s) z. B. `GET /api/dashboard/summary` oder domänenspezifisch mit gleicher Sichtbarkeitslogik wie Einzel-Listen. +2. Tests oder manuelle Checkliste gegen **Tenant-Leaks** (nur eigene/sehbare Daten). +3. Versionierung in `version.py` bei neuem Router-Block oder signifikantem Modul-Update. + +**Erfolgskriterium:** Fertigest Dashboard mit einer serverseitigen Zusammenfassung (oder festgelegte Client-Reduktion mit Messung). + +--- + +### B2 – Listenqueries (z. B. Übungsliste) + +**Schuld:** Korrelierte Subqueries pro Zeile können bei Wachstum teuer werden. + +**Behebungsschritte:** + +1. `EXPLAIN (ANALYZE, BUFFERS)` auf Produktions-näher Konfiguration mit realistischem `limit`. +2. Indizes für Filter und Sortierung ergänzen. +3. Refactoring: JOINs/LATERAL statt N-facher Subquery, wo messbar besser. + +**Erfolgskriterium:** Dokumentierte p95-Zielwerte erreicht oder Trend verbessert (siehe Roadmap). + +--- + +### B3 – Pagination + +**Schuld:** Tiefe `OFFSET`-Werte skalieren schlecht. + +**Behebungsschritte:** Keyset-Pagination für grosse Listen in späteren Phasen einführen; API-Vertrag dokumentieren. + +--- + +## C. Querschnitt + +### C1 – Messbarkeit + +**Schuld:** Optimierung ohne Baseline. + +**Behebungsschritte:** Einmalig Baseline (API p95, Bundle-Grössen Haupt-Route, ein Lasttest-Szenario) festhalten; wiederholen nach grossen Phasen. + +--- + +### C2 – Dokumentation und Audit + +**Schuld:** Wissen nur in Chats. + +**Behebungsschritte:** `HANDOVER.md` und `ACCESS_LAYER_ENDPOINT_AUDIT.md` bei jedem grösseren API-Block aktualisieren; Roadmap-Phase abhaken. + +--- + +## Mapping: Schuld → Regel + +| Schuld | Primär-Regel (Shinkan) | +|--------|-------------------------| +| God Pages | S1, S2 | +| API-Monolith | S3 | +| Globale Fetches | S4 | +| Chatty API | S5 | +| Caching-Ideen | S6 | +| Grössere Features ohne Messung | S7, S8 | diff --git a/docs/architecture/UMSETZUNGSPLAN_ROADMAP.md b/docs/architecture/UMSETZUNGSPLAN_ROADMAP.md new file mode 100644 index 0000000..ccbe0df --- /dev/null +++ b/docs/architecture/UMSETZUNGSPLAN_ROADMAP.md @@ -0,0 +1,132 @@ +# Umsetzungsplan und Roadmap – Refaktorierung Shinkan Jinkendo + +**Aktueller Stand (laufend):** Phase 1 begonnen – Dashboard: kein zweites `getCurrentProfile`, eine `listTrainingUnits`-Abfrage für „Nächste Termine“ und Notiz-Pool statt zweier identischer Calls. + +**Ziel:** Nach MVP eine **nachhaltige** Architektur für Wachstum, **Performance** (Server + schwache Clients) und **sichere Feature-Erweiterung**. +**Leitdokumente:** [ZIELBILD_ARCHITEKTUR.md](./ZIELBILD_ARCHITEKTUR.md), [SCHULDEN_UND_REMEDIATION.md](./SCHULDEN_UND_REMEDIATION.md), [VERBINDLICHE_REGELN_SHINKAN.md](./VERBINDLICHE_REGELN_SHINKAN.md). + +--- + +## Leitplanken (vereinbart) + +- **Kein Breaking** der Zugriffsschicht: neue und geänderte Endpoints folgen `get_tenant_context` / Audit wie bisher. +- **Inkrementell:** Jede Phase liefert **nutzbaren** Stand (kein Big-Bang-Stillstand). +- **Neue Features** während der Roadmap: **S8 Checkliste** und **S1/S3** strikt; wo möglich gleich im neuen API-Modul-Pfad. + +--- + +## Phase 0 – Baseline (kurz, Pflicht) + +**Dauer:** 0,5–1 Sprint (je nach Teamgrösse). + +| Task | Output | +|------|--------| +| API p95 der Top-5-Routen messen (z. B. `profiles/me`, `exercises` list, `training-units` list, `media-assets` list) | Notiz in `docs/architecture/` oder Verweis in `HANDOVER` | +| Ein Lasttestszenario (Login → Dashboard → Übungen → Planung) | Skript/Notiz + Ergebnis | +| Bundle: Grösse Einstieg + schwerste Route | Screenshot oder `vite build`-Logablage | + +**Abnahme:** Zahlen dokumentiert; wiederholbar. + +--- + +## Phase 1 – Quick Wins Netzwerk (hoher ROI, geringes Risiko) + +**Fokus:** Weniger redundante Requests, bessere Mobile-UX, kaum strukturelle Risiken. + +| Task | Bezug Remediation | +|------|-------------------| +| Dashboard: Doppel-`getCurrentProfile` auflösen; kanonisches Profil klären | A3 | +| Dashboard: `listTrainingUnits`/`listExercises`-Reduktion oder erster Summary-Call | A3, B1 | +| Org-Inbox: Ladestrategie festlegen (Technik-Kurzkonzept 1 Seite); Umsetzung mindestens Teil 1 (z. B. lazy oder TTL) | A3 | + +**Abnahme:** Kein funktionales Leck; Netzwerk-Tab zeigt messbar weniger parallele gleiche Muster beim ersten Dashboard-Load. + +--- + +## Phase 2 – Backend Lesepfade (Skalierung „viele Nutzer“) + +**Fokus:** DB und API stabil unter parallelen Lesern. + +| Task | Bezug | +|------|--------| +| `EXPLAIN` + Index-Tuning für `list_exercises` und nächste schwere Listen | B2 | +| Summary-API finalisieren/erweitern falls in P1 nur Teilbereich | B1 | +| Optional: erste Keyset-Pagination für eine Liste mit bekanntem Sort-Key | B3 | + +**Abnahme:** p95 der optimierten Routen **verbessert** ggü. Phase 0 oder dokumentierte Obergrenze eingehalten. + +--- + +## Phase 3 – Frontend-Struktur (Wartbarkeit + Client-Performance) + +**Fokus:** God-Pages abbauen, Virtualisierung wo nötig. + +| Task | Bezug | +|------|--------| +| Eine Page komplett zerteilen als Referenz (z. B. `TrainingPlanningPage` **oder** `ExerciseFormPage`) – Rest priorisiert nach Nutzung | A1 | +| Virtualisierung für die längste produktive Liste | A1, S2 | +| Schwere Imports auf `import()` umziehen (gezielt) | A4 | + +**Abnahme:** Referenz-Page unter Soft-Limit; Regel S1 für neue Änderungen durchsetzbar. + +--- + +## Phase 4 – API-Client Modularisierung + +**Fokus:** Wartbarkeit für viele neue Features. + +| Task | Bezug | +|------|--------| +| `frontend/src/api/` anlegen, `request`/`client` zentral | A2 | +| Facade: bestehende Importe von `utils/api` nicht sofort alle brechen; Migration in Wellen | A2 | +| Neue Endpoints nur noch in Domänen-Dateien | S3 | + +**Abnahme:** Anteil neuer Module > X% der neuen Zeilen (Team-Ziel); Monolith wächst nicht weiter. + +--- + +## Phase 5 – Vertiefung DB & Pagination + +**Fokus:** Wachstum Datenbestand. + +| Task | Bezug | +|------|--------| +| Keyset für weitere Listen | B3 | +| Weitere Query-Refactorings nach Monitoring | B2 | + +**Abnahme:** Dokumentierte Paginierungs-API; keine Regression in der Zugriffsschicht. + +--- + +## Meilensteine (empfohlen) + +| Meilenstein | Inhalt | +|-------------|--------| +| **M1** | Phase 0 + 1 abgeschlossen, HANDOVER aktualisiert | +| **M2** | Phase 2 abgeschlossen, Lasttest wiederholt | +| **M3** | Phase 3 Referenz-Page + Virtualisierung live | +| **M4** | Phase 4 migrationsbereit für alle neuen Features | +| **M5** | Phase 5 für Top-Listen abgeschlossen | + +--- + +## Parallel: neue Features + +- Jedes Feature: [VERBINDLICHE_REGELN_SHINKAN.md](./VERBINDLICHE_REGELN_SHINKAN.md) **S8**. +- Berührung schwerer Pfade: kurzer Performance-Nachweis (S7). + +--- + +## Risiken und Mitigation + +| Risiko | Mitigation | +|--------|------------| +| Summary-Endpoint falsch gefiltert | Code-Review + Abgleich mit Einzel-Endpoint-Logik; Tests mit mehreren Rollen | +| Refaktor bricht PWA/Offline | Smoke-Test nach grossen Frontend-Phasen | +| Keyset bricht alte Clients | Versionierte Query-Parameter oder Übergangsfenster | + +--- + +## Pflege + +Nach jeder Phase: **README** dieses Bündels prüfen; **Roadmap** Checkboxen/Status; **HANDOVER** nächste Phase nennen. diff --git a/docs/architecture/VERBINDLICHE_REGELN_SHINKAN.md b/docs/architecture/VERBINDLICHE_REGELN_SHINKAN.md new file mode 100644 index 0000000..a356748 --- /dev/null +++ b/docs/architecture/VERBINDLICHE_REGELN_SHINKAN.md @@ -0,0 +1,62 @@ +# Verbindliche Architekturregeln – Shinkan (Ergänzung) + +**Status:** verbindlich für die Shinkan-Codebasis, **ergänzend** zu: + +- `.claude/rules/ARCHITECTURE.md` +- `.claude/rules/CODING_RULES.md` +- `.claude/docs/technical/ACCESS_LAYER_AND_GOVERNANCE_PLAN.md` +- `.claude/docs/technical/MEDIA_ASSETS_AND_ARCHIVE_SPEC.md` + +Bei Widerspruch gewinnt die **spezifischere** Regel zur **Zugriffsschicht und Governance** (Sicherheit vor Komfort). Bei Widerspruch zwischen diesem Dokument und allgemeinen Mitai-Template-Resten in `ARCHITECTURE.md` gilt für **Shinkan** dieses Dokument und die Shinkan-Pflichtlektüre in `CLAUDE.md`. + +--- + +## S1 – Frontend: Grösse und Zerlegung von Seiten + +1. **Soft-Limit:** Neue oder stark erweiterte Seiten sollen **unter ~500 Zeilen** im Page-File bleiben. Darüber: Auslagern in Komponenten/Hooks/Feature-Module mit klaren Namen. +2. **Ausnahmen** nur mit Kurzbegründung im PR und Verweis auf Messung (Bundle/Performance) oder fachliche Unteilbarkeit. +3. **Wiederkehrende UI-Blöcke** nicht per Copy-Paste über Seiten hinweg duplizieren; extrahieren in `components/` oder `features/`. + +## S2 – Frontend: Listen und Speicher + +1. Listen, die **typischerweise > 100 sichtbare oder gehaltene Einträge** im DOM ermöglichen, **müssen** virtualisiert werden (oder serverseitig strikt begrenzt + „mehr laden“ mit dokumentiertem UX – nicht beides unbegründet ignorieren). +2. **Modals und zweite Raster** gleichzeitig zum Hauptbaum nur laden, wenn geöffnet (lazy mount), wo technisch machbar ohne UX-Bruch. + +## S3 – Frontend: API-Zugriff + +1. **Alle** API-Aufrufe über die zentrale Schicht (`utils/api` bzw. nach Modularisierung dessen Module). **Kein** `fetch('/api/...')` ohne diese Schicht. +2. Während der Migration vom API-Monolithen: **neue** Endpoints ausschliesslich im **domänenspezifischen** Modul anlegen; nur bei Bedarf Re-Export über die Facade. + +## S4 – Frontend: Globale Daten und Context + +1. Neue **global** geladene Daten (jede authentifizierte Session) **bedürfen** technischer Begründung (Badge-Kritikalität, Sicherheit). Alternative: **on-demand** beim ersten Bezug oder **TTL-Cache** mit dokumentierter Invalidierung (`shinkan:…`-Events bleiben möglich). +2. Context-`value`-Objekte **müssen** stabil gehalten werden (`useMemo` / `useCallback`), wenn nicht-triviale Unterbäume davon abhängen (bereits etabliert für Auth; gleiches Muster für neue Contexts). + +## S5 – Backend: Lesepfad-Design + +1. **Keine** mehrfachen fast identischen Listenaufrufe durch den Client für **denselben** zusammensetzbaren Bildschirm, wenn ein **einzelner** Summary-Endpoint unter gleicher Sichtbarkeitslogik möglich ist. Ausnahme: nachweislich unterschiedliche Cache-Lebensdauer oder unterschiedliche Rechte – dokumentieren. +2. Neue Listen-Endpoints: **Paginierung** (`limit`/`offset` oder Keyset nach Roadmap) und Obergrenzen; keine „unbegrenzt alles“-Defaults für grosse Tabellen. +3. Schwere SQL-Konstruktionen (viele korrelierte Subqueries pro Zeile) **nur** mit Kommentar **Warum** und Hinweis auf Indexlage oder geplantes Refactoring-Ticket. + +## S6 – Backend: Mandanten und Caching + +1. **Kein** HTTP- oder Anwendungs-Cache für mandantenspezifische oder nutzerspezifische Daten **ohne** expliziten Schlüssel (mindestens: Tenant-Kontext + relevante Parameter) und **Invalidierungsstrategie**. +2. Öffentliche oder global geteilte Katalogdaten dürfen mit `ETag` / kurzem Cache optimiert werden – **nach** Abgleich mit Governance. + +## S7 – Performance und Messung (Definition of Done für grössere Features) + +1. Features, die neue Listen schwerer als bestehende Top-10-Queries machen oder **> ~50 KB** zusätzliches Client-JS pro Route erzeugen: **kurz** messen (Lighthouse mobil oder Netzwerk-Timing) und im PR festhalten. +2. Regressions in **p95** der betroffenen API nach Deploy: bei Bedarf Rollback- oder Nachsteuerungskriterium mit Team vereinbaren (Zahlen Zielbild/Roadmap). + +## S8 – Feature-Checkliste (DoD) + +Vor Merge einer grösseren Erweiterung: + +- [ ] Zugriffsschicht / Audit aktualisiert (falls zutreffend) +- [ ] Kein Verstoss gegen S1–S7 ohne dokumentierte Ausnahme +- [ ] Keine neue direkte DB-Nutzung im Frontend +- [ ] Medien/Lifecycle (falls Medien betroffen) nach Media-Spec + +--- + +**Änderungen** an diesen Regeln nur mit **expliziter Projektfreigabe** (analog zu `ARCHITECTURE.md`). diff --git a/docs/architecture/ZIELBILD_ARCHITEKTUR.md b/docs/architecture/ZIELBILD_ARCHITEKTUR.md new file mode 100644 index 0000000..55819df --- /dev/null +++ b/docs/architecture/ZIELBILD_ARCHITEKTUR.md @@ -0,0 +1,78 @@ +# Architektur-Zielbild – Shinkan Jinkendo + +**Geltungsbereich:** Trainer-/Vereinsplattform, Multi-Tenancy und Governance nach bestehender Zugriffsschicht. +**Ziele:** dauerhaft tragfähig, performant bei vielen gleichzeitigen Nutzern, akzeptabel auf **geringer Client-Leistung** (wenig RAM/CPU), **wartbar** und so strukturiert, dass **neue Features** ohne neue Grosseinkaufe an technischer Schuld einbindbar sind. + +--- + +## 1. Leitprinzipien + +1. **API-first, Mandanten-sicher** – Fachlogik und Sichtbarkeit serverseitig; das Frontend orchestriert und zeigt. Unverändert gemäss bestehender Regeln (`ACCESS_LAYER`, Governance-Helfer). +2. **Schlanke Client-Oberfläche** – JavaScript pro Route begrenzen; schwere Abhängigkeiten nur bei Bedarf laden; Listen dort virtualisieren, wo Grössenordnungen wachsen. +3. **Explizite Lesepfade** – Aggregation und Zusammenfassungen dort, wo mehrere fast gleiche Requests heute nötig sind (Dashboard, Badges), **statt** Chatty-Client-Muster. +4. **Vorhersehbarkeit für die DB** – Listenqueries ohne unnötige O(n)·Subquery-Kosten pro Zeile; Indizes und Paginierungsstrategie sind Teil des Designs. +5. **Feature-Einbindung per Checkliste** – Jedes neue Feature durchläuft die gleiche Architektur- und Performance-Checkliste (siehe Regeldokument), bevor es als „fertig“ gilt. + +--- + +## 2. Zielbild Frontend + +### 2.1 Struktur + +- **Seiten (`pages/`)** bleiben Routing-Einstiege und Komposition; **keine** Dauerlösung für Logikblöcke > ~400–500 Zeilen in einer Datei – Auslagerung in `components/`, `hooks/`, `features//`. +- **Feature-Ordner (Ziel):** wo sinnvoll `frontend/src/features//` mit klarer Grenze: UI + feature-spezifische Hooks; geteilte Helfer in `utils/` nur wenn domänenübergreifend. +- **State:** Server-State über API (keine Business-Duplikation); UI-State lokal oder in bestehenden Contexts nur, wenn mehrere Schichten der Shell betroffen sind. + +### 2.2 Performance und schwache Endgeräte + +- Route-basiertes Code-Splitting bleibt Standard; **zusätzlich** innere `dynamic import()` für schwere Pakete (PDF, grosse Editoren), sobald eine Route sie braucht. +- Lange Listen: **Virtualisierung** ab einer projektdefinierten Schwelle (siehe Regeln). +- Globale Daten (Posteingang, Badges): **bedarfsgesteuert oder mit klar dokumentiertem Cache/TTL**, nicht pauschal jede Session mit voller Last – konkrete Strategie in Roadmap/Remediation. + +### 2.3 API-Schicht im Client + +- **Ziel:** Aufteilung des heutigen `utils/api.js`-Monolithen in **domänenspezifische Module** (z. B. `api/exercises`, `api/planning`, `api/media`), mit einer dünnen **Barrel- oder Facade-Export** für Kompatibilität während der Migration. +- **Konstante:** alle HTTP-Aufrufe mit Token/Mandanten-Headern zentral; kein Rohtransport aus Komponenten. + +--- + +## 3. Zielbild Backend / API + +- **Router-Disziplin** unverändert: ein fachliches Modul, ein Router (bestehende Architekturregeln). +- **Read-Model / Summary-Endpoints** für Dashboards und wiederkehrende Kacheln: **eine** abgestimmte Antwort pro Bildschirm, wo heute mehrere Listen parallel zusammengerechnet werden – unter strikt gleicher Sichtbarkeitslogik wie die Einzel-Endpoints. +- **Listen:** sortierte Indizes passend zu `WHERE` + `ORDER BY`; für grosse Datenmengen langfristig **Keyset-Pagination** statt tiefer Offsets. +- **Schwere Queries:** Korrelierte Subqueries pro Zeile nur, wenn messbar unkritisch; sonst JOIN-/Aggregate-Refactoring mit Review. + +--- + +## 4. Zielbild Datenhaltung + +- PostgreSQL bleibt System der Wahrheit; Migrationen nummeriert, wie heute. +- Kein Mandanten-Cache ohne expliziten Key und Invalidierungskonzept (Regeldokument). + +--- + +## 5. Einbindung neuer Features (vereinbartes Muster) + +1. Fachliche Kurzspez (oder Ticket) mit **Sichtbarkeit** und **Nutzungskontext** (Mobile/Desktop, erwartete Listenlängen). +2. API-Design: Endpoints, Payload-Grösse, Paginierung; Zugriffsschicht-Check. +3. UI-Modul: Route lazy, Komponentengrösse, ggf. Virtualisierung. +4. Messung: minimal Lighthouse/Netzwerk oder Server-Timing für den neuen Pfad. +5. Audit-Eintrag bei neuen geschützten Endpoints (bestehendes Verfahren). + +--- + +## 6. Nicht-Ziele dieses Zielbilds + +- Ersetzen der Zugriffsschicht oder der Medien-Spec. +- Microservices oder zweite Schreib-Datenbank ohne ausdrücklichen Projektbeschluss. +- „Framework-Wechsel“ (React bleibt, solange nicht separat entschieden). + +--- + +## 7. Abnahme „Zielbild erreicht“ (high level) + +- Keine bekannten **God-Pages** oberhalb dokumentierter Schwellen ohne dokumentierte Ausnahme. +- API-Client modularisiert oder klar phasierter Migrationsstand mit festem Enddatum. +- Dashboard und vergleichbare Homescreens ohne redundante Mehrfach-Listen desselben Objekttyps (oder dokumentierte technische Begründung + Messung). +- Datenbank-Lesepfade der Top-5-Listen unter definierter Latenz-Schwelle auf Referenz-Hardware in Lasttests (Werte in Roadmap festzulegen). diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index d37f622..266f9dc 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -21,19 +21,13 @@ function formatCappedCount(n, capped) { } function Dashboard() { - const [profile, setProfile] = useState(null) - const [loading, setLoading] = useState(true) const [trainingHome, setTrainingHome] = useState(null) const [trainingHomeErr, setTrainingHomeErr] = useState(null) const [phase0Stats, setPhase0Stats] = useState(null) const [phase0Err, setPhase0Err] = useState(null) - const { user } = useAuth() + const { user, loading: authLoading } = useAuth() const tenantClubDepKey = useMemo(() => getTenantClubDependencyKey(user), [user]) - useEffect(() => { - loadData() - }, []) - useEffect(() => { if (!user?.id) { setTrainingHome(null) @@ -45,20 +39,7 @@ function Dashboard() { setTrainingHomeErr(null) try { const today = new Date().toISOString().slice(0, 10) - const [upcomingRaw, reviewPendingRaw, plannedPool] = await Promise.all([ - api.listTrainingUnits({ - assigned_to_me: true, - status: 'planned', - start_date: today, - sort: 'asc', - limit: 8, - }), - api.listTrainingUnits({ - assigned_to_me: true, - debrief_pending: true, - sort: 'desc', - limit: 8, - }), + const [plannedPoolRaw, reviewPendingRaw] = await Promise.all([ api.listTrainingUnits({ assigned_to_me: true, status: 'planned', @@ -66,15 +47,25 @@ function Dashboard() { sort: 'asc', limit: 40, }), + api.listTrainingUnits({ + assigned_to_me: true, + debrief_pending: true, + sort: 'desc', + limit: 8, + }), ]) - const noteHits = (plannedPool || []).filter((u) => { - const tn = (u.trainer_notes || '').trim() - const n = (u.notes || '').trim() - return Boolean(tn || n) - }).slice(0, 5) + const plannedPool = Array.isArray(plannedPoolRaw) ? plannedPoolRaw : [] + const upcoming = plannedPool.slice(0, 8) + const noteHits = plannedPool + .filter((u) => { + const tn = (u.trainer_notes || '').trim() + const n = (u.notes || '').trim() + return Boolean(tn || n) + }) + .slice(0, 5) if (!cancelled) { setTrainingHome({ - upcoming: Array.isArray(upcomingRaw) ? upcomingRaw : [], + upcoming, reviewPending: Array.isArray(reviewPendingRaw) ? reviewPendingRaw : [], plannedWithNotes: noteHits, }) @@ -146,18 +137,7 @@ function Dashboard() { } }, [user?.id, tenantClubDepKey]) - const loadData = async () => { - try { - const profileData = await api.getCurrentProfile() - setProfile(profileData) - } catch (err) { - console.error('Failed to load data:', err) - } finally { - setLoading(false) - } - } - - if (loading) { + if (authLoading) { return (
@@ -182,7 +162,7 @@ function Dashboard() {

- {profile && } + {user ? : null} {user?.id ? ( <>