# Database Schema Overview Complete database schema for MotoVaultPro Modified Feature Capsule architecture. ## Migration System ### Migration Order (Dependencies) 1. **vehicles** - Primary entity, no dependencies 2. **fuel-logs** - Depends on vehicles (vehicle_id FK) 3. **maintenance** - Depends on vehicles (vehicle_id FK) 4. **stations** - Independent feature ### Migration Tracking - **Table**: `_migrations` - **Purpose**: Created by `backend/src/_system/migrations/run-all.ts` (not yet used for skipping executed files) - **Note**: Some SQL files use `IF NOT EXISTS`. Re-running all migrations may fail on indexes without `IF NOT EXISTS`. ## Core Tables ### vehicles Primary entity for all vehicle data. ```sql vehicles ( id UUID PRIMARY KEY, user_id VARCHAR(255) NOT NULL, vin VARCHAR(17) NOT NULL, make VARCHAR(100), model VARCHAR(100), year INTEGER, nickname VARCHAR(100), color VARCHAR(50), license_plate VARCHAR(20), odometer_reading INTEGER DEFAULT 0, is_active BOOLEAN DEFAULT true, deleted_at TIMESTAMP WITH TIME ZONE, -- Soft delete created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, CONSTRAINT unique_user_vin UNIQUE(user_id, vin) ) ``` **Indexes**: user_id, vin, is_active, created_at **Triggers**: auto-update updated_at column ### vin_cache Caches NHTSA vPIC API responses for 30 days. ```sql vin_cache ( vin VARCHAR(17) PRIMARY KEY, make VARCHAR(100), model VARCHAR(100), year INTEGER, engine_type VARCHAR(100), body_type VARCHAR(100), raw_data JSONB, cached_at TIMESTAMP WITH TIME ZONE ) ``` **Indexes**: cached_at (for cleanup) **TTL**: 30 days (application-managed) ### fuel_logs Tracks fuel purchases and efficiency. ```sql fuel_logs ( id UUID PRIMARY KEY, user_id VARCHAR(255) NOT NULL, vehicle_id UUID NOT NULL REFERENCES vehicles(id), date DATE NOT NULL, odometer_reading INTEGER NOT NULL, gallons DECIMAL(8,3) NOT NULL, price_per_gallon DECIMAL(6,3), total_cost DECIMAL(8,2), station_name VARCHAR(200), notes TEXT, created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE ) ``` **Foreign Keys**: vehicle_id → vehicles.id **Indexes**: user_id, vehicle_id, date ### stations Gas station locations and details. ```sql stations ( id UUID PRIMARY KEY, google_place_id VARCHAR(255) UNIQUE, name VARCHAR(200) NOT NULL, address TEXT, latitude DECIMAL(10,8), longitude DECIMAL(11,8), phone VARCHAR(20), website VARCHAR(255), created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE ) ``` **External Source**: Google Maps Places API **Cache Strategy**: 1 hour TTL via Redis ### maintenance Vehicle maintenance records and scheduling. ```sql maintenance ( id UUID PRIMARY KEY, user_id VARCHAR(255) NOT NULL, vehicle_id UUID NOT NULL REFERENCES vehicles(id), type VARCHAR(100) NOT NULL, -- oil_change, tire_rotation, etc description TEXT, due_date DATE, due_mileage INTEGER, completed_date DATE, completed_mileage INTEGER, cost DECIMAL(8,2), service_location VARCHAR(200), notes TEXT, is_completed BOOLEAN DEFAULT false, created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE ) ``` **Foreign Keys**: vehicle_id → vehicles.id **Indexes**: user_id, vehicle_id, due_date, is_completed ## Relationships ``` vehicles (1) ──── (many) fuel_logs │ └──── (many) maintenance stations (independent - no FK relationships) ``` ## Data Constraints ### User Data Isolation - All user data tables include `user_id` column - Application enforces user-scoped queries - No cross-user data access possible ### Referential Integrity - fuel_logs.vehicle_id → vehicles.id (CASCADE on update, RESTRICT on delete) - maintenance.vehicle_id → vehicles.id (CASCADE on update, RESTRICT on delete) - Soft deletes on vehicles (deleted_at) preserve referential data ### VIN Validation - Exactly 17 characters - Cannot contain letters I, O, or Q - Application-level checksum validation - Unique per user (same VIN can exist for different users) ## Caching Strategy ### Application-Level Caching (Redis) - **VIN decodes**: 30 days (key: `vpic:vin:{vin}`) - **User vehicle lists**: 5 minutes (key: `vehicles:user:{userId}`) - **Station searches**: 1 hour (key: `stations:search:{query}`) - **Maintenance upcoming**: 1 hour (key: `maintenance:upcoming:{userId}`) ### Database-Level Caching - **vin_cache table**: Persistent 30-day cache for vPIC API results - **Cleanup**: Application-managed, removes entries older than 30 days ## Migration Commands ### Run All Migrations ```bash # In container npm run migrate:all # Via Docker make migrate ``` Single-feature migration is not implemented yet. ### Migration Files - **Location**: `backend/src/features/[feature]/migrations/` - **Format**: `001_descriptive_name.sql` - **Order**: Lexicographic sorting (001, 002, etc.) ## Database Connection ### Development (Docker) - **Host**: postgres (container name) - **Port**: 5432 - **Database**: motovaultpro - **User**: postgres - **Password**: localdev123 ### Connection Pool - **Implementation**: pg (node-postgres) - **Pool Size**: Default (10 connections) - **Idle Timeout**: 30 seconds - **Location**: `backend/src/core/config/database.ts` ## Backup Strategy ### Development - **Docker Volume**: `postgres_data` - **Persistence**: Survives container restarts - **Reset**: `make clean` removes all data ### Production Considerations - Regular pg_dump backups - Point-in-time recovery - Read replicas for analytics - Connection pooling (PgBouncer)