C.W.K.
Stream
Lesson 01 of 04 · published

One Canonical State, Many Views

~10 min · projection, read-model, modes, single-source-of-truth

Level 0Cold Hearth
0 XP0/34 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"One canonical state, many views — never independent copies of reality."

Three Modes, One Store

Vesta carries a lifetime of crumbs and shows them through three primary modes, each a different way of looking at the same underlying data. Log is the landing surface: a reverse-chronological stream with the composer pinned to it — the shortest path from opening the app to dropping a fragment. Prose is the durable writing index: per-day assembly proposals and drafts, the render and edit surface, and the Rekindle handoff. Map is the geographic projection of where crumbs were left. These are the Waystone spine with the travel-only modes removed, and the crucial thing about all three is what they are not: they are not three databases. They are three projections of one canonical crumb store.

Every dated view is a question asked of that single store. The Log asks 'show me recent crumbs, newest first'. The Calendar asks 'how many crumbs on each day this month'. The Map asks 'where are these crumbs' points'. None of them owns a crumb; each renders the answer to a query. Add the sidebar's Today / Weekly / Monthly / Calendar navigation and it's the same story — every one of them projects the one crumb store from a different angle.

A view is a query, not a copy. The instant a view keeps its own stored copy of the data, you have two sources of truth — the store and the view — and they will drift the first time one updates without the other. Keep every view a projection that reads the canonical store on demand, and drift becomes structurally impossible: there's only ever one place the truth lives.

The Anti-Pattern This Avoids

The tempting shortcut, especially as a product grows, is to give each view its own table optimized for how it reads — a 'map table', a 'calendar table', a 'stream table' — and to write to all of them whenever a crumb changes. It feels fast, and it's a trap. Now every write has to update N places, every one of them is a chance to forget one, and the day they disagree you have a bug with no single correct answer. The crumb says one thing, the map table says another, and reconciling them is guesswork. Vesta refuses that shape: there is one canonical store, and every view is a read over it. A crumb changes in exactly one place, and all views reflect it because they all read the same truth.

Read-Optimized Is Fine — as a Derived Mirror

None of this means views can't be fast. It means speed comes from a derived read model, not a second source of truth. Vesta does keep a query-optimized mirror (that's the next lesson) — but it's explicitly derived from the canonical log and rebuildable at any time, so it can be as read-optimized as you like without ever becoming an authority that could contradict the source. The discipline isn't 'never optimize reads'; it's 'the read model is always downstream of the one truth, never a peer to it'. Optimize freely, as long as the optimization can be thrown away and rebuilt.

Code

Three modes, all reading one store·typescript
// ONE canonical store of crumbs
declare const crumbs: CrumbStore;

// each mode is a QUERY over it — none owns a crumb
const logView   = crumbs.query({ order: "target_date desc" });
const calendar  = crumbs.query({ groupBy: "target_date", count: true });
const mapView   = crumbs.query({ has: "location" }).map(toPoint);
const proseView = crumbs.query({ date }).then(assembleDay);

// a crumb changes in exactly ONE place:
crumbs.revise(id, { target_date });
// ...and every view reflects it, because they all read the same truth.
// No view holds a copy; there is nothing to keep in sync.

External links

Exercise

Find a feature where the same data is shown in several views — a list, a chart, a map, a summary. Check how it's stored: is there one source each view reads, or does each view have its own copy that something has to keep in sync? If it's the latter, sketch the version where all views project a single store, and identify which write-to-many-places bug you just deleted.
Hint
The smell is a write path that has to update several places at once to keep views consistent. Replace it with one canonical write and views that read on demand (or from a derived, rebuildable mirror). The bug you remove is the one where two views disagree and there's no principled way to say which is right.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.