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:
139
documentations/refactor/12-phase-4-local-first-web-api.md
Normal file
139
documentations/refactor/12-phase-4-local-first-web-api.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# Phase 4 Local-First Web API
|
||||
|
||||
## Purpose
|
||||
|
||||
Phase 4 makes the Web API start behaving like a local-first product. Phases 2
|
||||
and 3 created the domain store and explicit ingestion path; this phase adds
|
||||
read models that prefer local SQLite data and report data availability honestly.
|
||||
|
||||
This is still a backend phase. Do not start React yet.
|
||||
|
||||
## Manager Decision
|
||||
|
||||
Build one credible local-first Race Hub API slice before replacing the frontend.
|
||||
The current Web UI can keep working from the existing endpoints, but the backend
|
||||
should expose store-backed responses that a future React Race Hub can trust.
|
||||
|
||||
Phase 4 should add:
|
||||
|
||||
- store-backed read models for ingested meetings, sessions, drivers, results,
|
||||
and grid;
|
||||
- dataset/status metadata so the UI knows what is local, missing, or stale;
|
||||
- optional small API fallbacks only when explicitly requested;
|
||||
- tests for local-first behavior without network.
|
||||
|
||||
## Package Boundary
|
||||
|
||||
Prefer adding a backend read-model layer instead of embedding SQL inside HTTP
|
||||
handlers.
|
||||
|
||||
Recommended shape:
|
||||
|
||||
```text
|
||||
internal/query/
|
||||
racehub.go Race Hub read model assembly
|
||||
metadata.go dataset availability/source metadata
|
||||
query_test.go temp-db tests
|
||||
```
|
||||
|
||||
Then wire `internal/web` to use that layer.
|
||||
|
||||
If the implementation keeps the read layer inside `internal/web` temporarily,
|
||||
it must still avoid duplicating store SQL across handlers.
|
||||
|
||||
## Initial API Scope
|
||||
|
||||
Add a new Race Hub endpoint:
|
||||
|
||||
```text
|
||||
GET /api/v1/race-hub?session_key=9472
|
||||
```
|
||||
|
||||
Response should include:
|
||||
|
||||
- meeting;
|
||||
- session;
|
||||
- drivers;
|
||||
- session results enriched with driver/team fields;
|
||||
- starting grid enriched with driver/team fields;
|
||||
- dataset availability metadata.
|
||||
|
||||
Recommended metadata shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": "local",
|
||||
"session_key": 9472,
|
||||
"datasets": {
|
||||
"meeting": {"status": "available", "source": "local"},
|
||||
"session": {"status": "available", "source": "local"},
|
||||
"drivers": {"status": "available", "source": "local", "count": 20},
|
||||
"results": {"status": "missing", "source": "none", "count": 0},
|
||||
"starting_grid": {"status": "available", "source": "local", "count": 20}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Exact field names can vary, but the response must make missing datasets visible
|
||||
instead of silently returning empty app states.
|
||||
|
||||
## Existing Endpoint Policy
|
||||
|
||||
Do not rewrite every existing endpoint yet. It is enough to:
|
||||
|
||||
- add the new local-first Race Hub endpoint;
|
||||
- optionally make `/api/v1/meetings`, `/api/v1/sessions`, `/api/v1/drivers`,
|
||||
`/api/v1/results`, and `/api/v1/grid` read from local data when present;
|
||||
- preserve old OpenF1 behavior when local data is absent unless the request asks
|
||||
for local-only behavior.
|
||||
|
||||
Recommended query controls:
|
||||
|
||||
```text
|
||||
?source=local local only; no OpenF1 fallback
|
||||
?source=auto local first, existing OpenF1 fallback when missing
|
||||
```
|
||||
|
||||
Default should be conservative for existing endpoints. The new Race Hub endpoint
|
||||
can default to local-first with honest missing metadata.
|
||||
|
||||
## Server Wiring
|
||||
|
||||
`web.Server` currently only receives `*api.OpenF1Client`. Add an optional
|
||||
`*store.Store` or query service so Web mode can read the domain DB.
|
||||
|
||||
CLI/server behavior should remain simple:
|
||||
|
||||
```bash
|
||||
go run cmd/main.go --web
|
||||
go run cmd/main.go --web --db /path/to/boxbox.db
|
||||
```
|
||||
|
||||
If the DB does not exist or has no ingested data, Web mode should still start.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
Do not include these in Phase 4:
|
||||
|
||||
- React/Vite frontend setup.
|
||||
- replacing the current static Web UI;
|
||||
- automatic ingestion from Web browsing;
|
||||
- live SignalR persistence;
|
||||
- laps/stints/pits/weather/race-control read models unless the store schema is
|
||||
expanded and tested first.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
Phase 4 is complete when:
|
||||
|
||||
- a local-first Race Hub endpoint exists;
|
||||
- it can return ingested session data without OpenF1 calls;
|
||||
- it reports missing datasets explicitly;
|
||||
- Web mode can be pointed at a domain DB with `--db`;
|
||||
- offline tests cover the read model and HTTP handler behavior;
|
||||
- focused tests and build pass.
|
||||
|
||||
## Next Phase After This
|
||||
|
||||
Phase 5 is the first frontend implementation phase. That is the point to switch
|
||||
from Cursor to Claude for React/UI work.
|
||||
Reference in New Issue
Block a user