Spezifikation für Datenstruktur, Datenbankschema und Serverdesign einer intelligenten Fitness-App
ArtefaktDies ist das Artefakt, das in Folge 2 „Plan Your App's System Spec (No Code)“ verwendet wird. Sie können diese Spezifikation als Referenz für die Strukturierung von Cloud Firestore-Sammlungen, Serverendpunkten und dreistufigem KI-Agenten-Arbeitsspeicher verwenden.
Melden Sie sich an, um auf dieses Artefakt zuzugreifen
Melden Sie sich mit Ihrem Google-Entwicklerprofil an, um das vollständige Artefakt aus der Build It!-Reihe anzusehen und zu kopieren.
Entwicklerprofil erforderlich
Sie sind nicht berechtigt, ein Entwicklerprofil zu erstellen. Bitten Sie Ihren Workspace-Administrator, diese Funktion zu aktivieren.
Entwicklerprofil erstellen
Erstellen Sie ein kostenloses Google-Entwicklerprofil, um alle Build It!-Prompts und ‑Artefakte freizuschalten.
Builder Central beitreten, um freizuschalten
Fordern Sie mit nur einem Klick Ihr kostenloses Builder-Mitglieder-Badge an, um alle Build It!-Prompts und ‑Artefakte freizuschalten.
Status: In Bearbeitung (Entwurf mit Planning Companion)
Zieldatenbank: Google Cloud Firestore
Architekturziel: Sauberer, wartbarer, produktionsreifer Neuaufbau des Smart-Fitness-Prototyps von AI Studio.
0. Entscheidungen zu grundlegendem technischem Stack und Systemarchitektur
Um die Stabilität, Leistung und klare Grenzen der Produktion zu gewährleisten, ist die folgende Architektur-Baseline für alle Implementierungen gesperrt:
- Frontend-Framework: Next.js (App Router, React 19) mit Offline-Asset-Caching für Progressive Web-Apps (PWA) (mit Serwist/
@ducanh2912/next-pwa). - Styling und UI-Komponenten: Tailwind CSS v4 + Tailwind Variants (für die Variantenverwaltung) + Lucide React (Symbole).
- Client State Architecture:
- Zustand (mit
persist-Middleware): Verarbeitet den lokalen, taktilen und sitzungsspezifischen Clientstatus (Stoppuhr-Timer, aktive Satzpuffer, Schrittdiagramm und nicht gespeicherte Eingaben im Fitnessstudio). - TanStack Query (React Query): Verarbeitet alle asynchronen Serverstatus, das Caching von Firestore-Dokumenten, die Hintergrundvalidierung, die Deduplizierung von Anfragen und optimistische Mutationen.
- Zustand (mit
- Backend-Laufzeit und API-Hosting: Next.js-Routen-Handler (
app/api/...), die in Google Cloud Run in derselben Region wie Firestore und Gemini-Kontext-Caching gehostet werden, um eine interne Latenz von unter 15 ms zu erreichen. - Authentifizierung: Firebase Authentication (Google Log-in, E-Mail/Passwort und anonyme Onboarding-Verknüpfung). Der Client hängt
Authorization: Bearer <Firebase_ID_Token>an alle API-Anfragen an. Der Server überprüft die Identität mithilfe vonfirebase-admin.auth().verifyIdToken(token). - Primäre Datenbank: Google Cloud Firestore.
1. Systemübersicht und grundlegende User Journey
1.1 Umfang und Architekturprinzipien
In dieser Spezifikation werden die Datenmodelle, Cloud Firestore-Schemas, Serverendpunktverträge und KI-Agenten-Arbeitsspeicherstrukturen für die Smart Fitness-Anwendung definiert. Sie dient als zentrale Datenquelle für die gesamte Trainingsplanung, die Protokollierung früherer Trainingseinheiten und den autonomen, gestuften Coaching-Speicher. Sie ist vollständig von Frontend-Präsentations-Frameworks entkoppelt, die in einer separaten UI/UX-Designspezifikation behandelt werden.
1.2 Grundlegende Datenprinzipien
- Separation of Concerns: Strikte Trennung zwischen Datenverträgen (Backend/Datenbank) und dem UI-Design des Clients.
- Hybrid Library Architecture: Statische, unveränderliche Übungsenzyklopädie (
exercises) in Kombination mit dynamischer KI-Zusammenstellung von Wochenplänen (weekly_plans). - Human-in-the-Loop-Staging-Mutationen: KI-generierte Trainingsänderungen werden als Staging-Vorschläge zurückgegeben. Datenbank-Schreibvorgänge erfolgen ausschließlich nach expliziter Annahme durch den Nutzer.
- Kontinuierliches 3-Tier Living Memory: Die KI erfasst, aktualisiert und löscht Nutzerfakten autonom über permanente Grunddaten, ablaufende physiologische Zustände (mit expliziter TTL) und vorübergehenden situativen Kontext hinweg.
- Granulare Lesevorgänge für einzelne Dokumente: Die Daten werden in optimierten Dokumenten strukturiert (z. B. Spiralnotizbücher für Wochenpläne und wöchentliche Chat-Buckets), um die Leselatenz zu minimieren und redundante Abfragen zu vermeiden.
2. Serverendpunkte und API-Verträge
Für jeden Endpunkt muss die Antwortquelle (
AGENT,DATABASEoderHYBRID) angegeben werden, damit KI-Agenten-gestützte Endpunkte bei der Implementierung entsprechend konfiguriert werden können.
POST /api/coach/chat
- Zweck: Konversationelle Interaktion mit dem KI-Personal-Trainer, Beantwortung von Coaching-Fragen, Motivationsunterstützung und Generierung von Vorschlägen für Trainingsänderungen (sowohl tägliche Anpassungen als auch wöchentliche Überarbeitungen).
- Auslöser: Der Nutzer sendet eine Nachricht im Coach-Tab oder tippt auf einen Chip für einen kontextbezogenen Änderungsvorschlag (z.B. „Arnold Press durch DB Shoulder Press ersetzen“).
- Antwortquelle:
AGENT– nutzt Gemini mit strukturierten Tool-/Funktionsaufrufen und Streaming-Funktionen. Ändert die Datenbank nicht direkt, sondern gibt einen bereitgestellten Vorschlag für die Überprüfung durch einen Human in the Loop zurück. - Header:
Authorization: Bearer <Firebase_ID_Token>,Content-Type: application/json
Anfrage-Nutzlast:
{
"message": "string (user input or selected prompt chip text)",
"context": {
"active_tab": "string (enum: today | week | coach)",
"week_start_date": "string (ISO date YYYY-MM-DD)",
"selected_day": "string (e.g. monday, optional)"
}
}Antwortnutzlast:
{
"message": "string (conversational coaching response displayed in chat bubble)",
"proposal": {
"id": "string (unique proposal ID, nullable)",
"type": "string (enum: NONE | UPDATE_DAY | UPDATE_WEEK)",
"target_day": "string (e.g. monday, nullable)",
"summary_diff": "string (concise summary of changes, e.g. 'Swapped Arnold Press ➔ DB Shoulder Press', nullable)",
"data": "map (full proposed workout day object or weekly plan object, nullable)"
},
"memory_mutations": [
{
"operation": "string (enum: CREATE | UPDATE | DELETE)",
"target_tier": "string (enum: tier_1_staples | tier_2_physiological | tier_3_situational)",
"memory_id": "string (required for UPDATE and DELETE)",
"entry": "map (memory payload with fact, category, expires_at, etc., required for CREATE and UPDATE)",
"reason": "string (concise rationale for why memory was mutated)"
}
]
}- Dual-Write in
weekly_chats: Bei jeder Runde speichert der Server die eingehende Nutzernachricht (text) und die Antwort des Coaches (text) mitFieldValue.arrayUnion()in/users/{userId}/weekly_chats/{week_start_date}. - Staged Mutation Pattern: Gibt die Angebotsnutzlast zurück, ohne
/weekly_planszu ändern, bis der Nutzer auf [Accept Plan] tippt. - Kontinuierliche autonome Speicherverwaltung: Gibt
memory_mutationsaus, um Elemente im dreistufigen Profil des Nutzers zu erstellen, zu aktualisieren oder zu löschen.
POST /api/workouts/log
- Zweck: Speichert eine abgeschlossene Trainingseinheit im permanenten Trainingsarchiv des Nutzers, berechnet die endgültigen Gesamtwerte für Volumen/Wiederholungen, prüft auf persönliche Rekorde und markiert den geplanten Tag als abgeschlossen.
- Trigger: Der Nutzer tippt auf dem Bildschirm für das Trainingstracking auf „Training beenden“.
- Antwortquelle:
DATABASE– Direkte Datenbanktransaktion/direkter Schreibvorgang in Cloud Firestore.
Anfrage-Nutzlast:
{
"plan_reference": {
"week_start_date": "string (ISO date YYYY-MM-DD)",
"day_of_week": "string (e.g. monday)"
},
"title": "string (e.g. Upper Body Hypertrophy)",
"started_at": "string (ISO timestamp)",
"completed_at": "string (ISO timestamp)",
"duration_seconds": "number (e.g. 3120)",
"exercises_logged": [
{
"exercise_id": "string (canonical slug, e.g. incline-db-bench-press)",
"exercise_name": "string (e.g. Incline DB Bench Press)",
"sets": [
{
"set_number": "number (e.g. 1)",
"weight_lbs": "number (e.g. 60)",
"reps_completed": "number (e.g. 12)",
"completed": "boolean"
}
]
}
],
"user_notes": "string (optional subjective feedback)",
"perceived_exertion": "number (optional 1-10 RPE rating)"
}Antwortnutzlast:
{
"success": "boolean",
"log_id": "string (newly created workout log document ID)",
"summary": {
"total_volume_lbs": "number",
"total_sets_completed": "number",
"total_reps_completed": "number"
},
"new_prs": [
{
"exercise_id": "string (canonical slug, e.g. incline-db-bench-press)",
"metric": "string (e.g. '10 Rep Max' or 'Estimated 1RM')",
"value": "string (e.g. '65 lbs' or '105 lbs')"
}
]
}3. Cloud Firestore-Datenbankarchitektur
Sammlung: exercises
Hierarchie: Root-Sammlung (/exercises/{exercise_id}) • Zweck: Statische Enzyklopädie mit Multimedia-Inhalten, Hinweisen zur Ausführung, erforderlicher Ausrüstung und Anleitungen.
{
"id": "string (canonical slug matching document ID, e.g. incline-db-bench-press)",
"name": "string (e.g. Incline DB Bench Press)",
"category": "string (e.g. Strength, Hypertrophy, Cardio, Mobility)",
"target_muscles": {
"primary": ["string (e.g. Upper Chest)"],
"secondary": ["string (e.g. Front Deltoids, Triceps)"]
},
"media": {
"video_url": "string (URL to demonstration video)",
"poster_url": "string (URL to video poster / thumbnail image)"
},
"instructions": ["string (step-by-step execution guidance)"],
"form_tips": ["string (critical cues and common pitfalls)"],
"equipment_required": ["string (e.g. Dumbbells, Incline Bench)"]
}Sammlung: weekly_plans
Hierarchie: /users/{userId}/weekly_plans/{week_start_date} • Zweck: Das „Spiral Notebook“ enthält den vollständigen wöchentlichen Trainingsplan, zusammenfassende Messwerte und Workouts für einzelne Tage.
{
"week_start_date": "string (ISO date YYYY-MM-DD, e.g. 2026-09-07)",
"theme": "string (e.g. Hypertrophy Focus & Upper Body Overload)",
"summary": {
"total_time_minutes": "number (raw minutes, e.g. 180)",
"total_volume_lbs": "number (raw tonnage in lbs, e.g. 38400)",
"frequency": "number (active workout days, e.g. 4)"
},
"suggested_edits": ["string (pre-generated AI prompt suggestion)"],
"days": {
"monday": {
"date": "string (ISO date YYYY-MM-DD)",
"day_of_week": "string (e.g. Monday)",
"is_rest_day": "boolean",
"status": "string (enum: scheduled | completed | skipped)",
"completed_log_id": "string (nullable)",
"completed_at": "string (ISO timestamp, nullable)",
"title": "string (e.g. Upper Body Hypertrophy)",
"focus_tag": "string (e.g. Push & Chest Dominant)",
"duration_minutes": "number",
"estimated_volume_lbs": "number",
"coach_note": "string",
"muscle_focus": [{ "muscle": "string", "intensity": "number (0-100)" }],
"suggested_edits": ["string"],
"blocks": [
{
"label": "string (e.g. Main Compound Block)",
"exercises": [
{
"exercise_id": "string",
"name": "string",
"target_sets": "number",
"target_reps": "string or number",
"rest_seconds": "number"
}
]
}
]
}
}
}Sammlung: weekly_chats
Hierarchie: /users/{userId}/weekly_chats/{week_start_date} • Zweck: Der „Weekly Chat Bucket“ enthält den vollständigen Unterhaltungsverlauf für eine bestimmte Trainingswoche in einem einzigen Dokumentlesevorgang.
{
"week_start_date": "string (ISO date YYYY-MM-DD, e.g. 2026-09-07)",
"messages": [
{
"id": "string (unique message ID)",
"sender": "string (enum: user | coach)",
"text": "string (conversational message body)",
"created_at": "timestamp (server timestamp)",
"proposal": {
"id": "string (proposal UID, nullable)",
"type": "string (enum: UPDATE_DAY | UPDATE_WEEK, nullable)",
"target_day": "string (e.g. monday, nullable)",
"summary_diff": "string (nullable)",
"status": "string (enum: pending | accepted | dismissed)",
"data": "map (workout or week snapshot payload, nullable)"
}
}
]
}Sammlung: workout_logs
Hierarchie: /users/{userId}/workout_logs/{log_id} • Zweck: Dauerhaftes, unveränderliches Archiv abgeschlossener Trainingseinheiten.
{
"id": "string (document ID)",
"plan_reference": {
"week_start_date": "string (ISO date YYYY-MM-DD)",
"day_of_week": "string (e.g. monday)"
},
"title": "string (e.g. Upper Body Hypertrophy)",
"started_at": "timestamp",
"completed_at": "timestamp",
"duration_seconds": "number",
"summary": {
"total_volume_lbs": "number",
"total_sets_completed": "number",
"total_reps_completed": "number"
},
"exercises_logged": [
{
"exercise_id": "string",
"exercise_name": "string",
"sets": [
{
"set_number": "number",
"weight_lbs": "number",
"reps_completed": "number",
"completed": "boolean"
}
]
}
],
"user_notes": "string (nullable)",
"perceived_exertion": "number (1-10 RPE, nullable)"
}Sammlung: user_profiles
Hierarchie: /users/{userId}/profile/main • Zweck: Die „Living Client Chart“, in der das dreistufige Speicherprofil des Nutzers gespeichert wird.
{
"user_id": "string (document owner UID)",
"display_name": "string (e.g. Alex)",
"email": "string (e.g. alex@example.com)",
"preferences": {
"units": "string (enum: lbs | kg)",
"haptics": "boolean",
"sound": "boolean"
},
"equipment_ids": ["string (e.g. 'dumbbells', 'bench_adjustable', 'pull_up_bar')"],
"onboarding_status": "string (enum: not_started | in_progress | completed)",
"created_at": "timestamp",
"tier_1_staples": [
{
"id": "string (e.g. mem_01)",
"category": "string (enum: equipment | preference | goal | chronic_condition)",
"fact": "string (e.g. Home gym with dumbbells up to 50 lbs and adjustable bench)",
"source": "string (e.g. conversational_onboarding | manual_entry)"
}
],
"tier_2_physiological": [
{
"id": "string (e.g. mem_02)",
"body_part": "string (e.g. hamstrings, shoulder)",
"condition": "string (e.g. soreness, acute_fatigue, minor_strain)",
"fact": "string (e.g. Hamstrings are moderately sore from hill sprints)",
"effective_date": "string (ISO date YYYY-MM-DD)",
"expires_at": "timestamp"
}
],
"tier_3_situational": [
{
"id": "string (e.g. mem_03)",
"event": "string (e.g. golf_competition, travel)",
"fact": "string (e.g. Major golf tournament on Saturday; avoid excessive arm/lat fatigue on Friday)",
"week_start_date": "string (ISO date YYYY-MM-DD)",
"expires_at": "timestamp"
}
]
}Sammlung: prs
Hierarchie: /users/{userId}/prs/{exercise_slug} • Zweck: Speichert persönliche Rekorde, geschätzte 1-Rep-Max-Benchmarks und wiederholungsspezifische Rekorde.
{
"exercise_id": "string (canonical slug, e.g. incline-db-bench-press)",
"exercise_name": "string (e.g. Incline DB Bench Press)",
"estimated_1rm": "number (calculated using Brzycki formula, e.g. 105)",
"max_weight_set": {
"weight_lbs": "number (e.g. 80)",
"reps": "number (e.g. 10)",
"achieved_at": "timestamp"
},
"rep_records": {
"10": 80,
"8": 85,
"5": 95
},
"last_updated": "timestamp"
}4. Datenlebenszyklus und KI-Agenten-Pipeline
4.1 Pipeline 1: Coaching, Workout-Generierung und stufenweise Anpassung
- Nutzeraktion: Der Nutzer gibt eine Freiform-Eingabe ein oder tippt auf einen vorab generierten kontextbezogenen Bearbeitungs-Chip.
- Kontextzusammenstellung: Der Server ruft das aktive 3-Tier-Speicherprofil und den Plan für die aktuelle Woche ab und entfernt abgelaufene gemerkte Informationen.
- Agent-Inferenz: Gemini verarbeitet den Prompt und den Kontext mithilfe von strukturierten Tool-/Funktionsaufrufen.
- Autonome Änderung von gemerkten Informationen: Extrahierte Fakten über den Nutzer werden auf
/user_profiles/{userId}angewendet und mitequipment_idssynchronisiert. - Staged Proposal Staging: Vorgeschlagene Trainingsänderungen werden mit
proposal.status = "pending"in/weekly_chats/{week_start_date}zurückgegeben. - Nutzerentscheidung und Persistenz: Wenn Sie auf [Accept Plan] tippen, werden die vorgeschlagenen Daten an
/weekly_plansübergeben und der Vorschlag wird als"accepted"markiert.
4.2 Pipeline 2: Workout-Ausführung und ‑Archivierung (Grundwahrheit)
- Aufzeichnung der Live-Sitzung: Der Client verfolgt das tatsächlich gehobene Gewicht, die abgeschlossenen Wiederholungen und die Abschluss-Flags im lokalen UI-Status.
- Sitzungseinreichung: Der Client ruft
POST /api/workouts/logfür „Training beenden“ auf. - Berechnung und Validierung von Gesamtwerten: Der Server berechnet das kumulative Trainingsvolumen und prüft mithilfe der Brzycki-Formel, ob persönliche Rekorde vorliegen.
- Unveränderliche Persistenz: Schreibt einen unveränderlichen Datensatz in
/workout_logs, markiert den Tag"completed"in/weekly_plansund fügt neue PRs in/prsein.
4.3 Pipeline 3: Autonome Aufnahme und Invalidierung von gemerkten Informationen
- Aufnahme: Fakten werden kontinuierlich aus allen Gesprächsinteraktionen extrahiert.
- Klassifizierung: Tier 1 (Grundbedürfnisse, kein Ablauf), Tier 2 (physiologischer Zustand, 3–4 Tage TTL), Tier 3 (situativer Kontext, ereignisbasierte TTL).
- Bereinigung und Ungültigkeitserklärung: Abgelaufene Elemente (
current_timestamp > expires_at) werden aus dem Prompt-Kontext herausgefiltert und nach und nach gelöscht.
5. Firestore-Sicherheitsregeln und Spezifikationen für zusammengesetzte Indexe
5.1 Sicherheitsregeln (firestore.rules)
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
match /profile/{document=**} { allow read, write: if request.auth != null && request.auth.uid == userId; }
match /weekly_plans/{planId} { allow read, write: if request.auth != null && request.auth.uid == userId; }
match /weekly_chats/{chatId} { allow read, write: if request.auth != null && request.auth.uid == userId; }
match /prs/{exerciseSlug} { allow read, write: if request.auth != null && request.auth.uid == userId; }
match /workout_logs/{logId} {
allow read, create: if request.auth != null && request.auth.uid == userId;
allow update, delete: if false;
}
}
match /exercises/{exerciseId} {
allow read: if request.auth != null;
allow write: if false;
}
}
}5.2 Zusammengesetzte Abfrageindexe (firestore.indexes.json)
{
"indexes": [
{
"collectionGroup": "workout_logs",
"queryScope": "COLLECTION",
"fields": [{ "fieldPath": "completed_at", "order": "DESCENDING" }]
},
{
"collectionGroup": "weekly_plans",
"queryScope": "COLLECTION",
"fields": [{ "fieldPath": "week_start_date", "order": "DESCENDING" }]
},
{
"collectionGroup": "prs",
"queryScope": "COLLECTION",
"fields": [{ "fieldPath": "last_updated", "order": "DESCENDING" }]
}
]
}6. Änderungsprotokoll und gesperrte Entscheidungen
| Datum | Komponente/Entscheidung | Typ | Gesperrt durch |
|---|---|---|---|
| Ersteinrichtung | Leeres Spezifikationsgerüst initialisiert | Systemeinrichtung | Team |
| 2026-09-04 | Firestore-Sammlungen hinzugefügt: „exercises“ und „weekly_plans“ | Schema | Team |
| 2026-09-04 | Endpunkt POST /api/coach/chat und Sammlung chat_messages hinzugefügt | Endpunkt und Schema | Team |
| 2026-09-04 | Chatspeicher in das Bucket-Schema „weekly_chats“ refaktoriert | Schema | Team |
| 2026-09-04 | POST /api/workouts/log-Endpunkt und workout_logs-Sammlung hinzugefügt | Endpunkt und Schema | Team |
| 2026-09-04 | Added user_profiles 3-tier memory schema & updated POST /api/coach/chat with memory_mutations | Endpunkt und Schema | Team |
| 2026-09-04 | Abschnitt 1 (Systemumfang und Grundsätze) und Abschnitt 4 (Datenlebenszyklus und Pipelines) abgeschlossen | Vollständige Spezifikationen | Team |
| 2026-09-22 | Harmonisierte Spezifikationen: Next.js + Tailwind + Zustand-Stack, Bearer-Authentifizierung, PR-Untersammlung, Firestore-Regeln und kanonische Übungs-Slugs | Vollständige Harmonisierung der Spezifikationen | Architekturüberprüfung |