Database Architecture

PostgreSQL on Fly.io with PgBouncer connection pooling. Drizzle ORM for type-safe queries. Atlas for declarative schema migrations.

Database Topology

  Backend App
      │
      ├── DATABASE_URL ────────────► PgBouncer ──► PostgreSQL (app-user, writer role)
      │   (runtime queries)
      │
      └── DATABASE_URL_MIGRATION ──► PostgreSQL directly (migration-user, schema_admin)
          (schema changes only)

Three managed clusters on Fly.io (Frankfurt region): development, staging, production.

Connection Users

UserRoleConnectionUsed By
app-userwriterPgBouncer (DATABASE_URL)Runtime application queries
migration-userschema_adminDirect (DATABASE_URL_MIGRATION)Atlas migrations via release_command

Schema

  • Source of truth: backend/src/db/schema.ts (7,634 lines)
  • Tables: 140 (via pgTable())
  • Enums: 85 (via pgEnum())
  • ORM: Drizzle with .$type<LocalizedText>() for bilingual JSON fields

All entity types are exported from Shared Layer via shared/types.ts.

Key Entity Tables

DomainTablesSee
CRMleads, contacts, companiesLeads, Contacts, Companies
Projectsprojects, buildings, scenariosProjects, Buildings, Scenarios
Componentsbuilding_components, measuresBuilding Components, Measures
Financequotes, invoices, productsQuotes, Invoices, Products
Fundingfunding_programs, funding_applicationsFunding Programs, Funding Applications
Documentsdocuments, document_requests, filesDocuments, Document Obtaining, Files
Authusers, departments, user_rolesUsers, Departments, RBAC Authorization
Automationworkflows, forms, form_submissionsWorkflows, Forms
SchedulingappointmentsAppointments
Auditaudit_logsAudit Logs
Templatespdf_templatesPDF Templates

Migration System

Atlas declarative migrations. See Database Migrations for workflow.

ArtifactPathEditable
Schema definitionbackend/src/db/schema.tsYes (source of truth)
Generated SQLbackend/atlas/migrations/*.sqlNever
Integrity checksumbackend/atlas/migrations/atlas.sumNever

Workflow: Edit schema.tsmake db-generate NAME=description → verify SQL → commit migration + atlas.sum.

Atlas uses an ephemeral atlas_dev database on the Docker Compose PostgreSQL instance to compute schema diffs.

Seeding

Deterministic seeds using UUID v5 namespaces. See Database Seeding.

import { seedUuid, SEED_NAMESPACES } from './seed-utils';
seedUuid(SEED_NAMESPACES.USER, 'admin@renewa.de');

Pattern: check marker → skip if exists → insert with seedUuid(). Idempotent, incremental. Auto-runs when AUTO_SEED=true.

Seed files: backend/src/db/seeds/, imported via seeds/index.ts.

Data Patterns

  • LocalizedText: All user-facing text stored as jsonb with {de: string, en: string}. See Validation Pattern.
  • Soft deletes: Many tables use deletedAt timestamp column.
  • Audit trails: createdAt, updatedAt, createdBy, updatedBy on most tables. See Audit Logs.
  • UUIDs: All primary keys are UUIDs (uuid type with gen_random_uuid() default).