# Gas Stations Feature - Architecture

## Overview

The Gas Stations feature enables users to search for nearby gas stations using Google Maps Places API, save favorite stations with metadata, and integrate station data into fuel logging workflows. The feature follows MotoVaultPro's feature capsule pattern with complete isolation and user-scoped data.

## System Design

### High-Level Architecture

```
User Request (JWT)
    |
    v
API Layer (Controller + Routes)
    |
    v
Domain Layer (Service)
    |
    +---> External API (Google Maps Client)
    |         |
    |         v
    |     Circuit Breaker
    |         |
    |         v
    |     Google Places API
    |
    +---> Data Layer (Repository)
              |
              v
          PostgreSQL
```

### Component Responsibilities

#### API Layer
- **stations.controller.ts**: HTTP request/response handling
  - Request validation with Zod schemas
  - JWT authentication enforcement
  - User ID extraction from token
  - Error response formatting

- **stations.routes.ts**: Route definitions
  - POST /api/stations/search - Search nearby stations
  - POST /api/stations/save - Save station with metadata
  - GET /api/stations/saved - Get user's saved stations
  - GET /api/stations/saved/:placeId - Get specific saved station
  - PATCH /api/stations/saved/:placeId - Update saved station
  - DELETE /api/stations/saved/:placeId - Remove saved station

#### Domain Layer
- **stations.service.ts**: Core business logic
  - Search orchestration
  - Station caching strategy
  - User data isolation
  - Distance sorting
  - Data enrichment (combining saved + cached data)

- **stations.types.ts**: Type definitions
  - Request/response interfaces
  - Domain models
  - Validation rules

#### Data Layer
- **stations.repository.ts**: Database operations
  - Station cache CRUD operations
  - Saved stations CRUD operations
  - User-scoped queries
  - Soft delete implementation
  - Transaction support

#### External Integration
- **google-maps.client.ts**: Google Places API wrapper
  - Places Nearby Search
  - Place Details retrieval
  - Error handling
  - Response transformation

- **google-maps.circuit-breaker.ts**: Resilience pattern
  - 10s timeout per request
  - 50% error threshold triggers open
  - 30s reset timeout
  - Volume threshold: 10 requests minimum
  - State change logging

## Data Flow

### Search Flow

1. User requests nearby stations (latitude, longitude, radius)
2. Controller validates JWT and request body
3. Service calls Google Maps client through circuit breaker
4. Google Maps returns nearby stations
5. Service caches each station in PostgreSQL
6. Service sorts stations by distance
7. Controller returns enriched response

### Save Station Flow

1. User requests to save a station (placeId, metadata)
2. Controller validates JWT and request body
3. Service retrieves station from cache
4. Service inserts into saved_stations table (user_id scoped)
5. Service enriches saved record with cached data
6. Controller returns combined result

### List Saved Stations Flow

1. User requests their saved stations
2. Controller validates JWT
3. Service queries saved_stations by user_id
4. Service enriches each record with cached station data
5. Controller returns array of enriched stations

## External Dependencies

### Google Maps Places API

**Required APIs**:
- Places API (Nearby Search)
- Places API (Place Details)

**Authentication**: API key loaded from /run/secrets/google-maps-api-key

**Rate Limits**:
- 1,000 requests per day (free tier)
- Cost per additional 1,000 requests varies by API
- See GOOGLE-MAPS-SETUP.md for detailed pricing

**Error Handling**:
- Circuit breaker protects against cascading failures
- Graceful degradation when API unavailable
- User-friendly error messages

### PostgreSQL

**Tables Used**:
- station_cache: Temporary storage for Google Places results
- saved_stations: User's favorite stations with metadata

**Connection Pool**: Managed by core database service

## Caching Strategy

### Station Cache (PostgreSQL)

**Purpose**: Store Google Places results to avoid repeated API calls

**TTL**: 24 hours (auto-cleanup via scheduled job)

**Cache Key**: place_id (unique identifier from Google)

**Data Stored**:
- place_id, name, address
- latitude, longitude
- rating, photo_url
- created_at timestamp

**Eviction Policy**:
- Scheduled job runs every hour
- Deletes records older than 24 hours
- No manual invalidation

