Database Reference
Single Source of Truth: SQL migration files in migrations/ folder
Overview
TVL Platform database schema is maintained in SQL migration files following industry standards (Rails, Django, Prisma pattern).
Why migrations as source of truth?
- ✅ Chronological (shows evolution over time)
- ✅ Executable (can build database from scratch)
- ✅ Version controlled (Git tracks all changes)
- ✅ No duplication (schema defined once)
- ✅ Industry standard (how databases are actually built)
Structure
migrations/
├── mvp.0/              MVP.0 - Foundation + Hostaway one-way sync
│   ├── 001_initial_schema.sql      All 14 tables (identity, supply, channels, etc.)
│   ├── 002_rls_policies.sql        Row-level security policies
│   └── 003_seed_data.sql           Roles, permissions, lookups
│
├── mvp.1/              MVP.1 - Two-way sync + Booking awareness
│   └── 004_add_availability.sql    Availability and booking tables
│
├── mvp.2/              MVP.2 - Multi-channel distribution
│   └── 005_multi_channel.sql       Airbnb, VRBO connectors
│
├── v1/                 V1.0 - Full booking engine + payments
│   ├── 006_bookings.sql
│   └── 007_payments.sql
│
├── v2/                 V2.0 - Events & experiences
│
└── v3/                 V3.0 - Multi-vendor marketplace
Viewing Schema
See MVP.0 Schema (Foundation)
cat docs/reference/database/migrations/mvp.0/*.sql
See Complete MVP Schema (MVP.0 + MVP.1 + MVP.2)
cat docs/reference/database/migrations/mvp.0/*.sql \
    docs/reference/database/migrations/mvp.1/*.sql \
    docs/reference/database/migrations/mvp.2/*.sql
See Complete V1 Schema
cat docs/reference/database/migrations/mvp.0/*.sql \
    docs/reference/database/migrations/mvp.1/*.sql \
    docs/reference/database/migrations/mvp.2/*.sql \
    docs/reference/database/migrations/v1/*.sql
Understanding the Business Model
For business context and conceptual models: → See Domain Specifications
Each domain specification explains:
- Business requirements - Why these tables exist
- Conceptual ERD - Entity relationships (boxes and arrows)
- Business rules - Validation, workflows, constraints
- Version applicability - Which MVP/V versions use this domain
Separation:
- Specifications: Business model (WHAT and WHY)
- Migrations: SQL implementation (HOW)
Running Migrations
Local Development (Supabase)
# Start local Supabase
supabase start
# Run all migrations
supabase db push
# Or manually:
psql -h localhost -U postgres -d postgres < migrations/mvp.0/001_initial_schema.sql
psql -h localhost -U postgres -d postgres < migrations/mvp.0/002_rls_policies.sql
psql -h localhost -U postgres -d postgres < migrations/mvp.0/003_seed_data.sql
Production (Supabase Dashboard)
- Navigate to Supabase Dashboard → SQL Editor
- Copy migration file contents
- Execute SQL
- Verify with: SELECT * FROM migrations_log;
Adding New Tables
Step 1: Update Domain Specification
docs/specifications/domains/{domain}/specification.md
Document the business requirement and conceptual model.
Step 2: Create Migration File
# Create new migration
touch docs/reference/database/migrations/mvp.1/005_add_availability.sql
Step 3: Write SQL
-- migrations/mvp.1/005_add_availability.sql
-- Purpose: Add availability calendar tables for two-way sync
-- References: specifications/domains/04-availability-calendar/
CREATE TABLE availability_blocks (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
  unit_id UUID NOT NULL REFERENCES units(id) ON DELETE CASCADE,
  start_date DATE NOT NULL,
  end_date DATE NOT NULL,
  status availability_status NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  -- Prevent overlapping blocks
  EXCLUDE USING GIST (
    unit_id WITH =,
    daterange(start_date, end_date, '[]') WITH &&
  )
);
-- Indexes
CREATE INDEX idx_availability_org_id ON availability_blocks(org_id);
CREATE INDEX idx_availability_unit_id ON availability_blocks(unit_id);
CREATE INDEX idx_availability_dates ON availability_blocks(start_date, end_date);
-- RLS Policies
ALTER TABLE availability_blocks ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON availability_blocks
  USING (org_id = current_setting('app.current_org_id')::uuid);
Step 4: Test Migration
# Test on local database
psql -h localhost -U postgres < migrations/mvp.1/005_add_availability.sql
# Verify
psql -h localhost -U postgres -c "\d availability_blocks"
Schema Standards
All tables must follow these conventions:
Required Columns
id UUID PRIMARY KEY DEFAULT gen_random_uuid()
org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE
account_id UUID NOT NULL  -- for actor-owned entities
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
Required Elements
- ✅ Primary keys (UUID)
- ✅ Foreign keys with ON DELETE behavior
- ✅ Indexes on all FKs, org_id, account_id
- ✅ JSONB columns with GIN indexes
- ✅ Row-Level Security (RLS) policies
- ✅ Check constraints for data integrity
- ✅ Comments explaining purpose
Multi-Tenancy
- ALL actor-owned tables MUST have both org_idANDaccount_id
- System/lookup tables may have org_idonly
- RLS policies enforce org-level isolation
See: Multi-Tenancy Implementation
Related Documentation
- Specifications - Product requirements and business model
- Guides - Implementation how-tos
- Reference - Technical reference
- PII Handling - Compliance documentation
 
FAQ
Q: Where can I see all tables at once? A: Concatenate all migration files up to your target version (see "Viewing Schema" above).
Q: Why aren't schemas organized by domain? A: Databases are built chronologically via migrations, not by domain. Domain context is in specifications.
Q: Where are data dictionary and ERD diagrams? A: Business ERDs are in domain specifications. Technical ERD can be generated from database using tools like SchemaSpy or dbdocs.io.
Q: What about seed data?
A: Seed data is in migrations (e.g., 003_seed_data.sql). Roles, permissions, and lookup values.
Q: How do I see what changed between versions? A: Look at migration files or see Version Deltas for high-level changes.
Q: Can I query the database schema directly? A: Yes! Once migrations are run:
-- List all tables
SELECT tablename FROM pg_tables WHERE schemaname = 'public';
-- Describe a table
\d organizations
-- See all foreign keys
SELECT * FROM information_schema.table_constraints
WHERE constraint_type = 'FOREIGN KEY';
Last Updated: 2025-10-25
Migrations Location: migrations/
Latest Version: MVP.0 (initial schema)