mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-08-07 19:56:18 -04:00
Add local-first refactor foundation
This commit is contained in:
195
documentations/refactor/03-database-design.md
Normal file
195
documentations/refactor/03-database-design.md
Normal file
@@ -0,0 +1,195 @@
|
||||
# Database Design
|
||||
|
||||
## Summary
|
||||
|
||||
SQLite should become the local source of truth for historical and completed
|
||||
session data. The design should store both raw source payloads and normalized
|
||||
domain rows. Raw payloads preserve source fidelity and make reprocessing
|
||||
possible. Normalized rows power fast product queries, analytics, and stable Web
|
||||
screens.
|
||||
|
||||
## Storage Strategy
|
||||
|
||||
Use two layers:
|
||||
|
||||
1. Raw source storage.
|
||||
- Preserve fetched payloads exactly enough to reprocess later.
|
||||
- Track source, endpoint/topic, parameters, fetch time, status, and errors.
|
||||
2. Normalized domain tables.
|
||||
- Queryable application data keyed by F1 identifiers.
|
||||
- Built from successful source payloads.
|
||||
- Safe to upsert idempotently.
|
||||
|
||||
## Raw Payload Tables
|
||||
|
||||
Candidate tables:
|
||||
|
||||
- `source_payloads`
|
||||
- `id`
|
||||
- `source`
|
||||
- `resource`
|
||||
- `request_key`
|
||||
- `url_or_topic`
|
||||
- `params_json`
|
||||
- `payload_json`
|
||||
- `fetched_at`
|
||||
- `status`
|
||||
- `error`
|
||||
- `schema_version`
|
||||
|
||||
- `ingestion_runs`
|
||||
- `id`
|
||||
- `scope_type`
|
||||
- `scope_key`
|
||||
- `started_at`
|
||||
- `finished_at`
|
||||
- `status`
|
||||
- `refresh`
|
||||
- `summary_json`
|
||||
|
||||
- `ingestion_items`
|
||||
- `id`
|
||||
- `run_id`
|
||||
- `dataset`
|
||||
- `meeting_key`
|
||||
- `session_key`
|
||||
- `status`
|
||||
- `source`
|
||||
- `started_at`
|
||||
- `finished_at`
|
||||
- `error`
|
||||
|
||||
## Normalized Domain Tables
|
||||
|
||||
Core calendar/session tables:
|
||||
|
||||
- `meetings`
|
||||
- `sessions`
|
||||
- `circuits`
|
||||
|
||||
Participant tables:
|
||||
|
||||
- `drivers`
|
||||
- `session_drivers`
|
||||
- `teams` or team snapshots by season/session.
|
||||
|
||||
Classification and standings:
|
||||
|
||||
- `session_results`
|
||||
- `starting_grids`
|
||||
- `driver_championship_standings`
|
||||
- `constructor_championship_standings`
|
||||
|
||||
Race/session analysis:
|
||||
|
||||
- `laps`
|
||||
- `stints`
|
||||
- `pit_stops`
|
||||
- `positions`
|
||||
- `intervals`
|
||||
- `race_control_messages`
|
||||
- `weather_samples`
|
||||
- `overtakes`
|
||||
|
||||
Telemetry and spatial data:
|
||||
|
||||
- `car_data_samples`
|
||||
- `location_samples`
|
||||
- `track_outlines`
|
||||
|
||||
Media metadata:
|
||||
|
||||
- `team_radio_messages`
|
||||
|
||||
Derived/read-model candidates:
|
||||
|
||||
- `session_dataset_status`
|
||||
- `race_key_moments`
|
||||
- `driver_session_summaries`
|
||||
- `race_lap_snapshots`
|
||||
|
||||
Derived tables should be added only when query cost or UI complexity justifies
|
||||
them. Start with normalized source tables and build read models in Go unless
|
||||
performance argues otherwise.
|
||||
|
||||
## Provenance and Freshness
|
||||
|
||||
Each normalized dataset should be traceable to source ingestion metadata.
|
||||
|
||||
Track:
|
||||
|
||||
- Source: OpenF1, SignalR, static archive, manual, cache.
|
||||
- First ingested time.
|
||||
- Last ingested time.
|
||||
- Last successful refresh.
|
||||
- Last error.
|
||||
- Completion status.
|
||||
- Whether stale fallback was used.
|
||||
|
||||
This metadata supports the Data Library screen and makes partial data honest.
|
||||
|
||||
## Immutability Policy
|
||||
|
||||
Completed historical sessions:
|
||||
|
||||
- Treat as immutable after successful ingestion.
|
||||
- Do not refetch unless `--refresh` is explicitly requested.
|
||||
- Allow reprocessing from raw payloads if schema or read models change.
|
||||
|
||||
Current/future sessions:
|
||||
|
||||
- Treat as refreshable.
|
||||
- Allow opportunistic metadata fetches.
|
||||
- Avoid high-volume refreshes without explicit action.
|
||||
|
||||
Live sessions:
|
||||
|
||||
- SignalR is authoritative for live state.
|
||||
- Persist live data as an append-only event/snapshot stream after the live
|
||||
bridge is extracted.
|
||||
- Keep live data separate from normalized post-session OpenF1 records until
|
||||
reconciliation is designed.
|
||||
- Treat live data as the record of what was seen during the session, not as the
|
||||
corrected final historical truth.
|
||||
|
||||
## Migration And File Layout
|
||||
|
||||
The current project already creates a SQLite cache database for raw HTTP
|
||||
responses. The domain database should be introduced without breaking that cache.
|
||||
|
||||
Default stance:
|
||||
|
||||
- Existing cache tables are infrastructure, not product domain state.
|
||||
- New domain tables should be owned by `internal/store`.
|
||||
- A separate domain DB file is the lower-risk first implementation unless a
|
||||
schema design pass shows strong reasons to reuse the same file.
|
||||
- If the same file is reused, domain tables must be namespaced clearly and
|
||||
migrations must avoid touching the current `cache` table except through
|
||||
deliberate cache work.
|
||||
- Do not auto-migrate URL-keyed cache entries into domain rows.
|
||||
- Use explicit ingestion to populate the new domain tables.
|
||||
|
||||
## High-Volume Data
|
||||
|
||||
High-volume tables need careful indexing and retention decisions:
|
||||
|
||||
- `car_data_samples`
|
||||
- `location_samples`
|
||||
- `positions`
|
||||
- `intervals`
|
||||
- `laps` for full-season analysis
|
||||
|
||||
Initial policy:
|
||||
|
||||
- Ingest high-volume telemetry only when explicitly requested.
|
||||
- Keep Race Hub v1 focused on results, strategy, laps, race control, weather,
|
||||
positions, and track outlines.
|
||||
|
||||
## Research Questions
|
||||
|
||||
- Exact indexes for Race Hub, Live Replay, Driver Explorer, and Standings.
|
||||
- Whether `positions` and `intervals` should be downsampled or stored in full.
|
||||
- Whether `car_data_samples` and `location_samples` should be optional datasets.
|
||||
- How to map official F1 static archive sessions to OpenF1 `session_key`.
|
||||
- Whether to use SQLite FTS for race-control/team-radio search.
|
||||
- How to version schema migrations without adding unnecessary framework weight.
|
||||
Reference in New Issue
Block a user