**Why PostgreSQL, not Redis?**:
- Station data is relatively large (multiple fields)
- 24-hour retention doesn't require ultra-fast access
- Simplifies architecture (no Redis key management)
- Allows SQL queries (future analytics)

### Saved Stations (PostgreSQL)

**Purpose**: Persistent storage of user favorites

**TTL**: Indefinite (user manages via delete)

**User Isolation**: All queries filter by user_id

**Soft Deletes**: deleted_at timestamp preserves referential integrity

## Circuit Breaker Pattern

### Configuration

```typescript
{
  timeout: 10000,              // 10s max wait
  errorThresholdPercentage: 50, // Open at 50% error rate
  resetTimeout: 30000,         // 30s before retry
  volumeThreshold: 10,         // Min requests before opening
  rollingCountTimeout: 10000   // 10s window for counting
}
```

### States

1. **CLOSED** (Normal Operation)
   - Requests pass through to Google Maps API
   - Errors are tracked in rolling window
   - Opens if error rate exceeds 50%

2. **OPEN** (Failure Mode)
   - Requests fail immediately without calling API
   - Returns null to caller
   - After 30s, transitions to HALF_OPEN

3. **HALF_OPEN** (Testing Recovery)
   - Single test request passes through
   - Success closes circuit
   - Failure reopens circuit

### Monitoring

All state changes logged via Winston:
- `Circuit breaker opened for Google Maps {operation}`
- `Circuit breaker half-open for Google Maps {operation}`
- `Circuit breaker closed for Google Maps {operation}`

## Database Schema

### station_cache

```sql
CREATE TABLE station_cache (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  place_id VARCHAR(255) UNIQUE NOT NULL,
  name VARCHAR(255) NOT NULL,
  address VARCHAR(500),
  latitude DECIMAL(10, 8) NOT NULL,
  longitude DECIMAL(11, 8) NOT NULL,
  rating DECIMAL(2, 1),
  photo_url TEXT,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

  INDEX idx_place_id (place_id),
  INDEX idx_created_at (created_at)
);
```

**Indexes**:
- place_id: Fast lookup during save/enrich operations
- created_at: Efficient cleanup job queries

### saved_stations

```sql
CREATE TABLE saved_stations (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  user_id VARCHAR(255) NOT NULL,
  place_id VARCHAR(255) NOT NULL,
  nickname VARCHAR(255),
  notes TEXT,
  is_favorite BOOLEAN DEFAULT false,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  deleted_at TIMESTAMP,

  UNIQUE(user_id, place_id),
  INDEX idx_user_id (user_id),
  INDEX idx_place_id (place_id),
  INDEX idx_deleted_at (deleted_at)
);
```

**Indexes**:
- user_id: Fast user-scoped queries (most common operation)
- place_id: Fast lookups when updating/deleting
- deleted_at: Efficient soft delete filtering
- UNIQUE(user_id, place_id): Prevents duplicate saves

**Constraints**:
- user_id NOT NULL: All data must be user-scoped
- place_id NOT NULL: Must reference valid Google Place
- UNIQUE constraint: One save per user per station

## Error Handling

### Validation Errors (400)

**Triggers**:
- Missing required fields
- Invalid latitude/longitude
- Invalid radius
- Malformed placeId

**Response Format**:
```json
{
  "error": "Validation failed",
  "details": [
    { "field": "latitude", "message": "Must be between -90 and 90" }
  ]
}
```

### Authentication Errors (401)

**Triggers**:
- Missing JWT token
- Expired token
- Invalid token signature

**Response Format**:
```json
{
  "error": "Unauthorized",
  "message": "Valid JWT token required"
}
```

### Not Found Errors (404)

**Triggers**:
- Saved station doesn't exist
- Station not in cache
- User doesn't own saved station

**Response Format**:
```json
{
  "error": "Not found",
  "message": "Saved station not found"
}
```

### External API Errors (502)

**Triggers**:
- Google Maps API timeout
- Google Maps API rate limit
- Circuit breaker open

**Response Format**:
```json
{
  "error": "Service unavailable",
  "message": "Unable to search stations. Please try again later."
}
```

