Loading documentation...
Architectural and technology choices for HeatWaves — product and engineering context; canonical contracts live under fire-data-schema and ingestion docs.
This document outlines the major architectural and technology decisions made in the HEATWAVES project, providing context and reasoning for each choice. The goal is transparency, maintainability, and a shared understanding for people working on the codebase.
Decision:
Next.js 16 (App Router) on React 19 is the primary framework.
Rationale:
(main), (auth-pages), (docs)) provide a clean separation of product, auth, and documentation surfaces.>= 20.9.0 baseline aligned with Next 16 requirements.Callout:
Next.js enables both static and dynamic rendering, optimizing for performance and SEO.
Decision:
Supabase is used for database and authentication. All analytical reads and writes flow through Postgres RPCs in the dedicated fire_data schema (notably get_fire_data, probe_slice_coverage, compute_slice_hash, and the upsert_fact_*_batch family). Service-role keys are used from server routes for ingest writes; anon/SSR clients only read.
Rationale:
npm run types:gen:fire-data).Callout:
Executable schema truth (tables, RLS, keys, triggers, RPCs) lives infire-data-schema/postgres-contract-rls-and-keys.md; this rationale does not duplicate those contracts.
Decision:
shadcn/ui is the primary UI primitives layer, composed on Radix UI and styled with Tailwind CSS.
Rationale:
Decision:
/dashboard, /explore, /simple, /atlas).echarts-for-react and the local components/common/echarts/EChart.tsx wrapper) is used where Recharts is insufficient — primarily in data-heavy visualizations on shipped routes (not the discontinued /analytics chart UI).Progress, etc.) on top of Radix and Tailwind.Rationale:
Chart primitive (components/ui/chart.tsx) for consistent theming./dashboard and /atlas) that Recharts does not cover well.Callout:
Authoritative per-area chart inventory lives inapplication-architecture/charts-libraries-by-area.md; update that file when chart stacks change.
Decision:
Adopted a modular, Next.js-aligned folder structure.
Rationale:
/app, /components, /lib, /hooks, /types, /docs).graph TD Root["/"] --> App["/app"] Root --> Components["/components"] Root --> Lib["/lib"] Root --> Hooks["/hooks"] Root --> Contexts["/contexts"] Root --> Config["/config"] Root --> Public["/public"] Root --> Scripts["/scripts"] Root --> Supabase["/supabase"] Root --> Tests["/tests"] Root --> Types["/types"] Root --> Docs["/docs"]
Decision:
TypeScript is used throughout the codebase, with Supabase row and RPC types generated directly from the live schema via npm run types:gen:fire-data (output: types/fire_data.database.types.ts).
Rationale:
Decision:
Reads are server-driven through Postgres RPCs in the fire_data schema, wrapped in Next.js cache tags. Interactive client components use TanStack React Query.
get_fire_data and probe_slice_coverage RPCs, called from lib/data-model/queries.ts.'use cache' in lib/data-model/queries.ts with cacheTag / cacheLife: fact:${source}:${dim} and fact:all:all for chart reads, and catalog for source/dimension/category lookups. Enabled via cacheComponents: true in next.config.js. Auth I/O (getUser) sits behind Suspense in (main)/layout.tsx and app/(docs)/docs/layout.tsx.revalidateTag is called from POST /api/admin/ingest after successful slice ingest (per-slice fact:<source>:<dim> plus catalog), and from the Explore "undo" server action.app/providers.tsx) for Explore ingest-run monitoring: infinite run table, run detail sheet, and post-undo cache invalidation.slice_hash, probes coverage, and only triggers /api/admin/ingest for slices that are genuinely missing; adapter writes then invalidate the relevant tags.Rationale:
Canonical detail (no duplication here):
application-architecture/read-path-get-fire-data-and-catalog-caching.mdapplication-architecture/explore-fire-data-slice-probe-and-ingest.mdingestion/pipeline-slices-idempotency-and-triggers.mdKey Points:
ingestion/pipeline-slices-idempotency-and-triggers.md for the chunked RPC upsert path.next/dynamic for registry chart blocks on /dashboard (app/(main)/dashboard/_components/lazy-chart.tsx); /explore uses a single SeriesChartCard without dynamic imports — see application-architecture/dashboard-route.md.ANALYZE=true (see next.config.js).