← back to Charge And Explore
docs/architecture.md
112 lines
# Architecture
## Why native iOS (SwiftUI) first
The product leans hard on Core Location authorization states, MapKit, background
transitions, APNs, Keychain, Live Activities, and possibly CarPlay. Native
reduces bridge behavior, entitlement complexity, and review uncertainty. Swift
structured concurrency + actors give clean cancellation and shared-state
isolation for map search, provider fan-out, token refresh, and offline sync.
Combine only for long-lived publisher streams (e.g. a `CLLocationManager`
wrapper). React Native/Expo is a *second-stage* option only if Android must ship
fast and the team is much stronger in TypeScript.
## System diagram
```mermaid
flowchart LR
IOS[SwiftUI iOS App] -->|TLS + app session| API[API Gateway / BFF]
API --> AUTH[Identity and OAuth Service]
API --> REC[Recommendation Engine]
API --> TRIP[Trip and Routing Service]
API --> REPORTS[Reports and Moderation Service]
AUTH --> KMS[(Encrypted Token Vault)]
AUTH --> TESLA[Tesla Fleet API]
TEL[Tesla Fleet Telemetry] --> ING[Telemetry Ingress]
ING --> QUEUE[Event Queue]
QUEUE --> SESSION[Active Charging Session Service]
SESSION --> APNS[Apple Push Notification Service]
APNS --> IOS
REC --> PG[(PostgreSQL + PostGIS)]
TRIP --> PG
REPORTS --> PG
REC --> REDIS[(Redis Cache)]
ADAPTERS[Provider Adapter Layer] --> NREL[NREL]
ADAPTERS --> COMMERCIAL[HERE / TomTom]
ADAPTERS --> PLACES[Apple / Google / Yelp]
ADAPTERS --> MAPS[Apple / Mapbox / HERE]
ADAPTERS --> PG
```
## Backend stack
- TypeScript with Fastify or NestJS.
- PostgreSQL + PostGIS (station clustering, corridor search, dedup, walking-radius).
- Redis (result cache, idempotency locks, token-refresh coordination, rate limiting).
- Queue (SQS/BullMQ) for provider ingestion + deletion jobs.
- Object storage for moderated photos + provider-permitted images.
- KMS envelope encryption for Tesla refresh tokens + provider credentials.
- Separately deployed **Tesla telemetry ingress** with strict network policy + narrow write path.
- APNs provider service; OpenTelemetry traces + redacted structured logs.
- Internal web admin console (corrections, provenance, moderation, stale records,
provider health, deletion-job status).
**The iOS app calls only the first-party backend.** Map SDK public tokens may be
bundle-restricted; charging/places/Tesla/commercial secrets stay server-side.
## Data-ingestion + provenance flow
```mermaid
flowchart TD
SCHEDULE[Scheduled Import] --> ADAPTER[Provider Adapter]
ADAPTER --> VALIDATE[Schema and License Validation]
VALIDATE --> RAW[Permitted Raw Payload Store]
VALIDATE --> NORMALIZE[Normalize Fields and Units]
NORMALIZE --> MATCH[Spatial and Identity Matching]
MATCH -->|Known station| MERGE[Field-Level Merge]
MATCH -->|Uncertain| REVIEW[Duplicate Review Queue]
MATCH -->|New| INSERT[Create Canonical Station]
MERGE --> PROV[Write Field Provenance]
INSERT --> PROV
PROV --> SCORE[Freshness and Confidence]
SCORE --> CACHE[Invalidate Search Cache]
SCORE --> INDEX[Update PostGIS/Search Index]
USER[User Correction] --> MOD[Moderation]
MOD --> PROV
```
## Database schema outline
Core groups (see the spec for the full ~60-table list):
```
users · user_preferences · user_consents · privacy_requests · app_sessions
vehicle_authorizations · vehicle_profiles · vehicle_snapshots_ephemeral
telemetry_configurations · charging_sessions · charging_session_events_ephemeral
charging_stations · station_connectors · station_access_rules · station_amenities
places · place_categories · place_hours · station_place_links · walking_routes
provider_accounts · provider_records · provider_field_provenance · provider_licenses
trips · trip_routes · trip_stops · recommendation_runs · activity_itineraries
reviews · station_reports · photos · moderation_items · audit_events · deletion_jobs
```
Key indexes: PostGIS geography indexes on stations + places; provider/external-id
uniqueness; normalized operator + address; route-corridor spatial index; compound
`(station_id, category, open_at, walking_time)`.
## Caching
Static stations 1–7 days · place details/hours 1–30 days (per contract) · route
results minutes–hours · dynamic availability only within the provider's documented
freshness window. **Never** use Tesla's live vehicle endpoint as a cache-refresh
loop — use Fleet Telemetry.
## Offline "trip corridor package"
Encrypted, versioned bundle downloaded before a trip: route geometry +
alternatives, candidate stations + connectors, addresses/coords, selected place
names/hours/categories/walking durations, generated itineraries, attribution +
freshness, a few licensed thumbnails, and emergency fallback chargers. GRDB/SQLite
holds the offline read model; pending reports/saves/preferences queue with
idempotency keys and sync later.