6.0 KiB
Database Schema Overview
Complete database schema for MotoVaultPro Modified Feature Capsule architecture.
Migration System
Migration Order (Dependencies)
- vehicles - Primary entity, no dependencies
- fuel-logs - Depends on vehicles (vehicle_id FK)
- maintenance - Depends on vehicles (vehicle_id FK)
- stations - Independent feature
Migration Tracking
- Table:
_migrations - Purpose: Tracks executed migration files to prevent re-execution
- Behavior: Migration system is idempotent at the file level - will skip already executed files
- SQL Statement Level: Individual SQL statements within files may fail on re-run if they don't use
IF NOT EXISTSclauses - Safety: Safe to re-run the migration system; unsafe to manually re-run individual SQL files
Core Tables
vehicles
Primary entity for all vehicle data.
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.
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.
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.
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.
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_idcolumn - 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
# 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: admin-postgres (container name)
- Port: 5432 (internal), 5432 (external)
- Database: motovaultpro
- User: postgres
- Password: Loaded from secrets file
/run/secrets/postgres-password
Password Management: All database passwords are managed via Docker secrets, mounted from host files:
- Application DB:
./secrets/app/postgres-password.txt - Platform DB:
./secrets/platform/platform-db-password.txt - Vehicles DB:
./secrets/platform/vehicles-db-password.txt
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 cleanremoves all data
Production Considerations
- Regular pg_dump backups
- Point-in-time recovery
- Read replicas for analytics
- Connection pooling (PgBouncer)