110 lines
3.7 KiB
Markdown
110 lines
3.7 KiB
Markdown
|
|
# 🏗️ 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_<table>_<column>`
|
||
|
|
* **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();
|
||
|
|
```
|