### Server Errors (500)

**Triggers**:
- Database connection failure
- Unexpected exceptions
- Code bugs

**Response Format**:
```json
{
  "error": "Internal server error",
  "message": "An unexpected error occurred"
}
```

## Security

### Authentication

**Method**: JWT (Auth0)

**Enforcement**: All endpoints require valid JWT

**User Extraction**: User ID from token's `sub` claim

### Authorization

**User Data Isolation**: All queries filter by user_id

**Ownership Validation**:
- GET /saved/:placeId checks user_id matches
- PATCH /saved/:placeId checks user_id matches
- DELETE /saved/:placeId checks user_id matches

### SQL Injection Prevention

**Strategy**: Parameterized queries only

**Example**:
```typescript
// CORRECT (parameterized)
await pool.query(
  'SELECT * FROM saved_stations WHERE user_id = $1',
  [userId]
);

// NEVER DO THIS (concatenation)
await pool.query(
  `SELECT * FROM saved_stations WHERE user_id = '${userId}'`
);
```

### Secrets Management

**API Key Storage**: /run/secrets/google-maps-api-key (file mount)

**Access Pattern**: Read once at service startup

**Never Log**: Secrets never appear in logs

## Performance

### Expected Latencies

- **POST /search**: 500-1500ms (Google API call)
- **POST /save**: 50-100ms (database insert)
- **GET /saved**: 50-100ms (database query)
- **PATCH /saved/:id**: 50-100ms (database update)
- **DELETE /saved/:id**: 50-100ms (database delete)

### Optimization Strategies

1. **Cache Station Data**: Avoid repeated Google API calls
2. **Index User Queries**: Fast user_id filtering
3. **Sort in Memory**: Distance sorting after fetch (small datasets)
4. **Circuit Breaker**: Fail fast when API unavailable
5. **Connection Pooling**: Reuse database connections

### Scaling Considerations

- **Database**: Index tuning for large user bases
- **Google API**: Rate limiting and quota monitoring
- **Circuit Breaker**: Adjust thresholds based on traffic
- **Cache Cleanup**: Schedule during low-traffic periods

## Testing Strategy

### Unit Tests
- Service layer business logic
- Circuit breaker behavior
- Repository query construction
- Input validation

### Integration Tests
- Complete API workflows
- Database transactions
- Error scenarios
- User isolation

### External API Mocking
- Mock Google Maps responses
- Simulate API failures
- Test circuit breaker states

See TESTING.md for detailed testing guide.

## Future Enhancements

### Potential Improvements

1. **Real-time Fuel Prices**: Integrate GasBuddy or similar API
2. **Route Optimization**: Find cheapest stations along route
3. **Price Alerts**: Notify users of price drops
4. **Analytics Dashboard**: Spending trends, favorite stations
5. **Social Features**: Share favorite stations, ratings
6. **Offline Support**: Cache recent searches for offline access

### Breaking Changes

Any changes to API contracts require:
1. Version bump in API path (e.g., /api/v2/stations)
2. Frontend update coordination
3. Migration guide for existing data
4. Deprecation notice (minimum 30 days)

## Troubleshooting

### Common Issues

**Symptom**: "Unable to search stations"
**Cause**: Circuit breaker open or Google API key invalid
**Solution**: Check logs for circuit breaker state, verify API key

**Symptom**: "Station not found" when saving
**Cause**: Station not in cache (search first)
**Solution**: User must search before saving

**Symptom**: Slow search responses
**Cause**: Google API latency or rate limiting
**Solution**: Monitor circuit breaker metrics, check API quotas

**Symptom**: Duplicate saved stations
**Cause**: Race condition or UNIQUE constraint failure
**Solution**: Database constraint prevents duplicates, return 409 Conflict

## References

- API Documentation: `/backend/src/features/stations/docs/API.md`
- Testing Guide: `/backend/src/features/stations/docs/TESTING.md`
- Google Maps Setup: `/backend/src/features/stations/docs/GOOGLE-MAPS-SETUP.md`
- Feature README: `/backend/src/features/stations/README.md`
- Circuit Breaker Library: [opossum](https://nodeshift.dev/opossum/)
