Status: Analyse / Arbeitspapier
Stand: 2026-07-04
Geltungsbereich: DB-Migrationen, Container-Startup, CI/CD-Deploy — keine Anwendungsdomäne
Serie: Designprinzipien für Produktfamilie · Dokument 9 von n (Abschluss)
Vorgänger: NAVIGATION_IA_DESIGN_PRINCIPLES.md
Kernkomponenten:
| Bereich |
Pfade |
| DB-Init & Migrationen |
backend/db_init.py, backend/startup.sh |
| Migrationen |
backend/migrations/XXX_*.sql |
| Basis-Schema (Greenfield) |
backend/schema.sql |
| Tracking |
Tabelle schema_migrations |
| Compose Prod/Dev |
docker-compose.yml, docker-compose.dev-env.yml |
| CI/CD |
.gitea/workflows/deploy-dev.yml, deploy-prod.yml, test.yml |
| Versionierung |
backend/version.py (APP_VERSION, DB_SCHEMA_VERSION, MODULE_VERSIONS) |
| Doku (operativ) |
MIGRATIONS.md |
| Architektur-Regeln |
.claude/rules/ARCHITECTURE.md §2, §7 |
Modul
Migration & Deploy
Automatische PostgreSQL-Schema-Evolution beim Container-Start plus Git-getriebene Deploy-Pipeline (develop → Dev, main → Prod) auf selbst-gehosteter Infrastruktur (Docker auf Raspberry Pi).
Fachliche Verantwortung
Das Modul übernimmt:
- Schema-Migrationen — Nummerierte SQL-Dateien, idempotent wo möglich, getrackt in
schema_migrations.
- Startup-Orchestrierung — Postgres ready → Schema/Migrationen → optional SQLite-Import → Uvicorn.
- Umgebungstrennung — Dev (
3099/8099) vs. Prod (3002/8002), getrennte DBs/Volumes.
- Deploy-Automatisierung — Push auf Branch → Runner →
git reset --hard → docker compose build --no-cache → Health-Check.
- Post-Deploy-Tests — Pytest/Lint/Frontend-Build gegen deployed Container auf dem Runner.
- Versions-Metadaten — App-/Modul-Version und dokumentierte
DB_SCHEMA_VERSION.
Es übernimmt nicht:
- Fachliche Datenberechnungen (→ Data Layer)
- Automatisches Downgrade/Rollback von Schema
- Blue-Green oder Multi-Region-Deploy
Deploy-Pipeline (Happy Path)
Entwickler: commit → push develop
→ Gitea Runner: deploy-dev.yml
→ cd /home/lars/docker/bodytrack-dev
→ git fetch + reset --hard origin/develop
→ docker compose -f docker-compose.dev-env.yml build --no-cache && up -d
→ backend startup.sh → db_init.py (Migrationen)
→ curl localhost:8099/api/auth/status
→ test.yml (push + nach Deploy): pytest im Container, py_compile, npm run build
Prod: PR develop → main → deploy-prod.yml (Port 8002, bodytrack/)
Administrierte vs. code-definierte Konfiguration
| Konfiguration |
Speicherort |
Wer pflegt? |
| Migration-SQL |
backend/migrations/ |
Entwickler |
| Welche Migrationen angewendet |
schema_migrations (DB) |
Automatisch |
| Greenfield-Basis |
schema.sql |
Entwickler (selten) |
| Compose/Ports/Env |
docker-compose*.yml, .env auf Server |
Betrieb |
| Deploy-Workflow |
.gitea/workflows/*.yml |
Entwickler |
| App-Version / Changelog |
backend/version.py |
Entwickler (pro Release) |
| Prod-Geheimnisse |
Server-.env, nicht im Repo |
Betrieb |
Designprinzipien
1. Migrationen beim Container-Start ( nicht manuell in Prod)
|
|
| Prinzip |
startup.sh ruft db_init.py auf bevor Uvicorn startet; pending Migrationen werden automatisch angewendet. |
| Begründung |
Kein vergessenes Schema-Update; Deploy und DB-Stand bleiben gekoppelt. |
| Quelle |
startup.sh, db_init.py |
| Tragfähigkeit |
hoch |
| Einschränkung |
Fehlgeschlagene Migration blockiert API-Start (sys.exit(1)). |
2. Nummeriertes Datei-Pattern als Gate
|
|
| Prinzip |
Nur \d{3}_*.sql wird ausgeführt (z. B. 054_activity_session_metrics_eav.sql); alles andere wird ignoriert. |
| Begründung |
Sortierbare Reihenfolge; Ad-hoc-Skripte (check_features.sql, v9c_*.sql) verunreinigen nicht den Lauf. |
| Quelle |
run_migrations() Regex |
| Tragfähigkeit |
hoch |
| Einschränkung |
Legacy-Dateien ohne Nummer liegen noch im Ordner (historischer Ballast). |
3. Tracking-Tabelle als Single Source of „applied“
|
|
| Prinzip |
schema_migrations(filename) — jede erfolgreiche Datei genau einmal eingetragen; Pending = Dateien minus Applied. |
| Begründung |
Idempotenter Startup; wiederholter Container-Start wendet nichts doppelt an. |
| Quelle |
ensure_migration_table, apply_migration |
| Tragfähigkeit |
hoch |
| Einschränkung |
Kein checksum — geänderte Datei nach Apply wird nicht erneut ausgeführt. |
4. Alphabetische Reihenfolge = Migrations-Reihenfolge
|
|
| Prinzip |
sorted(glob) — dreistellige Präfixe (001, 054, 061) definieren die Apply-Order. |
| Begründung |
Einfach, git-freundlich, keine separate Migrations-Registry. |
| Quelle |
run_migrations() |
| Tragfähigkeit |
hoch |
| Einschränkung |
Nummern-Kollisionen oder nachträgliches Einfügen erfordern Disziplin (immer nächste freie Nummer). |
5. Fail-Fast bei Migrationsfehler
|
|
| Prinzip |
Schlägt eine Migration fehl → kein Commit in Tracking (bei Exception vor INSERT), Prozess exit 1, Container unhealthy. |
| Begründung |
API läuft nicht mit halb angewendetem Schema. |
| Quelle |
apply_migration, main in db_init.py |
| Tragfähigkeit |
hoch |
| Einschränkung |
Manueller Recovery-Prozess nötig (siehe MIGRATIONS.md Rollback). |
6. Greenfield: schema.sql, Bestand: nur Migrationen
|
|
| Prinzip |
Existiert profiles nicht → einmalig schema.sql laden; danach nur noch nummerierte Migrationen. |
| Begründung |
Frische Instanz schnell bootstrapped; langlebige DBs evolvieren incremental. |
| Quelle |
check_table_exists, load_schema |
| Tragfähigkeit |
hoch |
| Einschränkung |
schema.sql kann hinter Migrationen zurückfallen wenn nicht gepflegt. |
7. Idempotente DDL bevorzugen
|
|
| Prinzip |
ADD COLUMN IF NOT EXISTS, CREATE INDEX IF NOT EXISTS, defensive UPDATEs — Migration soll mehrfach ausführbar sein ohne Schaden. |
| Begründung |
Recovery nach partiellem Apply; manuelles Re-Run sicherer. |
| Quelle |
MIGRATIONS.md Best Practices |
| Tragfähigkeit |
hoch |
| Einschränkung |
Nicht alle Änderungen sind idempotent (DROP, irreversible Datenmigration). |
8. Kein psql-Meta in Migrationsdateien
|
|
| Prinzip |
Nur SQL — kein \echo, \i, \connect; Ausführung via psycopg2, nicht interaktiv. |
| Begründung |
Parser/Runner versteht nur SQL-Statements. |
| Quelle |
MIGRATIONS.md, apply_migration |
| Tragfähigkeit |
hoch |
| Einschränkung |
— |
9. Schema-Änderung = nummerierte Migration (nie ad-hoc in Prod)
|
|
| Prinzip |
Neue Tabellen/Spalten nur via backend/migrations/XXX_*.sql; nicht direkt in laufender Prod-DB editieren. |
| Begründung |
Reproduzierbarkeit Dev→Prod; Review im Git-Diff. |
| Quelle |
ARCHITECTURE.md, CLAUDE.md |
| Tragfähigkeit |
hoch |
| Einschränkung |
Agent-Regel — technisch nicht erzwungen. |
10. DB_SCHEMA_VERSION als dokumentierter Marker
|
|
| Prinzip |
backend/version.py → DB_SCHEMA_VERSION bei Schema-Änderung manuell bumpen (Format z. B. YYYYMMDD + Suffix). |
| Begründung |
API /api/version und Changelog zeigen Schema-Stand unabhängig von App-Minor. |
| Quelle |
ARCHITECTURE.md §2.6 |
| Tragfähigkeit |
mittel–hoch |
| Einschränkung |
Nicht automatisch aus schema_migrations abgeleitet — Drift möglich. |
11. Branch → Umgebung (develop / main)
|
|
| Prinzip |
develop → Dev-Deploy automatisch; main → Prod-Deploy automatisch; Prod nur nach expliziter Freigabe/Merge. |
| Begründung |
Klare Promotion; Dev als Integrationsumgebung. |
| Quelle |
Workflows, CLAUDE.md Deployment |
| Tragfähigkeit |
hoch |
| Einschränkung |
Kein Staging-Branch zwischen Dev und Prod. |
12. Deploy-Arbeitskopie = exakt Remote-Branch
|
|
| Prinzip |
Runner: git fetch + git reset --hard origin/<branch> — keine pull-Merge-Konflikte, kein schmutziger package-lock auf dem Pi. |
| Begründung |
Reproduzierbarer Deploy-Baum; Fix aus GUI-IA-Abnahme 2026-04-05. |
| Quelle |
deploy-prod.yml, deploy-dev.yml |
| Tragfähigkeit |
hoch |
| Einschränkung |
Lokale Hotfixes auf dem Server werden beim Deploy überschrieben. |
13. Immutabler Build pro Deploy (--no-cache)
|
|
| Prinzip |
docker compose build --no-cache bei jedem Deploy — frisches Image aus Dockerfile + Repo-Stand. |
| Begründung |
Keine veralteten Layer; Migrationen und Code garantiert im Image. |
| Quelle |
Deploy-Workflows |
| Tragfähigkeit |
hoch |
| Einschränkung |
Langsamere Deploys; kein Registry-basiertes Image-Promotion. |
14. Health-Check nach Deploy
|
|
| Prinzip |
Nach up -d: kurz warten, dann curl -sf …/api/auth/status (8099 Dev / 8002 Prod). |
| Begründung |
Minimale Smoke-Verification dass API antwortet (inkl. DB-Init durchlaufen). |
| Quelle |
Deploy-Workflows |
| Tragfähigkeit |
mittel–hoch |
| Einschränkung |
Prüft nicht fachliche Endpoints oder Migration-Inhalt. |
15. Persistente Volumes für Daten und Fotos
|
|
| Prinzip |
Postgres-Daten, /app/data, /app/photos in benannten/external Volumes — überleben Container-Rebuild. |
| Begründung |
Deploy = neues Image, nicht Datenverlust. |
| Quelle |
docker-compose*.yml |
| Tragfähigkeit |
hoch |
| Einschränkung |
Volume-Backup/Restore ist Betriebsaufgabe außerhalb Repo. |
16. Postgres Healthcheck vor Backend-Start
|
|
| Prinzip |
depends_on: condition: service_healthy — Backend startet erst wenn DB pg_isready. |
| Begründung |
wait_for_postgres in db_init ist zweite Absicherung; reduziert Race beim ersten Start. |
| Quelle |
Compose-Files |
| Tragfähigkeit |
hoch |
| Einschränkung |
— |
17. Tests gegen deployed Stack (Self-Hosted Runner)
|
|
| Prinzip |
test.yml führt pytest im laufenden Backend-Container auf dem Pi aus, nicht in isolierter GitHub-Cloud. |
| Begründung |
Tests laufen gegen echte Dev/Prod-Compose-Umgebung des Projekts. |
| Quelle |
.gitea/workflows/test.yml |
| Tragfähigkeit |
mittel |
| Einschränkung |
Prod-Deploy triggert Tests auf Prod-Pfad — Risiko wenn Tests schreibend; -m 'not slow' begrenzt Laufzeit. |
18. Feste Ports pro Umgebung
|
|
| Prinzip |
Dev 3099/8099, Prod 3002/8002 — nicht ändern (Reverse Proxy/Fritz!Box hängen daran). |
| Begründung |
Externe URLs (dev.mitai.jinkendo.de, mitai.jinkendo.de) stabil. |
| Quelle |
CLAUDE.md, Compose |
| Tragfähigkeit |
hoch (betriebsspezifisch) |
| Einschränkung |
Andere Projekte brauchen eigene Port-Matrix. |
19. Prod-Schutz: Deploy nur über Git
|
|
| Prinzip |
Keine direkten Prod-Container-/DB-Schreibzugriffe für Automation; Prod-Änderung = Merge main → Workflow. |
| Begründung |
Audit-Trail, Review, keine Drift. |
| Quelle |
ARCHITECTURE.md §7.1, /deploy Command |
| Tragfähigkeit |
hoch |
| Einschränkung |
Menschlicher SSH-Zugriff bleibt möglich — Prozess, nicht Technik. |
20. Versions-Bump als Release-Disziplin
|
|
| Prinzip |
Jede lieferbare Änderung: APP_VERSION, betroffene MODULE_VERSIONS, CHANGELOG in version.py; bei Schema auch DB_SCHEMA_VERSION. |
| Begründung |
/api/version, Support, Korrelation Deploy ↔ Code. |
| Quelle |
ARCHITECTURE.md §2.5, deploy.md |
| Tragfähigkeit |
hoch |
| Einschränkung |
Frontend-version.js in Spec erwähnt, im Repo teils nicht vorhanden — Dual-Bump unvollständig. |
Nicht übernehmen
-
Unnummerierte Migrationsdateien — v9c_*.sql, check_*.sql werden nicht auto-applied; nicht als Vorbild.
-
Migration-Datei nach Apply ändern — Tracking verhindert Re-Run; neue Nummer statt Edit.
-
Automatischer Downgrade — nicht implementiert; Rollback manuell + Tracking-Eintrag löschen.
-
Direktes Schema in Prod — immer Git-Migration + Deploy.
-
Breaking DROP ohne Koordination — App-Code und Migration in einem Release.
-
psql-Metacommands in .sql — bricht Python-Runner.
-
git pull auf Deploy-Server — Merge-Schmutz; reset --hard ist das Muster.
-
Prod-Deploy ohne Dev-Validierung — develop-First ist implizite Policy.
-
Schema-Drift ohne DB_SCHEMA_VERSION-Bump — dokumentarische Lücke.
-
Cached Docker-Build als Default — Mitai wählt Reproduzierbarkeit über Geschwindigkeit.
-
Migrationen außerhalb Container-Start vergessen — manuelles psql in Prod als Normalfall.
-
Hardcoded Seed-Daten in Migration — produktive User/Secrets nicht in SQL.
-
Port-Änderung „nebenbei“ — Infrastruktur-Kopplung.
-
Tests nur lokal, nie auf Runner-Stack — Mitai testet bewusst post-deploy im Pi-Container (Trade-off verstehen).
-
Transaktionssteuerung in SQL-Datei — Runner committet pro Datei; komplexe multi-step Rollbacks nicht eingebaut.
Modul-Inventar (Ist-Stand)
backend/
├── db_init.py # wait, schema, run_migrations, sqlite import
├── startup.sh # db_init → uvicorn
├── schema.sql # Greenfield
├── migrations/ # 001–061+ nummeriert (+ Legacy ohne Nummer)
└── version.py # APP_VERSION, DB_SCHEMA_VERSION, MODULE_VERSIONS
docker-compose.yml # Prod: 3002/8002
docker-compose.dev-env.yml # Dev: 3099/8099
.gitea/workflows/
├── deploy-dev.yml # push develop
├── deploy-prod.yml # push main
└── test.yml # pytest, lint, npm build on Pi
Server (Pi):
/home/lars/docker/bodytrack-dev/ # develop
/home/lars/docker/bodytrack/ # main
Migrationen (Stand): 60+ nummerierte Dateien (001 … 061); höchste Nummer im Repo prüfen vor neuer Migration.
Verwandte Dokumentation
Serie – Übersicht (abgeschlossen)
| # |
Modul |
Status |
| 1 |
Prompt Engine |
✅ PROMPT_ENGINE_DESIGN_PRINCIPLES.md |
| 2 |
Data Layer |
✅ DATA_LAYER_DESIGN_PRINCIPLES.md |
| 3 |
Feature & Entitlement |
✅ FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md |
| 4 |
Registry / Plugin (Meta) |
✅ 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 |
✅ dieses Dokument |