Part VI β The Blueprint Β· Lesson 11.1
HLD β Analytics Dashboard (Google Analytics)
In one lineAggregate on the server, render only what fits on screen, and never ship a million rows to a browser.
Where you've seen itChanging the date range on a dashboard and watching each card refresh on its own instead of the page going blank.
Diagram
1β2 Β· SCOPE AND SCALE
in scope widget grid Β· filters Β· date range Β· drill-down Β· export
out data ingestion Β· ML insights Β· alerting config
scale billions of raw events Β· 10β20 widgets per dashboard
queries take 200msβ10s depending on range3 Β· THE PIPELINE β aggregation belongs on the server
raw events (billions)
β pre-aggregated by the warehouse
βΌ
ββββββββββββββββββ GET /metrics?metric=sessions
β Query service β &from=β¦&to=β¦&granularity=day&dims=country
βββββββββ¬βββββββββ
β returns ~365 points, not 40M rows
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββ
β Dashboard β
β ββββββββββββββββββββββββββββββββββββββββ β
β β KPI ββ KPI ββ time series β β each widget owns
β β 200ms ββ 200ms ββ 1.2s β β its own query,
β ββββββββββ€ββββββββββ€ββββββββββββββββββββ€ β loading state,
β β table ββ map ββ funnel 8s β³ β β and error state
β ββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββ
One slow widget must never block the other eleven.6a Β· DEEP DIVE β KEEPING THE UI RESPONSIVE
problem fix
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
40M rows to plot downsample server-side (LTTB) to
~1β2k points β visually identical
huge table server pagination + virtualization
filter change re-runs 12 debounce (10.15) + AbortController
queries per keystroke cancels superseded requests
heavy client-side math Web Worker, off the main thread (5.1)
slow query blocks the page per-widget suspense boundaries
same query from 3 widgets dedupe by cache key (6.9)6b Β· DEEP DIVE β FILTER STATE AND SHAREABILITY
URL is the source of truth (6.10)
/dashboard?from=2026-01-01&to=2026-03-31&country=IN&metric=sessions
β
βββΊ every widget derives its query key from these params
βββΊ a shared link reproduces the EXACT view
βββΊ back button steps through filter history
Cache by the full key so switching back to a previous range
is instant and costs no query.How it works
- Server aggregates; the client receives chart-ready series, never raw events.
- Each widget is independent β own query, own cache key, own loading and error state.
- Downsample before plotting; beyond a couple of thousand points, more data adds no visible information.
- Filters live in the URL, making every dashboard state shareable and back-button friendly.
- Virtualize tables and move any heavy computation into a Web Worker.
Trade-offs
| Choice | Cost |
|---|---|
| Server aggregation | Less client flexibility; far less data transferred |
| Client aggregation | Interactive slicing; only viable for small sets |
| Per-widget loading | More requests; no blank-page stalls |
| Long cache TTL | Instant re-renders; risk of stale numbers |
Interview angle
"A widget needs to chart 40 million rows. How?"
- It doesn't β the server aggregates to the granularity the chart can actually show, typically a few hundred points.
- Downsample with an algorithm that preserves visual shape (LTTB) rather than naive sampling, which hides spikes.
- Keep the widget independently loaded and cached by its query key so other cards render while it works.
Recap
- Aggregate server-side; the browser gets chart-ready series.
- Independent widgets: one slow query must not blank the dashboard.
- Filters in the URL, cached by key β shareable and instant on return.