HWHeat Waves
    DashboardUtforsk
    Analyse
    Data Kilder
    • Design Rationale
    • Doc Map
    DocsSettings
    DashboardAtlasUtforsk
    Analyse
    Data Kilder
    1. Documentation
    2. Design rationale and key decisions

    Loading documentation...

    Design rationale and key decisions

    Architectural and technology choices for HeatWaves — product and engineering context; canonical contracts live under fire-data-schema and ingestion docs.

    Project Rationale & Key Decisions

    Introduction

    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.


    1. Framework Choice: Next.js 16

    Decision:
    Next.js 16 (App Router) on React 19 is the primary framework.

    Rationale:

    • Modern React-based framework with strong support for SSR (Server-Side Rendering), SSG (Static Site Generation), and React Server Components.
    • App Router route groups ((main), (auth-pages), (docs)) provide a clean separation of product, auth, and documentation surfaces.
    • Built-in API routes, server actions, and Turbopack development build.
    • Node >= 20.9.0 baseline aligned with Next 16 requirements.

    Callout:
    Next.js enables both static and dynamic rendering, optimizing for performance and SEO.


    2. Backend & Database: Supabase

    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:

    • Built on top of PostgreSQL, providing a robust, scalable, and familiar database.
    • Integrated authentication with row-level security.
    • Easy integration with TypeScript via generated types (npm run types:gen:fire-data).
    • Open-source and developer-friendly.

    Callout:
    Executable schema truth (tables, RLS, keys, triggers, RPCs) lives in fire-data-schema/postgres-contract-rls-and-keys.md; this rationale does not duplicate those contracts.


    3. UI Component Library: shadcn/ui

    Decision:
    shadcn/ui is the primary UI primitives layer, composed on Radix UI and styled with Tailwind CSS.

    Rationale:

    • Accessible, composable components owned in-tree rather than imported as a black-box library.
    • Tailwind keeps design tokens and theming consistent across product, auth, and docs surfaces.

    4. Charting Libraries: Recharts & Apache ECharts

    Decision:

    • Recharts is the default chart library across shipped chart surfaces (/dashboard, /explore, /simple, /atlas).
    • Apache ECharts (via 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).
    • shadcn/ui covers non-chart UI primitives (forms, dialogs, tables, Progress, etc.) on top of Radix and Tailwind.

    Rationale:

    • Recharts is composable, declarative, and integrates cleanly with the shadcn Chart primitive (components/ui/chart.tsx) for consistent theming.
    • Apache ECharts handles dense interactive scenarios (sankey, heatmaps, hierarchy morphs on /dashboard and /atlas) that Recharts does not cover well.
    • shadcn/ui provides accessible primitives and design tokens compatible with Tailwind, avoiding a separate component framework.

    Callout:
    Authoritative per-area chart inventory lives in application-architecture/charts-libraries-by-area.md; update that file when chart stacks change.


    5. Folder Structure & Modularity

    Decision:
    Adopted a modular, Next.js-aligned folder structure.

    Rationale:

    • Clear separation of concerns (e.g., /app, /components, /lib, /hooks, /types, /docs).
    • Promotes maintainability and scalability.
    • Follows Next.js best practices for App Router, layouts, and route groups.
    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"]

    6. Type Safety: TypeScript

    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:

    • Type safety from database to UI without hand-written DTOs.
    • RPC argument and return types stay in sync with the Postgres contract.

    7. Data Fetching & Caching Strategy

    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.

    • Server reads — get_fire_data and probe_slice_coverage RPCs, called from lib/data-model/queries.ts.
    • Server cache — '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.
    • Invalidation — 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.
    • Client cache — TanStack React Query (configured in app/providers.tsx) for Explore ingest-run monitoring: infinite run table, run detail sheet, and post-undo cache invalidation.
    • Cold-ingest gate — Explore computes a slice_hash, probes coverage, and only triggers /api/admin/ingest for slices that are genuinely missing; adapter writes then invalidate the relevant tags.

    Rationale:

    • Server-cached aggregates over a single canonical RPC keep chart pages fast and consistent.
    • Tag invalidation ties cache freshness directly to ingest events, avoiding time-based heuristics.
    • React Query covers the few surfaces that genuinely need client-side caching, polling, or infinite pagination, without imposing a second data layer on simple read pages.

    Canonical detail (no duplication here):

    • application-architecture/read-path-get-fire-data-and-catalog-caching.md
    • application-architecture/explore-fire-data-slice-probe-and-ingest.md
    • ingestion/pipeline-slices-idempotency-and-triggers.md

    8. Performance & UX Optimizations

    Key Points:

    • React Server Components are the default rendering model; client components are introduced only where interactivity requires them.
    • Tag-based cache invalidation (see §7) keeps server reads cheap and consistent without time-based polling.
    • Slice-hash idempotent ingest avoids duplicate work across repeated Explore visits and the daily cron — see ingestion/pipeline-slices-idempotency-and-triggers.md for the chunked RPC upsert path.
    • Code splitting via 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.
    • Bundle analysis is available via ANALYZE=true (see next.config.js).

    9. References

    • Next.js Documentation
    • Supabase Documentation
    • shadcn/ui
    • Recharts
    • Apache ECharts