Evaluierung: Datenbank-Migrationsstrategie — Neues PostgreSQL-Schema für v2¶
Kontext¶
Die v2-Entwicklung (Laravel + Nuxt 3) teilt sich aktuell eine einzige MySQL-Datenbank mit dem Legacy-System (Yii 1). Beide Systeme lesen und schreiben in dieselben 64+ Tabellen. Das erzeugt erhebliche Einschränkungen:
- Schema ist eingefroren: Spalten dürfen nicht umbenannt werden (würde Yii brechen)
- Kryptische Spaltennamen: METTRACK-Codes wie
I4201,C2303,K4601stattserial_number,next_cal_date,company - Laufzeit-Mapping-Layer:
HasFieldMapping-Trait +field_configuration-Tabelle übersetzen zwischen API-Namen und DB-Codes → Komplexität + Performance-Overhead - Keine Schema-Freiheit: Laravel darf nur 1 eigene Tabelle (
personal_access_tokens) anlegen - Konflikte: DMS-Inbox-Verarbeitung kann nicht parallel laufen; Report-Engine existiert doppelt
- Legacy-Ballast: MD5/SHA1-Passwort-Upgrade, V1CompatResponse-Middleware
Diese Evaluierung prüft, ob ein neues PostgreSQL-Schema die v2-Entwicklung vereinfachen würde.
A. Vorteile eines neuen PostgreSQL-Schemas¶
A.1 Wegfallende Komplexität¶
| Komponente | Aktuell | Nach Migration |
|---|---|---|
HasFieldMapping-Trait |
Jedes Model braucht Runtime-Mapping aus field_configuration |
Entfällt — Spalten heißen direkt serial_number etc. |
FieldMappingService |
Lädt + cached Mappings pro Tabelle | Entfällt |
field_configuration-Tabelle |
100+ Einträge, shared mit Yii, read-only für Laravel | Entfällt (oder wird zu einer optionalen Admin-Konfig) |
V1CompatResponse-Middleware |
Transformiert v2 → v1 JSON-Format | Entfällt nach v1-Abschaltung |
| Legacy-Passwort-Handling | MD5/SHA1-Erkennung + Upgrade in User.php |
Entfällt — einmalige Migration auf bcrypt |
DB-Konventionen in CLAUDE.md |
"NEVER rename DB columns", "Only ONE new table" | Entfällt — freie Schema-Evolution |
A.2 Bessere Datenmodellierung¶
- Lesbare Spalten:
inventory.serial_numberstattinventory.I4202 - Echte Datentypen:
BOOLEANstattTINYINT(1), nativeUUID,TIMESTAMPTZ,JSONB - Proper Foreign Keys: Echte FK-Constraints statt lose String-Referenzen
- Normalisierung: Custom-Fields könnten in JSONB oder EAV-Tabellen statt 25+ Spalten pro Tabelle
- Schema-Evolution: Laravel-Migrations ohne Rücksicht auf Yii
A.3 PostgreSQL-spezifische Vorteile¶
- JSONB: Flexible custom fields mit Indexierung (
GIN-Index) - Partial Indexes: z.B. Index nur auf aktive Geräte
- CTEs/Window Functions: Bessere Auswertungen (Kalibrierhistorie, Fälligkeitsberichte)
- Full-Text Search: Native
tsvector/tsquerystatt MySQL FULLTEXT - LISTEN/NOTIFY: Echtzeit-Events ohne Polling
- Array-Typen: Multi-Value-Felder ohne Junction-Tabellen
B. Schema-Design-Prinzipien (Vorschlag)¶
B.1 Namenskonventionen¶
- Tabellen:
snake_case, Plural (inventories,calibrations,customers) - Spalten:
snake_case, beschreibend (serial_number,next_calibration_date) - Primary Keys:
idalsUUID(nativ) - Foreign Keys:
{tabelle_singular}_id(z.B.customer_id,inventory_id) - Timestamps:
created_at,updated_at(Laravel-Standard)
B.2 Kern-Tabellen (Beispiel-Redesign)¶
inventories (statt inventory mit 80+ Ixxxx-Spalten)
├── id UUID PK (statt MTAG VARCHAR)
├── asset_number VARCHAR (statt I4201)
├── serial_number VARCHAR (statt I4202)
├── description TEXT (statt I4203)
├── manufacturer VARCHAR (statt I4206)
├── model VARCHAR (statt I4207)
├── status SMALLINT (statt I4211)
├── cal_interval_months INTEGER (statt I4220)
├── customer_id UUID FK (statt ktag VARCHAR)
├── parent_id UUID FK (statt I4217 → I4201 Lookup)
├── custom_fields JSONB (statt I4244-I4259, 15+ Spalten)
├── created_at TIMESTAMPTZ
├── updated_at TIMESTAMPTZ
└── deleted_at TIMESTAMPTZ (Soft Deletes)
B.3 Custom Fields via JSONB¶
Statt 25+ fester Custom-Spalten pro Tabelle (I4244-I4259) ein JSONB-Feld:
C. Migrationsstrategie¶
Option 1: Graduelle Migration (empfohlen)¶
Phase 1: Neue PostgreSQL-DB parallel aufsetzen
└─ Laravel bekommt zweite DB-Connection (`pgsql`)
└─ Neue Tabellen nur noch in Postgres
└─ Bestehende Tabellen bleiben in MySQL (Yii liest/schreibt weiter)
Phase 2: Tabelle-für-Tabelle migrieren
└─ ETL-Script: MySQL → Postgres (METTRACK-Codes → lesbare Namen)
└─ Laravel-Model auf neue Tabelle umstellen
└─ HasFieldMapping für migrierte Tabellen entfernen
└─ Dual-Read: Fallback auf MySQL falls nötig
Phase 3: v1-Abschaltung
└─ Yii-System abschalten / read-only setzen
└─ V1CompatResponse-Middleware entfernen
└─ MySQL-DB archivieren
Vorteil: Kein Big-Bang, schrittweise testbar, Rollback pro Tabelle möglich.
Option 2: Big-Bang-Migration¶
- Komplett-Export MySQL → Postgres zu einem Stichtag
- Yii wird gleichzeitig abgeschaltet
- Risiko: Erfordert vollständige v2-Feature-Parität vor Umstellung
Empfehlung: Option 1 (graduell)¶
D. Auswirkungen auf bestehenden Code¶
D.1 Laravel — Was sich ändert¶
| Datei/Bereich | Änderung |
|---|---|
config/database.php |
Neue pgsql-Connection als Default |
app/Models/*.php |
Neue Tabellennamen, lesbare Spalten, native Typen |
app/Models/Concerns/HasFieldMapping.php |
Entfällt komplett |
app/Services/FieldMapping/ |
Entfällt komplett |
database/migrations/ |
Neue Migrations für Postgres-Schema |
app/Http/Resources/Api/V2/ |
Vereinfacht (kein toApiArray() mehr, direkte Attribute) |
app/Http/Middleware/V1CompatResponse.php |
Entfällt nach v1-Abschaltung |
app/Models/User.php |
Legacy-Passwort-Logik entfernen (Zeilen 151-227) |
database/seeders/ |
Anpassung auf neues Schema |
tests/ |
Anpassung auf neue Spaltennamen |
D.2 Frontend-v2 — Minimaler Impact¶
- Frontend spricht nur mit Laravel API (
/api/v2/) - API-Feldnamen bleiben identisch (sind bereits lesbar:
serial_numberetc.) - Keine Änderungen nötig, solange API-Contract gleich bleibt
D.3 Neue Artisan-Commands (benötigt)¶
php artisan migrate:from-legacy— ETL-Command für Datentransfer MySQL → Postgres- Mapping-Tabelle: METTRACK-Code → neuer Spaltenname (einmalig)
E. Risiken und Herausforderungen¶
| Risiko | Schwere | Mitigation |
|---|---|---|
| Datenverlust bei Migration | Hoch | Checksummen, Validierungs-Queries, Rollback-Plan |
| v1-Feature noch nicht in v2 | Mittel | Feature-Parität-Matrix erstellen vor Phase 3 |
| Downtime bei Umstellung | Mittel | Dual-Write in Phase 2 minimiert Downtime |
| MetTeam-Integration (nAssetUID etc.) | Mittel | FK-Mapping-Tabelle für externe IDs beibehalten |
| Custom Fields Migration | Niedrig | JSONB ist flexibler als feste Spalten |
| Team-Einarbeitung PostgreSQL | Niedrig | Laravel abstrahiert 95% der DB-Unterschiede |
F. Empfohlener Phasenplan¶
Phase 1 — Fundament¶
- PostgreSQL in Docker-Setup integrieren
- Neues Schema-Design für Kerntabellen finalisieren
- Erste Laravel-Migration für Postgres erstellen
- ETL-Command-Grundgerüst (
migrate:from-legacy)
Phase 2 — Kern-Migration¶
inventoriesmigrieren (wichtigste Tabelle, 80+ Spalten → ~20 + JSONB)customersmigrierencalibrations+resultsmigrierenusersmigrieren (inkl. Passwort-Upgrade auf bcrypt)- Je Tabelle: Model anpassen, HasFieldMapping entfernen, Tests aktualisieren
Phase 3 — Neben-Tabellen¶
- Alle verbleibenden Tabellen migrieren
- V1CompatResponse-Middleware entfernen
- FieldMappingService + HasFieldMapping-Trait entfernen
field_configuration-Tabelle entfernen oder zu Admin-Config umbauen
Phase 4 — Abschluss¶
- Yii read-only setzen oder abschalten
- MySQL-DB archivieren
- Dokumentation aktualisieren
- Performance-Tests und Optimierung
G. Fazit¶
Eine PostgreSQL-Migration würde die v2-Entwicklung erheblich vereinfachen:
- ~30% weniger Code durch Wegfall von FieldMapping-Layer, V1Compat, Legacy-Passwort-Handling
- Deutlich bessere Developer Experience durch lesbare Spaltennamen
- Mehr Flexibilität durch eigenes Schema ohne Yii-Rücksicht
- Zukunftssichere Basis mit PostgreSQL-Features (JSONB, native UUID, Partial Indexes)
Der graduelle Ansatz (Option 1) minimiert das Risiko und erlaubt parallelen Betrieb während der Übergangsphase. Das Frontend-v2 wäre von der Migration nicht betroffen.
Betroffene Dateien (Referenz)¶
laravel/app/Models/Concerns/HasFieldMapping.php— Entfällt nach Migrationlaravel/app/Services/FieldMapping/FieldMappingService.php— Entfällt nach Migrationlaravel/app/Http/Middleware/V1CompatResponse.php— Entfällt nach v1-Abschaltunglaravel/app/Models/User.php(Zeilen 151-227) — Legacy-PW-Logik entfälltlaravel/config/database.php— pgsql als Defaultlaravel/CLAUDE.md— DB-Konventionen aktualisierenlaravel/database/migrations/— Neues Migrations-Setlaravel/app/Models/*.php(90 Models) — Schrittweise anpassen