525 lines
18 KiB
Markdown
525 lines
18 KiB
Markdown
# MotoVaultPro Architecture Overview
|
|
|
|
## Architecture Summary
|
|
|
|
MotoVaultPro is a single-tenant vehicle management application built with a **6-container Docker-first architecture**. All development and deployment occurs in production-configured containers with no local installation dependencies.
|
|
|
|
### Core Containers
|
|
1. **Traefik** - Reverse proxy and service discovery
|
|
2. **Frontend** - React SPA with Vite (Node.js 20 + nginx)
|
|
3. **Backend** - Node.js API with Fastify (Node.js 20)
|
|
4. **PostgreSQL** - Primary database (PostgreSQL 15)
|
|
5. **Redis** - Caching layer (Redis 7)
|
|
6. **Platform** - Vehicle data service (Python FastAPI)
|
|
|
|
### Key Architectural Principles
|
|
- **Production-Only**: All services use production builds and configuration
|
|
- **Docker-First**: Zero local installations, all work happens in containers
|
|
- **Single-Tenant**: Application serves a single user/tenant
|
|
- **User-Scoped Data**: All application data isolated by `user_id`
|
|
- **Feature Capsule Organization**: Self-contained feature modules in `backend/src/features/`
|
|
|
|
## Container Interaction Diagram
|
|
|
|
```
|
|
Internet/Users
|
|
|
|
|
v
|
|
+----------------+------------------+
|
|
| Traefik |
|
|
| (Reverse Proxy & TLS) |
|
|
| Ports: 80, 443, 8080 |
|
|
+----------------+------------------+
|
|
|
|
|
+---------------------+---------------------+
|
|
| |
|
|
v v
|
|
+------------------+ +--------------------+
|
|
| Frontend | | Backend |
|
|
| React + Vite | | Node.js + Fastify |
|
|
| Port: 3000 | | Port: 3001 |
|
|
+------------------+ +--------------------+
|
|
(frontend network) (backend network)
|
|
|
|
|
v
|
|
+-------------------+
|
|
| Platform |
|
|
| Python + FastAPI |
|
|
| Port: 8000 |
|
|
+-------------------+
|
|
|
|
|
+---------------------------------+
|
|
| |
|
|
v v
|
|
+---------------+ +-------------+
|
|
| PostgreSQL | | Redis |
|
|
| Port: 5432 | | Port: 6379 |
|
|
+---------------+ +-------------+
|
|
(database network - internal)
|
|
```
|
|
|
|
## Network Topology
|
|
|
|
### Frontend Network
|
|
- **Purpose**: Public traffic routing
|
|
- **Type**: Bridge (non-internal)
|
|
- **Connected Services**: Traefik, Frontend
|
|
- **Access**: External (for Traefik public endpoints)
|
|
|
|
### Backend Network
|
|
- **Purpose**: API service communication
|
|
- **Type**: Bridge (non-internal)
|
|
- **Connected Services**: Traefik, Backend, Platform
|
|
- **Access**: External (requires Auth0 JWT validation)
|
|
|
|
### Database Network
|
|
- **Purpose**: Data layer isolation
|
|
- **Type**: Bridge (internal)
|
|
- **Connected Services**: Backend, Platform, PostgreSQL, Redis
|
|
- **Access**: Internal only, no external exposure
|
|
|
|
## Request Flow
|
|
|
|
### User Request Flow
|
|
```
|
|
1. User → HTTPS Request
|
|
2. Traefik → TLS Termination + Route Selection
|
|
- Domain: motovaultpro.com or www.motovaultpro.com
|
|
- Path: / → Frontend (priority 10)
|
|
- Path: /api → Backend (priority 20)
|
|
- Path: /platform → Platform (priority 25)
|
|
3. Frontend/Backend → Process Request
|
|
4. Response → Traefik → User
|
|
```
|
|
|
|
### Authentication Flow
|
|
```
|
|
1. User → Frontend → Auth0 (Login)
|
|
2. Auth0 → JWT Token → Frontend
|
|
3. Frontend → Backend (with Authorization: Bearer <JWT>)
|
|
4. Backend → Validate JWT via Auth0 JWKS
|
|
5. Backend → Extract user_id from JWT (request.user.sub)
|
|
6. Backend → Execute user-scoped queries
|
|
7. Backend → Response → Frontend
|
|
```
|
|
|
|
### Backend to Platform Flow
|
|
```
|
|
1. Backend Feature → HTTP Request → Platform Service
|
|
- URL: http://mvp-platform:8000
|
|
- Headers: Authorization: Bearer <API_KEY>
|
|
2. Platform → Query PostgreSQL (vehicles schema)
|
|
3. Platform → Check/Update Redis Cache
|
|
4. Platform → Response → Backend
|
|
```
|
|
|
|
### Data Access Flow
|
|
```
|
|
1. Backend/Platform → PostgreSQL Query
|
|
- Database: motovaultpro
|
|
- Connection: Internal via database network
|
|
2. Backend/Platform → Redis Cache Check
|
|
- Connection: Internal via database network
|
|
3. Response → Backend → Frontend
|
|
```
|
|
|
|
## Service Details
|
|
|
|
### Traefik (Reverse Proxy)
|
|
- **Image**: traefik:v3.0
|
|
- **Purpose**: Service discovery, TLS termination, load balancing
|
|
- **Ports**:
|
|
- 80 (HTTP)
|
|
- 443 (HTTPS)
|
|
- 8080 (Dashboard)
|
|
- **Networks**: frontend, backend
|
|
- **Health Check**: `traefik healthcheck` (30s interval)
|
|
- **Configuration**:
|
|
- `/etc/traefik/traefik.yml` - Main config
|
|
- `/etc/traefik/middleware.yml` - Middleware rules
|
|
- Dynamic routing via Docker labels
|
|
|
|
### Frontend (React SPA)
|
|
- **Technology**: React 18 + Vite + TypeScript
|
|
- **Build**: Multi-stage Dockerfile (node:20-alpine → nginx:alpine)
|
|
- **Port**: 3000 (internal)
|
|
- **Networks**: frontend
|
|
- **Dependencies**: Backend (API calls)
|
|
- **Health Check**: `curl http://localhost:3000` (30s interval)
|
|
- **Environment Variables**:
|
|
- `VITE_AUTH0_DOMAIN` - Auth0 tenant
|
|
- `VITE_AUTH0_CLIENT_ID` - Auth0 application ID
|
|
- `VITE_AUTH0_AUDIENCE` - API identifier
|
|
- `VITE_API_BASE_URL` - Backend API path (/api)
|
|
- **Routing**: Traefik routes non-/api paths to frontend
|
|
|
|
### Backend (Node.js API)
|
|
- **Technology**: Node.js 20 + Fastify + TypeScript
|
|
- **Build**: Production build in node:20-alpine
|
|
- **Port**: 3001 (internal)
|
|
- **Networks**: backend, database
|
|
- **Dependencies**: PostgreSQL, Redis, Platform
|
|
- **Health Check**: `http://localhost:3001/health` (30s interval)
|
|
- **Configuration**:
|
|
- `/app/config/production.yml` - Main config
|
|
- `/app/config/shared.yml` - Shared config
|
|
- `/run/secrets/*` - Secret files (K8s pattern)
|
|
- **Secrets**:
|
|
- `postgres-password` - Database password
|
|
- `platform-vehicles-api-key` - Platform service auth
|
|
- `auth0-client-secret` - Auth0 M2M secret
|
|
- `google-maps-api-key` - Maps API key
|
|
- **Storage**: `/app/data/documents` - Document uploads
|
|
|
|
### PostgreSQL (Primary Database)
|
|
- **Image**: postgres:15-alpine
|
|
- **Database**: motovaultpro
|
|
- **Port**: 5432 (exposed for development)
|
|
- **Networks**: database (internal)
|
|
- **User**: postgres
|
|
- **Password**: File-based secret (`/run/secrets/postgres-password`)
|
|
- **Health Check**: `pg_isready -U postgres` (10s interval)
|
|
- **Storage**: Named volume `mvp_postgres_data`
|
|
- **Schemas**:
|
|
- `public` - Application data (user-scoped)
|
|
- `vehicles` - Platform vehicle data (shared)
|
|
|
|
### Redis (Cache Layer)
|
|
- **Image**: redis:7-alpine
|
|
- **Port**: 6379 (exposed for development)
|
|
- **Networks**: database (internal)
|
|
- **Persistence**: AOF enabled (`--appendonly yes`)
|
|
- **Health Check**: `redis-cli ping` (10s interval)
|
|
- **Storage**: Named volume `mvp_redis_data`
|
|
- **Usage**:
|
|
- Vehicle data caching (year-based hierarchy)
|
|
- Session storage (future)
|
|
- Rate limiting (future)
|
|
|
|
### Platform (Vehicle Data Service)
|
|
- **Technology**: Python 3.11 + FastAPI
|
|
- **Build**: Custom Dockerfile from `mvp-platform-services/vehicles/`
|
|
- **Port**: 8000 (internal)
|
|
- **Networks**: backend, database
|
|
- **Dependencies**: PostgreSQL, Redis
|
|
- **Health Check**: `wget http://localhost:8000/health` (30s interval)
|
|
- **Configuration**:
|
|
- `/app/config/production.yml` - Service config
|
|
- `/app/config/shared.yml` - Shared config
|
|
- **Secrets**:
|
|
- `postgres-password` - Shared database access
|
|
- **Purpose**:
|
|
- Vehicle make/model/trim/engine data
|
|
- VIN decoding (planned)
|
|
- Standardized vehicle information
|
|
|
|
## Platform Service Integration
|
|
|
|
### Current Architecture
|
|
The Platform service is a **separate Python container** that provides vehicle data capabilities to the backend. This separation exists for:
|
|
- **Technology Independence**: Python ecosystem for data processing
|
|
- **Specialized Caching**: Year-based hierarchical caching strategy
|
|
- **Resource Isolation**: Independent scaling and monitoring
|
|
|
|
### Shared Infrastructure
|
|
- **Database**: Both Backend and Platform use `mvp-postgres`
|
|
- **Cache**: Both services share `mvp-redis`
|
|
- **Network**: Connected via `backend` and `database` networks
|
|
- **Secrets**: Share database credentials
|
|
|
|
### Communication Pattern
|
|
```javascript
|
|
// Backend calls Platform via internal HTTP
|
|
const response = await fetch('http://mvp-platform:8000/api/v1/vehicles/makes?year=2024', {
|
|
headers: {
|
|
'Authorization': `Bearer ${platformApiKey}`
|
|
}
|
|
});
|
|
```
|
|
|
|
### Future Evolution
|
|
**Planned**: Absorb Platform service into Backend as a feature capsule
|
|
- Timeline: Post-MVP phase
|
|
- Approach: Rewrite Python service in Node.js
|
|
- Location: `backend/src/features/vehicle-platform/`
|
|
- Benefits: Simplified deployment, shared codebase, reduced containers
|
|
|
|
## Feature Capsule Architecture
|
|
|
|
### Organization Pattern
|
|
Features are **self-contained modules** within the backend service at `backend/src/features/[name]/`:
|
|
|
|
```
|
|
backend/src/features/
|
|
├── vehicles/ # Vehicle management
|
|
├── fuel-logs/ # Fuel tracking
|
|
├── maintenance/ # Service records
|
|
├── stations/ # Gas station locations
|
|
└── documents/ # Document storage
|
|
```
|
|
|
|
### Feature Structure
|
|
Each feature follows a standardized structure:
|
|
```
|
|
feature-name/
|
|
├── README.md # Feature documentation
|
|
├── api/ # Route handlers
|
|
├── domain/ # Business logic
|
|
├── data/ # Data access layer
|
|
├── migrations/ # Database migrations
|
|
├── tests/ # Unit and integration tests
|
|
├── events/ # Event handlers (optional)
|
|
└── external/ # External service integrations (optional)
|
|
```
|
|
|
|
### Current Features
|
|
|
|
#### Vehicles
|
|
- **Purpose**: Vehicle inventory management
|
|
- **Tables**: `vehicles` (user-scoped)
|
|
- **Integration**: Platform service for make/model/trim data
|
|
- **Endpoints**: CRUD + dropdown data
|
|
|
|
#### Fuel Logs
|
|
- **Purpose**: Fuel purchase and efficiency tracking
|
|
- **Tables**: `fuel_logs` (user-scoped)
|
|
- **Integration**: Stations feature for location data
|
|
- **Endpoints**: CRUD + analytics
|
|
|
|
#### Maintenance
|
|
- **Purpose**: Service and maintenance record tracking
|
|
- **Tables**: `maintenance_records` (user-scoped)
|
|
- **Integration**: Vehicles feature for vehicle data
|
|
- **Endpoints**: CRUD + reminders
|
|
|
|
#### Stations
|
|
- **Purpose**: Gas station location and pricing
|
|
- **Tables**: `stations` (user-scoped)
|
|
- **Integration**: Google Maps API
|
|
- **Endpoints**: Search + favorites
|
|
|
|
#### Documents
|
|
- **Purpose**: Document storage and retrieval
|
|
- **Tables**: `documents` (user-scoped)
|
|
- **Storage**: Filesystem (`/app/data/documents/`)
|
|
- **Endpoints**: Upload + download + delete
|
|
|
|
## Data Flow
|
|
|
|
### Authentication via Auth0 JWT
|
|
```
|
|
1. Frontend → Auth0 Login
|
|
2. Auth0 → JWT Token (contains user_id in 'sub' claim)
|
|
3. Frontend → Backend (Authorization: Bearer <JWT>)
|
|
4. Backend → Validate JWT:
|
|
- Verify signature via Auth0 JWKS endpoint
|
|
- Check issuer: https://{AUTH0_DOMAIN}/
|
|
- Check audience: {AUTH0_AUDIENCE}
|
|
5. Backend → Extract user_id from token.sub
|
|
6. Backend → Execute user-scoped queries
|
|
```
|
|
|
|
### User ID Extraction
|
|
```typescript
|
|
// From auth.plugin.ts
|
|
await request.jwtVerify(); // Validates and populates request.user
|
|
const userId = request.user.sub; // Auth0 user ID (e.g., "auth0|123456")
|
|
```
|
|
|
|
### User-Scoped Data Access
|
|
All application data is isolated by `user_id`:
|
|
|
|
```sql
|
|
-- Example: Get user's vehicles
|
|
SELECT * FROM vehicles WHERE user_id = $1;
|
|
|
|
-- Example: Create fuel log (user_id inserted)
|
|
INSERT INTO fuel_logs (user_id, vehicle_id, gallons, cost, ...)
|
|
VALUES ($1, $2, $3, $4, ...);
|
|
|
|
-- Example: Soft delete (preserves audit trail)
|
|
UPDATE vehicles
|
|
SET deleted_at = NOW()
|
|
WHERE id = $1 AND user_id = $2;
|
|
```
|
|
|
|
### Data Isolation Strategy
|
|
- **Foreign Keys**: All user data tables have `user_id` foreign key
|
|
- **Query Scoping**: All queries filtered by authenticated user's ID
|
|
- **Soft Deletes**: `deleted_at` timestamp for audit trail
|
|
- **No Cascade Deletes**: Prevents accidental data loss
|
|
- **Index Strategy**: Composite indexes on (`user_id`, primary key)
|
|
|
|
### Cross-Feature Data Flow
|
|
```
|
|
1. User creates vehicle (Vehicles feature)
|
|
2. User logs fuel purchase (Fuel Logs feature)
|
|
- References vehicle_id
|
|
- References station_id (Stations feature)
|
|
3. User adds maintenance record (Maintenance feature)
|
|
- References vehicle_id
|
|
- Can attach documents (Documents feature)
|
|
```
|
|
|
|
## Configuration Management
|
|
|
|
### Kubernetes-Style Patterns
|
|
The application uses Kubernetes-inspired configuration patterns:
|
|
|
|
**ConfigMaps** (YAML files):
|
|
- `/app/config/production.yml` - Service-specific config
|
|
- `/app/config/shared.yml` - Cross-service config
|
|
|
|
**Secrets** (File-based):
|
|
- `/run/secrets/postgres-password` - Database credentials
|
|
- `/run/secrets/platform-vehicles-api-key` - Service auth
|
|
- `/run/secrets/auth0-client-secret` - OAuth credentials
|
|
- `/run/secrets/google-maps-api-key` - External API keys
|
|
|
|
### Environment Variables
|
|
Used for service references and runtime configuration:
|
|
```yaml
|
|
NODE_ENV: production
|
|
CONFIG_PATH: /app/config/production.yml
|
|
SECRETS_DIR: /run/secrets
|
|
DATABASE_HOST: mvp-postgres
|
|
REDIS_HOST: mvp-redis
|
|
PLATFORM_VEHICLES_API_URL: http://mvp-platform:8000
|
|
```
|
|
|
|
## Development Workflow
|
|
|
|
### Container Commands (via Makefile)
|
|
```bash
|
|
make start # Start all 6 containers
|
|
make stop # Stop all containers
|
|
make restart # Restart all containers
|
|
make rebuild # Rebuild and restart containers
|
|
make logs # View all container logs
|
|
make logs-backend # View backend logs only
|
|
make clean # Remove containers and volumes
|
|
```
|
|
|
|
### Health Monitoring
|
|
```bash
|
|
# Check container health
|
|
docker ps
|
|
|
|
# View specific service logs
|
|
docker logs mvp-backend -f
|
|
docker logs mvp-platform -f
|
|
docker logs mvp-postgres -f
|
|
|
|
# Test health endpoints
|
|
curl http://localhost:3001/health # Backend
|
|
curl http://localhost:8000/health # Platform
|
|
```
|
|
|
|
### Database Access
|
|
```bash
|
|
# PostgreSQL shell
|
|
make db-shell-app
|
|
|
|
# Execute SQL
|
|
docker exec -it mvp-postgres psql -U postgres -d motovaultpro
|
|
|
|
# View Redis data
|
|
docker exec -it mvp-redis redis-cli
|
|
```
|
|
|
|
## Security Architecture
|
|
|
|
### Authentication & Authorization
|
|
- **Provider**: Auth0 (OAuth 2.0 / OpenID Connect)
|
|
- **Token Type**: JWT (JSON Web Token)
|
|
- **Validation**: JWKS-based signature verification
|
|
- **Token Cache**: 1 hour TTL for Auth0 public keys
|
|
- **Issuer Validation**: Prevents token spoofing
|
|
- **Audience Validation**: API-specific tokens only
|
|
|
|
### Protected Resources
|
|
All API endpoints require valid JWT except:
|
|
- `/health` - Health check (priority routing)
|
|
- `/api/health` - API health check (bypass auth)
|
|
|
|
### Data Security
|
|
- **VIN Handling**: Check digit validation, no VIN in logs
|
|
- **User Isolation**: Queries filtered by authenticated `user_id`
|
|
- **Soft Deletes**: Audit trail preservation
|
|
- **No Cascading Deletes**: Manual data cleanup only
|
|
- **Encrypted Connections**: PostgreSQL SSL/TLS
|
|
|
|
### Infrastructure Security
|
|
- **Non-root Containers**: All services run as non-root users
|
|
- **Network Isolation**: Internal database network
|
|
- **Secret Management**: File-based secrets (K8s pattern)
|
|
- **No Hardcoded Credentials**: Environment and file injection only
|
|
|
|
## Deployment Strategy
|
|
|
|
### Production Environment
|
|
- **Container Orchestration**: Docker Compose (current) → Kubernetes (future)
|
|
- **Service Discovery**: Traefik with Docker provider
|
|
- **TLS**: Automatic certificate management via Traefik
|
|
- **Scaling**: Single instance per service (MVP phase)
|
|
- **Monitoring**: Container health checks + log aggregation
|
|
|
|
### Zero-Downtime Deployment
|
|
```bash
|
|
# Build new images
|
|
make rebuild
|
|
|
|
# Traefik handles traffic routing during restart
|
|
# Health checks ensure services are ready before traffic
|
|
```
|
|
|
|
## Observability
|
|
|
|
### Logging Strategy
|
|
- **Format**: Structured JSON logging
|
|
- **Levels**: ERROR, WARN, INFO, DEBUG
|
|
- **Context**: Request ID, User ID (truncated), Service name
|
|
- **Aggregation**: Docker logs → Future: ELK/Loki stack
|
|
|
|
### Health Checks
|
|
Every service exposes health endpoints:
|
|
- **Backend**: `GET /health` - Database + Redis connectivity
|
|
- **Platform**: `GET /health` - Database + Redis connectivity
|
|
- **Frontend**: Nginx status check
|
|
- **PostgreSQL**: `pg_isready`
|
|
- **Redis**: `redis-cli ping`
|
|
|
|
### Metrics (Future)
|
|
- Request rate and latency
|
|
- Database query performance
|
|
- Cache hit rates
|
|
- Error rates by endpoint
|
|
|
|
## Performance Considerations
|
|
|
|
### Caching Strategy
|
|
- **Vehicle Data**: Year-based hierarchical caching in Redis
|
|
- **JWT Keys**: 1-hour cache for Auth0 public keys
|
|
- **Database Queries**: Indexed by (`user_id`, primary key)
|
|
|
|
### Database Optimization
|
|
- **Connection Pooling**: Fastify PostgreSQL plugin
|
|
- **Prepared Statements**: SQL injection prevention + performance
|
|
- **Indexes**: Composite indexes on frequently queried columns
|
|
- **Query Scoping**: All queries filtered by `user_id` for data locality
|
|
|
|
### Horizontal Scaling (Future)
|
|
- **Stateless Services**: Backend and Platform can scale horizontally
|
|
- **Shared State**: PostgreSQL and Redis as centralized state
|
|
- **Load Balancing**: Traefik distributes traffic across instances
|
|
|
|
## Related Documentation
|
|
|
|
- Feature Documentation: `backend/src/features/{feature}/README.md`
|
|
- Platform Architecture: `docs/PLATFORM-SERVICES.md`
|
|
- Security Details: `docs/SECURITY.md`
|
|
- Database Schema: `docs/DATABASE-SCHEMA.md`
|
|
- Testing Guide: `docs/TESTING.md`
|
|
- Vehicles API: `docs/VEHICLES-API.md`
|
|
- Development Commands: `Makefile`
|