Files
motovaultpro/docs/changes/database-20251111/database-api-update.md
2025-11-11 10:29:02 -06:00

541 lines
16 KiB
Markdown

# Vehicle Dropdown Database Migration - Master Overview
## Status: READY FOR IMPLEMENTATION
This document coordinates the migration from normalized ID-based vehicle dropdowns to a denormalized string-based system using ETL-generated data (1.1M+ vehicle options).
---
## Quick Context
### What Changed
- **Old System**: Normalized database (vehicles.make, vehicles.model, etc.) with ID-based API responses
- **New System**: Denormalized vehicle_options table with string-based API responses
- **Impact**: Complete replacement of vehicle dropdown system (database + backend + frontend)
### Why This Change
- **Better Data**: ETL pipeline provides 1.1M+ comprehensive vehicle configurations from 1980-2026
- **Simpler Queries**: Single denormalized table vs complex JOINs across 6+ tables
- **Real Transmissions**: 828 actual transmission types vs hardcoded ["Automatic", "Manual"]
- **Performance**: Composite indexes enable sub-50ms query times
---
## Implementation Strategy
### Parallel Work Units
This migration is broken into **8 independent work units** that can be executed by separate agents in parallel (where dependencies allow).
| Agent | Task | File | Dependencies | Can Start |
|-------|------|------|--------------|-----------|
| Agent 1 | Database Migration | `database-migration.md` | None | Immediately |
| Agent 2 | Platform Repository | `backend-platform-repository.md` | Agent 1 done | After DB |
| Agent 3 | Platform Service | `backend-platform-service.md` | Agent 2 done | After Repo |
| Agent 4 | Vehicles API | `backend-vehicles-api.md` | Agent 3 done | After Service |
| Agent 5 | Frontend API Client | `frontend-api-client.md` | Agent 4 API contract defined | After API contract |
| Agent 6 | Frontend Forms | `frontend-vehicle-form.md` | Agent 5 done | After API client |
| Agent 7 | Testing | `testing-validation.md` | Agents 1-6 done | After all |
| Agent 8 | VIN Decode (if needed) | Manual investigation | Agent 1 done | After DB |
### Critical Path
```
Database (Agent 1)
Platform Repository (Agent 2)
Platform Service (Agent 3)
Vehicles API (Agent 4)
Frontend API Client (Agent 5)
Frontend Forms (Agent 6)
Testing (Agent 7)
```
**Parallel opportunities:**
- Agent 8 can investigate VIN decode while Agents 2-6 work
- Agent 5 can start after Agent 4 defines API contract (doesn't need full implementation)
---
## Key Design Decisions (FINAL)
### 1. API Format: String-Based
**Decision**: Switch from `{id: number, name: string}[]` to `string[]`
**Rationale**: Aligns with denormalized database design, simpler code
**Example:**
```typescript
// OLD
GET /api/vehicles/dropdown/makes?year=2024
Response: [{id: 1, name: "Ford"}, {id: 2, name: "Honda"}]
// NEW
GET /api/vehicles/dropdown/makes?year=2024
Response: ["Ford", "Honda"]
```
### 2. Migration Strategy: Complete Replacement
**Decision**: Replace vehicles.* schema entirely (not parallel schemas)
**Rationale**: Clean architecture, no data duplication, simpler maintenance
**Impact**: Must verify VIN decode still works (Agent 8)
### 3. NULL Handling: Show with 'N/A'
**Decision**: Include electric vehicles, display 'N/A' for missing engine/transmission
**Rationale**: Ensures all vehicles available, handles 1.1% of records with NULL engine_id
**Example:**
```typescript
// Electric vehicle in dropdown
{
engine: "N/A (Electric)",
transmission: "Single-Speed Automatic"
}
```
### 4. Implementation Approach: Single Update
**Decision**: No feature flags, complete migration in one cycle
**Rationale**: Faster delivery, cleaner code, no dual-system complexity
**Requirement**: Thorough testing before deployment
---
## Breaking Changes
### API Changes
**Endpoint Response Format:**
```typescript
// All dropdown endpoints now return string[]
GET /api/vehicles/dropdown/years number[] (unchanged)
GET /api/vehicles/dropdown/makes string[] (was {id, name}[])
GET /api/vehicles/dropdown/models string[] (was {id, name}[])
GET /api/vehicles/dropdown/trims string[] (was {id, name}[])
GET /api/vehicles/dropdown/engines string[] (was {id, name}[])
GET /api/vehicles/dropdown/transmissions string[] (was {id, name}[])
```
**Query Parameters:**
```typescript
// Parameters now use string values, not IDs
GET /api/vehicles/dropdown/models?year=2024&make=Ford (was make_id=1)
GET /api/vehicles/dropdown/trims?year=2024&make=Ford&model=F-150 (was make_id=1&model_id=42)
```
### Database Schema Changes
**Removed Tables:**
```sql
vehicles.make
vehicles.model
vehicles.model_year
vehicles.trim
vehicles.engine
vehicles.trim_engine
vehicles.transmission
vehicles.trim_transmission
```
**New Tables:**
```sql
public.engines -- 30,066 records
public.transmissions -- 828 records
public.vehicle_options -- 1,122,644 records
```
**New Database Functions:**
```sql
get_makes_for_year(year INT)
get_models_for_year_make(year INT, make VARCHAR)
get_trims_for_year_make_model(year INT, make VARCHAR, model VARCHAR)
get_options_for_vehicle(year INT, make VARCHAR, model VARCHAR, trim VARCHAR)
```
### Frontend Type Changes
**Old Type:**
```typescript
interface DropdownOption {
id: number;
name: string;
}
```
**New Type:**
```typescript
type DropdownOption = string;
// Or simply use string[] directly
```
---
## New Database Schema Details
### Tables
**engines**
```sql
CREATE TABLE engines (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL -- e.g., "V8 5.0L", "L4 2.0L Turbo"
);
-- 30,066 records
```
**transmissions**
```sql
CREATE TABLE transmissions (
id SERIAL PRIMARY KEY,
type VARCHAR(255) NOT NULL -- e.g., "8-Speed Automatic", "6-Speed Manual"
);
-- 828 records
```
**vehicle_options**
```sql
CREATE TABLE vehicle_options (
id SERIAL PRIMARY KEY,
year INTEGER NOT NULL,
make VARCHAR(100) NOT NULL, -- "Ford", "Honda" (Title Case)
model VARCHAR(100) NOT NULL, -- "F-150", "Civic"
trim VARCHAR(255), -- "XLT SuperCrew", "Sport Touring"
engine_id INTEGER REFERENCES engines(id),
transmission_id INTEGER REFERENCES transmissions(id)
);
-- 1,122,644 records
-- 1.1% have NULL engine_id (electric vehicles)
-- Composite indexes for cascade queries
CREATE INDEX idx_vehicle_year_make ON vehicle_options(year, make);
CREATE INDEX idx_vehicle_year_make_model ON vehicle_options(year, make, model);
CREATE INDEX idx_vehicle_year_make_model_trim ON vehicle_options(year, make, model, trim);
```
### Database Functions
These functions optimize common queries:
```sql
-- Returns distinct makes for a given year
get_makes_for_year(year INT) RETURNS TABLE(make VARCHAR)
-- Returns distinct models for year/make
get_models_for_year_make(year INT, make VARCHAR) RETURNS TABLE(model VARCHAR)
-- Returns distinct trims for year/make/model
get_trims_for_year_make_model(year INT, make VARCHAR, model VARCHAR)
RETURNS TABLE(trim_name VARCHAR)
-- Returns engine/transmission options for specific vehicle
get_options_for_vehicle(year INT, make VARCHAR, model VARCHAR, trim VARCHAR)
RETURNS TABLE(engine_name VARCHAR, transmission_type VARCHAR, ...)
```
---
## Data Flow Changes
### Old Flow (ID-Based Cascade)
```
1. User selects Year (2024)
→ GET /dropdown/makes?year=2024
→ Returns: [{id: 1, name: "Ford"}, {id: 2, name: "Honda"}]
2. User selects Make (clicks "Ford", id=1)
→ GET /dropdown/models?year=2024&make_id=1
→ Returns: [{id: 42, name: "F-150"}, {id: 43, name: "Mustang"}]
3. User selects Model (clicks "F-150", id=42)
→ GET /dropdown/trims?year=2024&make_id=1&model_id=42
→ Returns: [{id: 301, name: "XLT"}, {id: 302, name: "Lariat"}]
4. Form stores: make="Ford", model="F-150", trim="XLT" (name strings)
```
### New Flow (String-Based Cascade)
```
1. User selects Year (2024)
→ GET /dropdown/makes?year=2024
→ Returns: ["Ford", "Honda"]
2. User selects Make ("Ford")
→ GET /dropdown/models?year=2024&make=Ford
→ Returns: ["F-150", "Mustang"]
3. User selects Model ("F-150")
→ GET /dropdown/trims?year=2024&make=Ford&model=F-150
→ Returns: ["XLT", "Lariat"]
4. Form stores: make="Ford", model="F-150", trim="XLT" (same strings)
```
**Benefit**: Simpler frontend logic - no ID tracking, direct string values
---
## File Locations Reference
### Database Files
```
data/make-model-import/migrations/001_create_vehicle_database.sql
data/make-model-import/output/01_engines.sql
data/make-model-import/output/02_transmissions.sql
data/make-model-import/output/03_vehicle_options.sql
data/make-model-import/IMPLEMENTATION_SUMMARY.md (reference doc)
```
### Backend Platform Feature
```
backend/src/features/platform/domain/vehicle-data.repository.ts
backend/src/features/platform/domain/vehicle-data.service.ts
backend/src/features/platform/types/index.ts
backend/src/features/platform/cache/platform-cache.service.ts (may need updates)
```
### Backend Vehicles Feature
```
backend/src/features/vehicles/api/vehicles.controller.ts
backend/src/features/vehicles/api/vehicles.routes.ts (minimal changes)
backend/src/features/vehicles/domain/vehicles.service.ts
backend/src/features/vehicles/types/index.ts
```
### Frontend
```
frontend/src/features/vehicles/api/vehicles.api.ts
frontend/src/features/vehicles/types/vehicles.types.ts
frontend/src/features/vehicles/components/VehicleForm.tsx
```
---
## Success Criteria
### Technical Requirements
- [ ] Database migration completes successfully (3 SQL files imported)
- [ ] All dropdown endpoints return string[] format
- [ ] Query parameters use strings (not IDs)
- [ ] NULL values display as 'N/A' or appropriate label
- [ ] Transmissions show real data (not hardcoded)
- [ ] Composite indexes perform sub-50ms queries
- [ ] All backend tests pass
- [ ] All frontend tests pass
- [ ] Mobile responsiveness verified
### Functional Requirements
- [ ] Create vehicle form works end-to-end
- [ ] Edit vehicle form loads and displays correctly
- [ ] Cascading dropdowns work: Year → Make → Model → Trim → Engine/Trans
- [ ] Electric vehicles appear in results with 'N/A' engine display
- [ ] Saved vehicles store correct string values
- [ ] VIN decode feature still operational (if dependent on schema)
### Quality Requirements (per CLAUDE.md)
- [ ] All linters pass with zero issues
- [ ] All automated checks green
- [ ] No formatting errors
- [ ] Code follows project conventions
- [ ] Old code deleted (not commented out)
---
## Testing Checklist
### Backend API Tests
- [ ] GET /dropdown/years returns number[]
- [ ] GET /dropdown/makes?year=2024 returns string[]
- [ ] GET /dropdown/models?year=2024&make=Ford returns string[]
- [ ] GET /dropdown/trims with all params returns string[]
- [ ] GET /dropdown/engines returns string[] with 'N/A' for electric
- [ ] GET /dropdown/transmissions returns real data (not hardcoded)
- [ ] Invalid parameters return 400 errors
- [ ] Database errors return 500 with appropriate message
### Frontend Form Tests
- [ ] Create form: Year dropdown loads on mount
- [ ] Create form: Make dropdown loads after year selected
- [ ] Create form: Model dropdown loads after make selected
- [ ] Create form: Trim dropdown loads after model selected
- [ ] Create form: Engine dropdown loads after trim selected
- [ ] Create form: Submission saves correct string values
- [ ] Edit form: Loads existing vehicle data correctly
- [ ] Edit form: Pre-populates all dropdowns in correct order
- [ ] Edit form: Allows changing selections and cascades properly
### Mobile Testing (REQUIRED per CLAUDE.md)
- [ ] Dropdowns render correctly on mobile viewport
- [ ] Touch interactions work smoothly
- [ ] Form is usable on small screens
- [ ] No horizontal scrolling issues
### Integration Tests
- [ ] Complete flow: Select all fields → Save → Verify in DB
- [ ] Edit existing vehicle → Change year → Cascades reset correctly
- [ ] Electric vehicle selection → Engine shows 'N/A' → Saves correctly
---
## Rollback Plan
If critical issues discovered:
1. **Database Rollback**
```bash
# Drop new tables
DROP TABLE vehicle_options CASCADE;
DROP TABLE transmissions CASCADE;
DROP TABLE engines CASCADE;
# Restore vehicles.* schema from backup
# (ensure backup created before migration)
```
2. **Code Rollback**
```bash
git revert <commit-hash> # Revert all changes
make rebuild # Rebuild containers
```
3. **Partial Rollback (if only frontend broken)**
- Revert frontend changes only
- Backend can stay with new system
- Fix frontend issues and redeploy
---
## Common Issues & Solutions
### Issue: VIN Decode Broken
**Symptom**: VIN decode endpoint returns errors after migration
**Solution**:
- Check if VIN decode queries vehicles.* tables
- If yes, need to create separate VIN decode data solution
- May need to keep minimal vehicles.* for VIN decode only
### Issue: Electric Vehicles Show No Data
**Symptom**: Electric vehicles missing from dropdowns
**Solution**:
- Verify NULL handling in repository queries
- Check that 'N/A' label generation works correctly
- Ensure frontend displays 'N/A' properly
### Issue: Slow Query Performance
**Symptom**: Dropdown queries take > 100ms
**Solution**:
- Verify composite indexes created: `\d vehicle_options`
- Check query plans: `EXPLAIN ANALYZE <query>`
- Ensure using database functions (not raw queries)
- Consider adding Redis caching
### Issue: Make Names in Wrong Case
**Symptom**: Makes show as "FORD" instead of "Ford"
**Solution**:
- Verify ETL conversion to Title Case worked
- Check database: `SELECT DISTINCT make FROM vehicle_options LIMIT 10;`
- If wrong, re-run ETL with case conversion fix
---
## Agent Communication Protocol
### Completion Signals
Each agent should:
1. Update their assigned documentation file with actual changes made
2. Post completion status in project communication channel
3. List any deviations from plan
4. Note any blockers for dependent agents
### Handoff Information
When completing work, agents should document:
- **What changed**: Specific files and functions modified
- **How to verify**: Command or test to confirm work complete
- **Breaking changes**: Any API changes affecting downstream agents
- **Blockers resolved**: Issues fixed that were blocking others
### Example Completion Message
```
Agent 2 (Platform Repository): COMPLETE
Files modified:
- backend/src/features/platform/domain/vehicle-data.repository.ts
Changes:
- getMakes() now returns string[] using get_makes_for_year()
- All 5 dropdown methods updated to query vehicle_options table
- NULL engine_id returns 'N/A (Electric)' label
Verification:
- Run: npm test -- vehicle-data.repository.test.ts
- All tests passing
API Contract for Agent 3:
- getMakes(pool, year) → Promise<string[]>
- getModels(pool, year, make) → Promise<string[]>
- getTrims(pool, year, make, model) → Promise<string[]>
- getEngines(pool, year, make, model, trim) → Promise<string[]>
- getTransmissions(pool, year, make, model) → Promise<string[]>
Agent 3 can now proceed with service layer updates.
```
---
## Detailed Task Documents
Each task has a dedicated document with implementation details:
1. **[Database Migration](./database-migration.md)** - Agent 1
2. **[Platform Repository Updates](./backend-platform-repository.md)** - Agent 2
3. **[Platform Service Updates](./backend-platform-service.md)** - Agent 3
4. **[Vehicles API Updates](./backend-vehicles-api.md)** - Agent 4
5. **[Frontend API Client Updates](./frontend-api-client.md)** - Agent 5
6. **[Frontend Form Updates](./frontend-vehicle-form.md)** - Agent 6
7. **[Testing & Validation](./testing-validation.md)** - Agent 7
---
## Questions or Issues
If you encounter issues:
1. Check this master doc for common issues
2. Review your task-specific document
3. Check database state: `docker exec mvp-postgres psql -U postgres -d motovaultpro`
4. Check logs: `make logs`
5. Consult original data docs: `data/make-model-import/IMPLEMENTATION_SUMMARY.md`
---
## Timeline Estimate
- **Agent 1** (Database): 30 minutes
- **Agent 2** (Platform Repo): 1-2 hours
- **Agent 3** (Platform Service): 1 hour
- **Agent 4** (Vehicles API): 1-2 hours
- **Agent 5** (Frontend API): 1 hour
- **Agent 6** (Frontend Forms): 2-3 hours
- **Agent 7** (Testing): 2-3 hours
**Total Sequential**: ~10-14 hours
**With Parallelization**: ~6-8 hours (agents work simultaneously where possible)
---
**Document Version**: 1.0
**Last Updated**: 2025-11-10
**Status**: Ready for Implementation