# 🏗️ DB Migrations Strategy V1 **Date:** 04/12/2025 **Scope:** `veza-backend-api` **Goal:** Canonical, UUID-first, production-ready PostgreSQL schema. --- ## 1. Philosophy We are moving from an **"Iterative/Repair"** mindset (fixing types, patching IDs) to a **"Declarative/Final"** mindset. The V1 migrations represent the database as it *should* be created for a fresh deployment. ### Core Rules ("The Standard") 1. **Identity:** All Primary Keys are `UUID` (`gen_random_uuid()`). No `SERIAL` or `BIGINT` PKs. 2. **Time:** * `created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP` * `updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP` (with Trigger) * `deleted_at TIMESTAMPTZ` (Nullable, for Soft Delete) 3. **Integrity:** * All Foreign Keys must be `UUID`. * All Foreign Keys must have explicit `ON DELETE` clauses (mostly `CASCADE` for child entities). * All Foreign Keys must be Indexed. 4. **Text:** Use `TEXT` or `VARCHAR(N)` appropriately. IDs/Tokens are usually `VARCHAR`. --- ## 2. Migration File Structure We use a grouped numbering system to organize domains. ### `migrations/` * **`001_extensions_and_types.sql`** * Enable `pgcrypto` (legacy support), `uuid-ossp`. * Define global ENUMs (e.g., `user_role`, `playlist_permission` if DB-enforced). * **`010_auth_and_users.sql`** * `users`, `federated_identities`. * `refresh_tokens`, `password_reset_tokens`, `email_verification_tokens`. * `user_sessions`. * **`020_rbac_and_profiles.sql`** * `roles`, `permissions`, `user_roles`, `role_permissions`. * `user_profiles` (if distinct from users), `user_settings`. * **`040_streaming_core.sql`** * `tracks`, `track_versions`. * `playlists`, `playlist_tracks`. * `playlist_collaborators`, `playlist_follows`. * **`041_streaming_analytics.sql`** * `track_plays`, `track_likes`, `track_shares`, `track_comments`. * `track_history`. * **`042_media_processing.sql`** * `hls_streams`. * `hls_transcode_queue`. * `bitrate_adaptation_logs`. * **`050_legacy_chat.sql`** * `rooms` (Legacy support). * *Note: Modern chat is in `chat` schema, managed by Rust service.* * **`900_triggers_and_functions.sql`** * `update_updated_at_column()` function. * Apply triggers to all tables with `updated_at`. --- ## 3. Idempotence & Forward-Only * **Production:** Migrations are applied forward. We do not support `DOWN` migrations for V1 in the strict sense (rollback is usually "restore backup"). * **Development:** We support a `reset_db.sh` script that drops the schema and reapplies all V1 migrations. ## 4. Indexes Strategy * **Primary Keys:** Implicit B-Tree. * **Foreign Keys:** MUST be indexed explicitly (Postgres does not do this automatically). * Naming: `idx__` * **Search Fields:** `email`, `username`, `slug` get `UNIQUE` indexes. * **Sorting:** `created_at DESC` indexes for activity feeds. --- ## 5. Example Migration Snippet ```sql -- === USERS === CREATE TABLE public.users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email VARCHAR(255) NOT NULL, username VARCHAR(30) NOT NULL, -- Timestamps created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted_at TIMESTAMPTZ ); CREATE UNIQUE INDEX idx_users_email ON public.users(email) WHERE deleted_at IS NULL; CREATE UNIQUE INDEX idx_users_username ON public.users(username) WHERE deleted_at IS NULL; -- Trigger CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON public.users FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); ```