0004 — PostgreSQL + TypeORM, migration-managed schema¶
Status: reconstructed — needs confirmation
Context¶
The domain (users, KYC, orders, holdings, payouts, audit) is relational and
money-bearing. The schema was originally bootstrapped in production with
TypeORM synchronize: true, which applies entity changes to the database
unattended — unacceptable once deploys became automatic on merge.
Decision¶
PostgreSQL 16 (postgres:16-alpine in deploy/docker-compose.yml) with
TypeORM 0.3 (libs/database). Since 2026-07-23 the schema is owned by
migrations in libs/database/src/migrations/:
- The three never-executed early migrations were squashed into a generated
InitialSchemamigration (entities vs. empty database). - Production was baselined with
migration:run --fakeafter apg_dump --schema-onlydiff proved the generated schema byte-identical to the synchronize-created one. DB_SYNCHRONIZE=falseeverywhere (deploy/gen-env.sh); the backend deploy workflow backs up the DB and runs pending migrations before restarting apps (.github/workflows/deploy-backend.yml).
Consequences¶
- Schema changes are reviewable in PRs (
npm run migration:generate). - Fresh installs get the full schema from
InitialSchemaalone. - Auto-generated migrations must be eyeballed before merge; a generated
DROPwill be executed in production if merged.