236 lines
6.5 KiB
Markdown
236 lines
6.5 KiB
Markdown
# 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**: 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 EXISTS` clauses
|
|
- **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.
|
|
|
|
```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 metrics.
|
|
|
|
```sql
|
|
fuel_logs (
|
|
id UUID PRIMARY KEY,
|
|
user_id VARCHAR(255) NOT NULL,
|
|
vehicle_id UUID NOT NULL REFERENCES vehicles(id) ON DELETE CASCADE,
|
|
date_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
odometer INTEGER,
|
|
trip_distance DECIMAL(10,2),
|
|
fuel_type VARCHAR(50),
|
|
fuel_grade VARCHAR(50),
|
|
fuel_units DECIMAL(10,3) NOT NULL,
|
|
cost_per_unit DECIMAL(10,3) NOT NULL,
|
|
total_cost DECIMAL(10,2),
|
|
location_data VARCHAR(500),
|
|
notes TEXT,
|
|
created_at TIMESTAMP WITH TIME ZONE,
|
|
updated_at TIMESTAMP WITH TIME ZONE
|
|
)
|
|
```
|
|
|
|
**Foreign Keys**: vehicle_id → vehicles.id (ON DELETE CASCADE)
|
|
**Indexes**: user_id, vehicle_id, date_time, created_at
|
|
|
|
### 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
|
|
**Storage**: Persisted in PostgreSQL with station_cache table
|
|
**Cache Strategy**: Postgres-based cache with TTL management
|
|
|
|
### 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) ON DELETE CASCADE,
|
|
type VARCHAR(100) NOT NULL,
|
|
category VARCHAR(50),
|
|
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 (ON DELETE CASCADE)
|
|
**Indexes**: user_id, vehicle_id, due_date, is_completed
|
|
**Constraints**: Unique(vehicle_id, type), Check(category IN valid values)
|
|
|
|
## 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 (ON DELETE CASCADE)
|
|
- maintenance.vehicle_id → vehicles.id (ON DELETE CASCADE)
|
|
- Cascading deletes ensure related logs/maintenance are removed when vehicle is deleted
|
|
- Soft deletes on vehicles (deleted_at) may result in orphaned hard-deleted related records
|
|
|
|
### 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)
|
|
- **Platform dropdown data**: 6 hours (key: `dropdown:{dataType}:{params}`)
|
|
- **VIN decodes**: 7 days (key: `vin:decode:{vin}`)
|
|
- **User vehicle lists**: 5 minutes (key: `vehicles:user:{userId}`)
|
|
- **Fuel logs per vehicle**: 5 minutes (key: `fuel-logs:vehicle:{vehicleId}:{unitSystem}`)
|
|
- **Vehicle statistics**: Real-time (no caching, fresh queries)
|
|
- **Maintenance data**: Unit system-aware caching where applicable
|
|
|
|
### Database-Level Caching
|
|
- **vin_cache table**: Persistent cache for VIN decodes
|
|
- **Cleanup**: Application-managed based on TTL strategy
|
|
|
|
## 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**: mvp-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 clean` removes all data
|
|
|
|
### Production Considerations
|
|
- Regular pg_dump backups
|
|
- Point-in-time recovery
|
|
- Read replicas for analytics
|
|
- Connection pooling (PgBouncer)
|