Schema & migrations
Every module lives in its own schema. No foreign key crosses schemas: owner_id is referenced by UUID, without REFERENCES core.users.
First 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);The twin .down.sql file undoes the migration (DROP TABLE memo.notes;).
Since migrations run with search_path = "memo,public", you can write notes without the prefix — but spelling out memo.notes is more explicit.
Offline SQLx #
To build without a database (CI, packaging): ship the .sqlx cache and compile with SQLX_OFFLINE=true. Regenerate it after any query change:
DATABASE_URL=postgres://… cargo sqlx prepare # puis commitez .sqlxThe SQL above is identical whatever the language of the module. Only the tool applying the migrations changes (sqlx in Rust; Alembic/Phinx, golang-migrate, etc. elsewhere) — remember to pin the search_path to your schema.