# Data Layer – Designprinzipien (Extraktion) **Status:** Analyse / Arbeitspapier **Stand:** 2026-07-04 **Geltungsbereich:** Multi-Layer Data Architecture (Phase 0c, Issue #53) — keine Mitai-Gesamtarchitektur, keine konkrete Gesundheits-/Ernährungsfachlogik als Produktinhalt **Serie:** Designprinzipien für Produktfamilie · Dokument 2 von n **Vorgänger:** [PROMPT_ENGINE_DESIGN_PRINCIPLES.md](./PROMPT_ENGINE_DESIGN_PRINCIPLES.md) **Kernkomponenten:** | Bereich | Pfade | |---------|-------| | Metriken (Layer 1) | `backend/data_layer/*_metrics.py`, `scores.py`, `correlations.py` | | Utilities | `backend/data_layer/utils.py` | | Visualisierung (Layer 2b) | `*_chart_payloads.py`, `*_viz.py` | | KI-Formatierung (Layer 2a-Hilfe) | `prompt_output_compact.py` | | Persistenz-Orchestrierung | `activity_persistence_orchestrator.py`, `activity_session_metrics.py` | | Konsumenten | `routers/charts.py`, `placeholder_resolver.py`, `routers/exportdata.py` | | Leitfäden | `DATA_LAYER_EXTENSION_GUIDE.md`, `docs/issues/issue-53-phase-0c-multi-layer-architecture.md` | | Architektur-Regel Import-Grenze | `.claude/rules/ARCHITECTURE.md` §8 | --- ## Modul **Data Layer** (Phase 0c Multi-Layer Architecture, Issue #53) Zentrale Schicht für **Datenabruf, Berechnung und strukturierte Aufbereitung** — ohne UI-Formatierung, ohne Prompt-Texte, ohne Chart.js-spezifische Ausgabe in den Kern-Metrik-Modulen. --- ## Fachliche Verantwortung Der Data Layer ist die **Single Source of Truth für alle abgeleiteten Messwerte und Metriken**. Er übernimmt: 1. **Datenabruf** — Lesen aus PostgreSQL (profile-scoped), optional mit Quality-Filter. 2. **Berechnung** — Trends, Scores, Korrelationen, Aggregationen, Projektionen. 3. **Strukturierte Rückgabe** — Dicts/Listen mit numerischen Werten, Datumsfeldern, Metadaten (`confidence`, `data_points`). 4. **Konsumenten-Bereitstellung** — Charts (Layer 2b), KI-Platzhalter (Layer 2a via Resolver), Export, Router-Anreicherung. Er übernimmt **nicht**: - CSV-Parsing und Feld-Mapping (Import-Schicht) - Prompt-Template-Auflösung (Prompt Engine) - React-Rendering oder Frontend-Berechnungen - Autorisierung / Feature-Limits (Auth-Schicht) ### Schichtenmodell (Multi-Layer) ``` ┌─────────────────────────────────────────────────────────┐ │ Layer 0: Persistenz (PostgreSQL) │ │ weight_log, nutrition_log, activity_log, sleep_log, … │ └──────────────────────────┬──────────────────────────────┘ │ ┌──────────────────────────▼──────────────────────────────┐ │ Layer 1: DATA LAYER (Metriken) │ │ Strukturierte Daten · confidence · data_points │ │ KEINE formatierten Strings · KEINE Chart.js-Objekte │ └──────────────┬───────────────────────┬──────────────────┘ │ │ ▼ ▼ ┌──────────────────────────┐ ┌────────────────────────────┐ │ Layer 2a: KI / Prompts │ │ Layer 2b: Visualisierung │ │ placeholder_resolver │ │ *_chart_payloads, *_viz │ │ prompt_output_compact │ │ routers/charts.py │ └──────────────────────────┘ └────────────────────────────┘ ``` ### Administrierte vs. code-definierte Konfiguration | Was | Wo | Administrierbar? | |-----|-----|------------------| | Berechnungslogik (Formeln, Fenster) | `data_layer/*.py` | ❌ Code + Review | | Confidence-Schwellen | `data_layer/utils.py` | ❌ Code | | Goal Mode / Focus Weights | DB (`profiles`, `user_focus_area_weights`) | ✅ Nutzer/Admin | | Quality Filter (Profil) | DB (`profiles`) | ✅ Admin | | Chart-Zeitfenster | Query-Parameter an API | ✅ Request | | Referenzwerte (persönlich) | DB + `reference_values.py` | ✅ Nutzer | | EAV Session Metrics | DB (`training_*_parameter`) | ✅ Admin | **Bewusst nicht hardcodiert in Routern:** Metrik-Berechnungen — Router delegieren an Data Layer. **Hardcodiert (Code):** Domänen-Module, Confidence-Regeln, Schwellen pro Metrik-Typ, TDEE-Fallback-Logik, Chart-Payload-Struktur. ### Trennung: Metriken · Chart-Payloads · KI-Formatierung · Persistenz | Schicht | Module | Verantwortung | |---------|--------|---------------| | **Metriken** | `body_metrics.py`, `nutrition_metrics.py`, … | Reine Berechnung, strukturierte Dicts | | **Chart-Payloads** | `nutrition_chart_payloads.py`, `correlation_chart_payloads.py`, … | Chart.js-kompatible `{ labels, datasets, metadata }` aus Layer-1-Daten | | **Viz-Bundles** | `body_viz.py`, `fitness_viz.py`, … | Zusammengesetzte Dashboard-/History-Pakete für Frontend | | **KI-Kompaktierung** | `prompt_output_compact.py` | Token-sparende Zahlen/JSON für Platzhalter | | **Interpretation** | `*_interpretation.py`, `vital_signs_assessment.py` | Textliche Einordnung (WHO-Klassen etc.) — Grenze zu Layer 2a | | **Persistenz-Orchestrator** | `activity_persistence_orchestrator.py` | Schreibpfade REST/CSV → DB + Nebenwirkungen (EAV, Eval) | ### Konsumenten (wer ruft den Data Layer auf?) | Konsument | Muster | |-----------|--------| | `routers/charts.py` | Layer-1-Funktion + Chart-Payload-Builder | | `placeholder_resolver.py` | Layer-1 → Formatierung/JSON für `{{placeholders}}` | | `routers/exportdata.py` | `enrich_sessions_with_metrics`, `serialize_dates` | | `routers/activity.py`, `csv_import.py` | `activity_persistence_orchestrator` (Schreiben) | | `prompt_executor.execute_prompt_with_data` | ⚠️ teils Roh-SQL parallel zum Data Layer (Legacy) | ### Rollen Der Data Layer hat **keine eigene Admin-UI**. Konfiguration erfolgt indirekt: - **Admin:** Training-Parameter, Attributprofile, Referenzwert-Typen, Quality-Filter - **Nutzer:** Profildaten, Referenzwerte, Focus-Area-Gewichte (beeinflussen Scores) - **Entwickler:** Neue Funktionen in `data_layer/` nach Extension Guide --- ## Designprinzipien ### 1. Single Source of Truth für Berechnungen | | | |---|---| | **Prinzip** | Jede Metrik wird **einmal** in `data_layer/` berechnet; Charts, KI und Export konsumieren dieselbe Funktion. | | **Begründung** | Verhindert divergierende Zahlen zwischen Dashboard, Analyse und KI-Ausgabe. | | **Quelle** | Issue #53 Executive Summary; `nutrition_chart_payloads.py` Kommentar „identisch zu GET /api/charts/energy-balance“ | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Nicht alle Pfade migriert (`insights._prepare_template_vars`, `execute_prompt_with_data` Roh-SQL). | ### 2. Layer 1 liefert strukturierte Daten, keine formatierten Strings | | | |---|---| | **Prinzip** | Kern-Metrik-Funktionen geben Dicts mit `float`/`int`/`date` zurück — **keine** Strings mit Einheiten („86,1 kg“). | | **Begründung** | Formatierung ist konsumentenspezifisch (DE-Locale, Chart-Achsen, KI-Token). | | **Quelle** | `data_layer/__init__.py` Docstring: „NO FORMATTING. NO STRINGS WITH UNITS.“ | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | `placeholder_resolver` und `*_interpretation` Module formatieren teils direkt — Grenze Layer 1/2a nicht überall scharf. | ### 3. Pflicht-Metadaten: confidence + data_points | | | |---|---| | **Prinzip** | Jede Metrik-Funktion liefert mindestens `confidence` (`high`\|`medium`\|`low`\|`insufficient`) und `data_points`. | | **Begründung** | UI/KI können Datenqualität kommunizieren; Debugging und Monitoring vereinfacht. | | **Quelle** | `DATA_LAYER_EXTENSION_GUIDE.md` § Pflicht-Felder; `calculate_confidence()` in `utils.py` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Nicht runtime-validiert; Disziplin per Code-Review. | ### 4. Confidence nach Metrik-Typ und Zeitfenster | | | |---|---| | **Prinzip** | Schwellen unterscheiden `general`, `correlation`, `trend` und Fensterlänge (7d / 28d / 90d). | | **Begründung** | Korrelationen brauchen mehr Paare; Trends messen Abdeckung (% der Tage). | | **Quelle** | `data_layer/utils.py` → `calculate_confidence()` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Schwellen global hardcodiert, nicht pro Metrik konfigurierbar. | ### 5. Domänen-Module statt Monolith | | | |---|---| | **Prinzip** | Ein Python-Modul pro fachlichem Bereich (`body_metrics`, `nutrition_metrics`, …), max. ~500 Zeilen, dann Split. | | **Begründung** | Wartbarkeit, klare Ownership, parallele Entwicklung. | | **Quelle** | `DATA_LAYER_EXTENSION_GUIDE.md` § Modul-Struktur | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Einige Module deutlich >500 Zeilen (Phase-0c-Wachstum). | ### 6. Layer 2b: Chart-Payloads als Adapter | | | |---|---| | **Prinzip** | Chart.js-Strukturen leben in dedizierten `*_chart_payloads.py` / `*_viz.py`, nicht in Metrik-Modulen. | | **Begründung** | Gleiche Metrik, verschiedene Visualisierungen; API-Endpoints bleiben dünn. | | **Quelle** | `nutrition_chart_payloads.py`; `routers/charts.py` Imports | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Teilweise noch SQL-Duplikation in Payload-Buildern neben Layer-1-Aufruf. | ### 7. Layer 2a-Hilfe: KI-spezifische Kompaktierung getrennt | | | |---|---| | **Prinzip** | Token-Reduktion für LLM-Kontext (`compact_float_for_prompt`, `compact_json_payload_for_prompts`) ist eigenes Modul, nicht in Metrik-Kern. | | **Begründung** | KI hat andere Anforderungen als Charts (Präzision vs. Token-Kosten). | | **Quelle** | `prompt_output_compact.py`; Tests in `tests/test_prompt_output_compact.py` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Nur für KI-Pfad; Charts nutzen eigene Rundung. | ### 8. Import-Grenze: Ingest vs. Interpretation | | | |---|---| | **Prinzip** | CSV-Import macht Mapping + Typkonvertierung + Duplikatlogik — **keine** fachliche Auswertung beim Insert. | | **Begründung** | Semantik gehört in Layer 1+, sonst versteckte Business-Logik in Import-Adaptern. | | **Quelle** | `ARCHITECTURE.md` §8; Issue #53 | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Legacy-Adapter (Apple-Schlaf-Aggregat, dedizierte Import-Endpoints) noch aktiv. | ### 9. Persistenz-Orchestrator für Schreibpfade | | | |---|---| | **Prinzip** | Alle Schreibwege eines Domänenobjekts (REST, CSV, Legacy) laufen durch **einen** Orchestrator mit Nebenwirkungen (EAV, Evaluation). | | **Begründung** | Konsistente Duplikat-Erkennung, Registry-Felder, keine divergierenden Insert-Logiken. | | **Quelle** | `activity_persistence_orchestrator.py` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Bisher vor allem Aktivität; andere Domänen noch direkt in Routern. | ### 10. Registry als Feld-Kanon (Activity) | | | |---|---| | **Prinzip** | Erlaubte persistierbare Felder für CSV/REST leiten sich aus `module_registry` ab, nicht aus Router-Hardcoding. | | **Begründung** | Single Source of Truth für Import-Mappings und DB-Updates. | | **Quelle** | `activity_data_canon.py`, `activity_persistence_orchestrator.py` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Nur Activity vollständig; andere Module noch klassische Spalten-CRUD. | ### 11. EAV-Anreicherung als Read-Layer | | | |---|---| | **Prinzip** | Session-Metriken (EAV) werden beim **Lesen** angereichert (`enrich_sessions_with_metrics`), nicht pro Consumer dupliziert. | | **Begründung** | Ein Merge-Kanon für Liste, Detail, Export, Platzhalter. | | **Quelle** | `activity_session_metrics.py`; `ACTIVITY_SESSION_METRICS_EAV_AGENT_GUIDE.md` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Domänenspezifisch (Training); Muster übertragbar. | ### 12. Scores als composable Layer | | | |---|---| | **Prinzip** | Composite Scores (`scores.py`) kombinieren Domänen-Metriken mit nutzer-spezifischen Focus Weights — keine Score-Logik in Routern. | | **Begründung** | Goal-Mode-/Focus-abhängige Gewichtung zentral, für KI und Dashboard gleich. | | **Quelle** | `data_layer/scores.py`; Phase-0b-Fokus-System | | **Tragfähigkeit** | **mittel–hoch** | | **Einschränkung** | Eng an Mitai-Zielsystem gekoppelt; Muster „gewichtete Composite Scores“ ist generisch. | ### 13. API-First: Router delegieren, rechnen nicht | | | |---|---| | **Prinzip** | `routers/charts.py` und ähnliche Endpoints rufen Data-Layer-Funktionen auf und mappen auf HTTP — keine Trend-Berechnung im Router. | | **Begründung** | Testbarkeit; Frontend ohne Business-Logik. | | **Quelle** | `ARCHITECTURE.md` §1.2 API-First | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | `charts.py` ist groß (2246+ Zeilen) — viel Adapter-Code, aber Berechnung delegiert. | ### 14. serialize_dates / safe_float als Querschnitt | | | |---|---| | **Prinzip** | JSON/API-Serialisierung (Dates, Decimal) zentral in `utils.py`, nicht pro Modul neu erfunden. | | **Begründung** | PostgreSQL-Typen (DATE, DECIMAL) konsistent für API und Export. | | **Quelle** | `data_layer/utils.py` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | — | ### 15. Extension Guide als verbindlicher Entwicklungsvertrag | | | |---|---| | **Prinzip** | Neue Metriken folgen Template (Retrieve → Confidence → Early Return → Calculate → Return) und werden in `__init__.py` exportiert. | | **Begründung** | Einheitliche Struktur für 97+ Funktionen und wachsende Codebase. | | **Quelle** | `DATA_LAYER_EXTENSION_GUIDE.md` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Guide und Ist-Code divergieren teils (Modulgröße, `goals.py` noch nicht in `__init__`). | --- ## Nicht übernehmen Muster, die sich nicht bewährt haben oder zu produktspezifisch sind: 1. **Berechnungslogik in `placeholder_resolver.py`** — Phase-0b-Legacy; Resolver soll nur formatieren/aggregieren, nicht rechnen. 2. **Paralleler Roh-SQL-Kontext in `prompt_executor.execute_prompt_with_data`** — lädt Modul-Rohdaten per SQL, obwohl Layer 1 existiert; zweite Wahrheit. 3. **Legacy Insights-Pfad (`insights._prepare_template_vars`)** — eigene Variablen-Vorbereitung ohne Data Layer. 4. **Import mit fachlicher Interpretation** — Apple-Schlaf-Aggregat und ähnliche Adapter verstecken Semantik im Ingest (Gitea #69). 5. **Monolithische Router mit Inline-Berechnung** — vor Phase 0c; gelegentlich noch Reste in nicht migrierten Pfaden. 6. **Interpretation vermischt mit Layer 1** — `*_interpretation.py` liefert teils fertige Texte; für Familien-Architektur klar als Layer 2a/2b markieren oder auslagern. 7. **SQL-Duplikation in Chart-Payloads** — manche Payload-Builder führen eigene Queries statt ausschließlich Layer-1-Ergebnisse zu visualisieren. 8. **Hardcodierte Confidence global** — funktioniert, aber nicht pro Metrik/Domäne konfigurierbar; Skalierung in Multi-Tenant-Produktfamilie prüfen. 9. **Domänen-Module als Produktinhalt** — `body_metrics`, TDEE, WHR etc. sind Mitai-spezifisch; **Schichtenmodell** übernehmen, **Formeln** nicht blind kopieren. 10. **Fehlende runtime-Validierung des Return-Schemas** — `confidence`/`data_points` per Konvention, nicht per TypedDict/Pydantic erzwungen. 11. **Uneinheitliche Schreib-Orchestrierung** — nur Activity hat `persistence_orchestrator`; andere Domänen noch fragmentiert. 12. **Riesige Einzeldateien** — einige Metrik-Module >>500 Zeilen widersprechen eigenem Extension Guide. --- ## Modul-Inventar (Ist-Stand) ``` backend/data_layer/ ├── Kern-Metriken (Layer 1) │ ├── body_metrics.py │ ├── nutrition_metrics.py │ ├── activity_metrics.py │ ├── recovery_metrics.py │ ├── health_metrics.py │ ├── scores.py │ └── correlations.py ├── Visualisierung (Layer 2b) │ ├── *_chart_payloads.py (nutrition, recovery, correlation) │ └── *_viz.py (body, nutrition, fitness, recovery, history_overview) ├── KI / Format (Layer 2a-Nähe) │ ├── prompt_output_compact.py │ └── *_interpretation.py ├── Persistenz / EAV │ ├── activity_persistence_orchestrator.py │ ├── activity_session_metrics.py │ └── activity_data_canon.py ├── Querschnitt │ ├── utils.py │ ├── reference_values.py │ └── nutrition_body_merge.py └── __init__.py (Exports) ``` **Konsumenten-Endpoints (Auswahl):** 20+ Chart-Endpoints in `routers/charts.py` (E1–E5, A1–A8, R1–R5, C1–C4). --- ## Verwandte Dokumentation - Issue #53 Abschluss: [issue-53-phase-0c-multi-layer-architecture.md](../../../../docs/issues/issue-53-phase-0c-multi-layer-architecture.md) - Extension Guide: [DATA_LAYER_EXTENSION_GUIDE.md](../../technical/DATA_LAYER_EXTENSION_GUIDE.md) - Fachliche Datenarchitektur: [DATA_ARCHITECTURE.md](../../functional/DATA_ARCHITECTURE.md) - Import-Grenze: [ARCHITECTURE.md](../../../rules/ARCHITECTURE.md) §8 - Platzhalter-Anbindung: [PLACEHOLDER_REGISTRY_FRAMEWORK.md](../../technical/PLACEHOLDER_REGISTRY_FRAMEWORK.md) - Prompt Engine (Konsument Layer 2a): [PROMPT_ENGINE_DESIGN_PRINCIPLES.md](./PROMPT_ENGINE_DESIGN_PRINCIPLES.md) - Feature & Entitlement: [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](./FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md) --- ## Geplante Folgedokumente (Serie) | # | Modul | Datei (geplant) | |---|-------|-----------------| | 1 | Prompt Engine | ✅ `PROMPT_ENGINE_DESIGN_PRINCIPLES.md` | | 2 | Data Layer | ✅ dieses Dokument | | 3 | Feature & Entitlement System | ✅ `FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md` | | 4 | Registry-/Plugin-Muster | ✅ `REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md` | | 5 | Auth & Session | ✅ `AUTH_SESSION_DESIGN_PRINCIPLES.md` | | 6 | Universal Import | ✅ `UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md` | | 7 | Dashboard Widgets | ✅ `DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md` | | 8 | Navigation / IA | ✅ `NAVIGATION_IA_DESIGN_PRINCIPLES.md` | | 9 | Migration & Deploy | ✅ `MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md` |