Lesson 15 / 25

Levels of CQRS and When to Stop

Choose a level of CQRS that matches the problem and team.

A spectrum, not a switch

CQRS comes in levels. Level 1: separate command and query classes or handlers over the same database and tables, a code-organisation choice with almost no cost. Level 2: the same database but separate read structures, such as views, materialised views or denormalised tables updated in the same transaction or by triggers; reads are fast and still strongly consistent. Level 3: separate read stores (a search index, a document database, a cache) updated asynchronously from events or CDC; maximum flexibility and scale, with eventual consistency and more moving parts. Level 4: CQRS plus event sourcing, where the event store is the write model. Move up a level only when a concrete need appears: read load the write database cannot handle, query shapes the write model cannot serve, or independent scaling. Many successful systems stop at level 1 or 2.

Level 2: a materialised view in the same database

Fast reads without a second datastore; refresh strategy decides freshness.

CREATE MATERIALIZED VIEW daily_sales AS
SELECT date_trunc('day', placed_at) AS day,
       currency,
       count(*)          AS orders,
       sum(total_minor)  AS revenue_minor
FROM   orders
WHERE  status <> 'CANCELLED'
GROUP  BY 1, 2;

CREATE UNIQUE INDEX ON daily_sales (day, currency);

-- refresh periodically without blocking readers (PostgreSQL)
REFRESH MATERIALIZED VIEW CONCURRENTLY daily_sales;

Count the moving parts

Each level adds infrastructure to deploy, monitor and back up, and new failure modes such as a stuck projection. Write down which problem the next level solves before adopting it.

Quick check: Which CQRS level introduces eventual consistency between writes and reads?

  • Separate handlers on the same tables
  • Database views refreshed in the same transaction
  • Separate read stores updated asynchronously from events
  • None of them
Answer

Separate read stores updated asynchronously from events — Asynchronous updates to separate stores mean reads can lag behind writes.