Kairo-Jinkendo/docs/reference/design-principles/mitai/MIGRATION_DEPLOY_DESIGN_PRINCIPLES.md

370 lines
15 KiB
Markdown
Raw Permalink Normal View History

# Migration & Deploy – Designprinzipien (Extraktion)
**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](./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:
1. **Schema-Migrationen** — Nummerierte SQL-Dateien, idempotent wo möglich, getrackt in `schema_migrations`.
2. **Startup-Orchestrierung** — Postgres ready → Schema/Migrationen → optional SQLite-Import → Uvicorn.
3. **Umgebungstrennung** — Dev (`3099`/`8099`) vs. Prod (`3002`/`8002`), getrennte DBs/Volumes.
4. **Deploy-Automatisierung** — Push auf Branch → Runner → `git reset --hard` → `docker compose build --no-cache` → Health-Check.
5. **Post-Deploy-Tests** — Pytest/Lint/Frontend-Build gegen **deployed** Container auf dem Runner.
6. **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
1. **Unnummerierte Migrationsdateien** — `v9c_*.sql`, `check_*.sql` werden nicht auto-applied; nicht als Vorbild.
2. **Migration-Datei nach Apply ändern** — Tracking verhindert Re-Run; neue Nummer statt Edit.
3. **Automatischer Downgrade** — nicht implementiert; Rollback manuell + Tracking-Eintrag löschen.
4. **Direktes Schema in Prod** — immer Git-Migration + Deploy.
5. **Breaking DROP ohne Koordination** — App-Code und Migration in einem Release.
6. **psql-Metacommands in `.sql`** — bricht Python-Runner.
7. **`git pull` auf Deploy-Server** — Merge-Schmutz; `reset --hard` ist das Muster.
8. **Prod-Deploy ohne Dev-Validierung** — develop-First ist implizite Policy.
9. **Schema-Drift ohne `DB_SCHEMA_VERSION`-Bump** — dokumentarische Lücke.
10. **Cached Docker-Build als Default** — Mitai wählt Reproduzierbarkeit über Geschwindigkeit.
11. **Migrationen außerhalb Container-Start vergessen** — manuelles psql in Prod als Normalfall.
12. **Hardcoded Seed-Daten in Migration** — produktive User/Secrets nicht in SQL.
13. **Port-Änderung „nebenbei“** — Infrastruktur-Kopplung.
14. **Tests nur lokal, nie auf Runner-Stack** — Mitai testet bewusst post-deploy im Pi-Container (Trade-off verstehen).
15. **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
- Operativ: [MIGRATIONS.md](../../technical/MIGRATIONS.md)
- Architektur: `.claude/rules/ARCHITECTURE.md` §2 (Versionierung), §7 (Prod-Schutz)
- Deploy-Command: `.claude/commands/deploy.md`, `merge-to-prod.md`
- Import/Migration-Grenze: [UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md](./UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md)
- Auth auf Prod: [AUTH_SESSION_DESIGN_PRINCIPLES.md](./AUTH_SESSION_DESIGN_PRINCIPLES.md)
---
## 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 |