feat(docs): update architecture documentation references and enhance handover details
All checks were successful
Deploy Development / deploy (push) Successful in 42s
Test Suite / pytest-backend (push) Successful in 35s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / playwright-tests (push) Successful in 57s
All checks were successful
Deploy Development / deploy (push) Successful in 42s
Test Suite / pytest-backend (push) Successful in 35s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / playwright-tests (push) Successful in 57s
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
1c268555f6
commit
7043addd15
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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` |
|
||||
|
|
|
|||
21
docs/architecture/README.md
Normal file
21
docs/architecture/README.md
Normal file
|
|
@ -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.
|
||||
131
docs/architecture/SCHULDEN_UND_REMEDIATION.md
Normal file
131
docs/architecture/SCHULDEN_UND_REMEDIATION.md
Normal file
|
|
@ -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 |
|
||||
132
docs/architecture/UMSETZUNGSPLAN_ROADMAP.md
Normal file
132
docs/architecture/UMSETZUNGSPLAN_ROADMAP.md
Normal file
|
|
@ -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.
|
||||
62
docs/architecture/VERBINDLICHE_REGELN_SHINKAN.md
Normal file
62
docs/architecture/VERBINDLICHE_REGELN_SHINKAN.md
Normal file
|
|
@ -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`).
|
||||
78
docs/architecture/ZIELBILD_ARCHITEKTUR.md
Normal file
78
docs/architecture/ZIELBILD_ARCHITEKTUR.md
Normal file
|
|
@ -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/<name>/`.
|
||||
- **Feature-Ordner (Ziel):** wo sinnvoll `frontend/src/features/<domäne>/` 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).
|
||||
|
|
@ -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 (
|
||||
<div className="app-page" style={{ padding: '2rem 0', textAlign: 'center' }}>
|
||||
<div className="spinner"></div>
|
||||
|
|
@ -182,7 +162,7 @@ function Dashboard() {
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{profile && <EmailVerificationBanner profile={profile} />}
|
||||
{user ? <EmailVerificationBanner profile={user} /> : null}
|
||||
|
||||
{user?.id ? (
|
||||
<>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user