Skip to content

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 InitialSchema migration (entities vs. empty database).
  • Production was baselined with migration:run --fake after a pg_dump --schema-only diff proved the generated schema byte-identical to the synchronize-created one.
  • DB_SYNCHRONIZE=false everywhere (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 InitialSchema alone.
  • Auto-generated migrations must be eyeballed before merge; a generated DROP will be executed in production if merged.