Schéma & migrations
Chaque module vit dans son propre schéma. Aucune clé étrangère ne traverse les schémas : on référence owner_id par UUID, sans REFERENCES core.users.
Première migration #
-- migrations/000001_memo_schema.up.sql
CREATE SCHEMA IF NOT EXISTS memo;
CREATE TABLE memo.notes (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
owner_id UUID NOT NULL, -- core.users.id, PAS de FK inter-schéma
title VARCHAR(500) NOT NULL,
body TEXT NOT NULL DEFAULT '',
color VARCHAR(7) NOT NULL DEFAULT '#1a73e8',
pinned BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_memo_notes_owner ON memo.notes(owner_id);Le fichier .down.sql jumeau annule la migration (DROP TABLE memo.notes;).
Comme les migrations tournent avec search_path = "memo,public", vous pouvez écrire notes sans préfixe — mais préfixer memo.notes reste plus explicite.
SQLx hors-ligne #
Pour builder sans base (CI, paquet) : embarquez le cache .sqlx et compilez avec SQLX_OFFLINE=true. Régénérez-le après tout changement de requête :
DATABASE_URL=postgres://… cargo sqlx prepare # puis commitez .sqlxLe SQL ci-dessus est identique quel que soit le langage du module. Seul l'outil qui applique les migrations change (sqlx en Rust ; Alembic/Phinx, golang-migrate, etc. ailleurs) — pensez à fixer le search_path sur votre schéma.