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

Project, Don't Copy

~13 min · source-immutable, projection, recall, cqrs

Level 0Reel Novice
0 XP0/39 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"Ashen Reel may read projections. Future writes are typed Recall intents; never a parallel local transcript or marker database."

The Cache That Becomes a Second Truth

Ashen Reel shows a transcript rail beside the video — the spoken words of the current Recall release, highlighting the line at the playback position. The obvious optimization is to cache those transcripts locally: faster to load, works if Recall is slow, feels snappy. And it's a trap. The moment you persist a copy of the transcript, you have two owners of the same truth, and the day someone corrects a line in Recall, your cached copy is quietly, confidently wrong.

So Ashen Reel doesn't. It projects the transcript: it fetches the segments into session memory, uses them while the window is open, and keeps nothing. There is no transcripts table in Ashen Reel's database — on purpose. Next launch re-fetches from Recall; Recall stays the one and only owner.

A borrowed truth kept only in memory can't drift. The safest copy of someone else's data is the one you don't keep. Read it when you need it, use it, discard it. Nothing you didn't persist can ever go stale behind the owner's back.

The projection is a read model, deliberately amnesiac:

This is the read side of a clean CQRS split. Recall owns the write side — the canonical transcript, corrections, releases. Ashen Reel builds a read-only projection for display and throws it away. When the underlying truth changes, the projection is simply rebuilt on next read; it is never patched, because it was never authoritative. Separate who writes from who reads and the drift bugs vanish.

Corrections Get a New Identity, Not a Merge

Here's the elegant part. When a transcript is corrected in Recall, that isn't an edit to an existing thing — it's a new release with its own identity. Ashen Reel doesn't diff the old transcript against the new one and merge; it just fetches the new release id and shows it. A correction upstream becomes a fresh projection downstream, automatically. You never wrote reconciliation logic because the identity of a corrected release did the reconciling for you.

Future writes go home as typed intents, never a local shadow. Ashen Reel will eventually let you mark a moment or a watch-span — but those become typed intents sent back to Recall, which owns them. The player never grows a private markers table it later has to sync. If the endpoint to accept an intent doesn't exist yet, the feature waits (Track 7) rather than inventing a local store to fill the gap.

Code

The transcript rail is a projection — borrowed, used, discarded·swift
/// The transcript rail: Recall's truth, borrowed for THIS session only.
final class TranscriptRail {
    private var segments: [Segment] = []   // in-memory; dies with the window

    func load(forRelease id: ReleaseID) async {
        // Fetch from Recall's loopback endpoint into memory. We do NOT persist.
        // There is no `transcripts` table in Ashen Reel's SQLite -- on purpose.
        // Next launch re-fetches; Recall stays the single owner of the truth.
        segments = await recall.segments(forRelease: id)
    }

    // A correction in Recall = a NEW release id = a fresh fetch here.
    // We never diverge, because we never stored a copy to diverge FROM.
}

External links

Exercise

Find a place where your system caches or copies data owned by another system or service. Ask: what happens when the owner changes it? Do you have a sync/invalidation mechanism, and is it correct? Now consider whether an in-memory projection rebuilt on demand would serve the same UI without the persistent copy. For at least one case, sketch the amnesiac version and list the sync code you'd get to delete.
Hint
The smell is a local table whose rows are copies of another system's records, plus code that tries to keep them fresh. Ask whether you can drop the table and re-derive on read instead. If the data is display-only and you don't own it, a projection you throw away usually beats a copy you have to reconcile.

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.