Skip to content

PATTERN Cited by 4 sources

Async-projected read model

Async-projected read model is the operational shape of CQRS: a write-optimized source of truth (normalized relational DB, event log, document store) is the command side, and a separate, derived read-optimized store is built from it asynchronously. The read store may be a graph, a denormalized row set, a search index, an aggregate table, or a cache — whatever the query path actually needs. Reactive rebuilds keep it current.

Canonical shape

Writes                Async projection               Reads
  │                         │                          │
  ▼                         ▼                          ▼
Source of truth  ─►  transformer / rebuilder  ─►  Read model(s)
 (ops-friendly)    (triggered by writes                 ▲
                     or scheduled)                      │
                                                     Queries

The read model can be:

  • A graph — Canva's routing graph in Redis, rebuilt reactively on any relevant relational write.
  • A search index — Elasticsearch projection of primary-store rows.
  • An aggregate — warehouse table populated by a DBT model (see end-to-end-recompute).
  • A serving-tier row — the row an app API returns; see warehouse-unload-bridge for the OLAP→OLTP variant.

When to use it

You reach for this when:

  • The write shape and the read shape genuinely want to be different stores — relational for small edits vs. graph for traversal, transactional for correctness vs. columnar for scan.
  • The read workload is high-volume and the write workload is low-volume (Canva: supplier data rarely changes; routing queries run on every checkout).
  • The read model is fully derivable from the source of truth (rebuilding after loss is tolerable, because the source of truth is enough).
  • You want to A/B-test or upgrade the read representation without touching writers.

Implementation notes

  • Reactive trigger vs. schedule. Reactive (change data capture / event-driven projection) keeps the read model close to real-time; scheduled rebuilds are simpler but introduce explicit staleness. Canva picks reactive rebuild so supplier-config changes propagate into routing results quickly.
  • Whole-artifact rebuild vs. incremental patch. Small read models can be rebuilt from scratch on every trigger (simpler, no consistency bugs). Large ones need incremental update, which introduces "divergence from source" as a failure mode. Canva's per-region graph is small enough to publish as a whole artifact.
  • Source-of-truth invariant. The read model is never authored directly; if it's lost or corrupted, it's rebuilt. This is the disaster-recovery story and also the justification for not worrying about cache invalidation.
  • Shard the read model by the query dimension. Canva shards by destination region, matching the actual query shape (see concepts/geographic-sharding). A single giant read model is often the wrong answer.

Consistency model

Eventual consistency by default — there is a lag from write to read-model update. Whether this is acceptable is the first question to answer. For Canva's routing, supplier-config staleness measured in seconds is fine; for a billing-relevant read model, probably not.

Seen in

  • sources/2026-10-02-databricks-real-time-retail-intelligence-building-e-commerce-recommenda — pre-computed recommendations as a projected serving-tier read model. Databricks' retail recsys "Path A" is a textbook async-projected read model: the lakehouse (medallion Delta tables) is the write-optimized source of truth; a nightly Databricks Workflows job runs the full recommendation funnel offline for every active user and projects the result — a top-N ranked list (50–100 items per surface) — into Lakebase online tables keyed by user_id + surface. At request time the app does a plain KV lookup against the read model: "no model inference, no vector search, no feature assembly — just a direct read from Lakebase." The read model is a serving-tier row set, rebuilt on a nightly schedule rather than reactively — the staleness trade-off (previous-day signals) is explicitly accepted because long-term preferences evolve over days. Pairs with "Path B" real-time scoring for context that only exists at request time, and Path B falls back to this read model under latency pressure (patterns/cheap-approximator-with-expensive-fallback).

  • sources/2024-12-10-canva-routing-print-orders — relational operations DB → async reactive rebuild → per-region graph in Redis; the source of truth in relational makes graph loss tolerable (just rebuild).

  • sources/2026-09-08-databricks-build-durable-agents-with-temporal-and-lakebase — the read model as the application-facing view of a durable agent's state, projected from Temporal Event History. Databricks' underwriting agent keeps two stores for two consumers: Temporal's Event History drives replay (execution semantics, debugging), while Lakebase Postgres holds the read-optimized normalized view — agent_runs (status, request, token totals, recommendation), agent_messages (transcript), agent_tool_calls (args, status, result, timing), agent_review_decisions — indexed for the queries the app actually runs (cases by user+status, one transcript with evidence, reviews awaiting a person, aggregate metrics). The projection is maintained by Temporal Activities under at-least-once execution, so the read model can briefly lag ("the API can show older state while a write retries") before the accepted row becomes queryable — a textbook statement of the eventual-consistency window this pattern accepts. The Workflow validates review commands against its own state and ignores stale decisions even when the projection lags, so the lag is safe. (Source: sources/2026-09-08-databricks-build-durable-agents-with-temporal-and-lakebase)

  • concepts/cqrs — the general principle
  • warehouse-unload-bridge — the OLAP→OLTP variant
  • end-to-end-recompute — batch-style variant where the read model is rebuilt end-to-end per run
  • systems/canva-print-routing

Jira-automation leaderboard instance (2026-08-07)

Atlassian's Unleash PWA uses XP events as the source of truth and leaderboard rows as an asynchronously maintained projection. Jira Automation combines near-real-time updates, periodic incremental recomputation, and full rebuilds, so the view can be repaired or reinterpreted after an automation failure or scoring-rule change. (Source: sources/2026-08-07-atlassian-building-a-real-time-pwa-on-atlassians-own-stack)

Merged aliases

  • asynchronous-precomputed-report-batch-framework
  • decouple-reads-from-writes-at-storage-layer
  • partial-materialized-views- precomputed-relevance-graph
Last updated · 766 distilled / 2,225 read