Database overview
Baseleg uses Cloudflare D1 as the initial relational database, with Drizzle as the query/migration toolkit.
This document describes how the repo is structured so the data owner can expand it safely without breaking architecture boundaries.
Entity-relationship diagram
Reflects the schema as of migration 0005_flights_and_usage_events. PK/FK badges mark primary/foreign keys; a → note under an FK field names its target. ? after a field name means nullable. auth_* tables are generated by the Better Auth library rather than hand-designed.
bookings also has indexes on (aircraft_id, start_at, end_at), (instructor_id, start_at, end_at), and (person_id) to support conflict checks and lookups. flights.booking_id has a unique index — zero or one Flight per Booking.
Key rules
- UI routes/pages must not import Drizzle schema or repository implementations.
- Application packages must not depend on infrastructure implementations directly.
- Infrastructure owns D1/Drizzle wiring and repository implementations.
- Schema changes must ship with migrations.
Repo structure
db/schema/— canonical schema “source of truth”, one file per bounded contextdb/migrations/— SQL migration files (generated/maintained as the schema evolves)db/seeds/— seed scripts/data for local/dev environmentspackages/infrastructure/db/— Drizzle + D1 wiring (implementation detail)packages/infrastructure/repositories/— repository implementations (DB-backed)
Conventions
- One schema entrypoint per bounded context, e.g.:
db/schema/people.tsdb/schema/aircraft.tsdb/schema/scheduling.ts
- Migrations are append-only and ordered.
- Prefer explicit foreign keys for cross-table relationships.
- Keep “reporting” queries out of domain/application; implement in infrastructure behind ports.
Beyond the v1 spine
The v1 spine (People, Aircraft, Scheduling) has real schema, migrations, and indexes as shown above. Billing has one real table (usage_events) — the raw hours-flown fact written on every Flight completion — but no Charge Item/Invoice schema yet. Training, Compliance, Notifications, and Reporting remain intentionally unimplemented at the data layer — see each context’s page under bounded contexts for what’s planned.