CQRS and event sourcing (awareness)
Keep the events, derive the rest: every read model can be rebuilt, and every event you store is kept for good.
Predict first
Before you read: predict
A chat app's moderator is settling a dispute about room #ops and needs the room exactly as it stood at 14:05 yesterday: who was in it and what its topic said. Since then, members have left and joined, and the topic has changed.
Which store can show that?
The 30-second version
CQRS gives writes and reads separate models: commands go through one that checks the rules, and queries read models shaped for each screen. Event sourcing stores every change as an event, in order, and works out state by replaying them. Either works without the other. Read models lag, and replays repeat handlers.
Keep the events, derive the rest: every read model can be rebuilt, and every event you store is kept for good.
How it works
Step 1 of 14
In the kitchen
- the strength return and the ration list, both from the diary → two read models from one record (steps 1, 7)
Analogy: A battalion's war diary
Each day the adjutant writes the day's moves, arrivals and postings in ink. The strength return and the quartermaster's ration list are both drawn up from the diary.
| In the battalion | In the system |
|---|---|
| one inked entry per happening, in date order | an event, stored in order (step 3) |
| the strength return and the ration list, both from the diary | two read models from one record (steps 1, 7) |
| a later entry correcting a mistaken one | a correcting event (step 6) |
| a ration list thrown away and drawn up again from page one | a rebuild (step 8) |
| a historian's map of every move, drawn decades later | a new read model over history (step 14) |
| old diaries in yards, new ones in metres, every map drawn in metres | an upcaster (step 13) |
| brigade's copy of the strength return, a day behind | a read model that lags (step 11) |
In real terms: the log is the truth; every read model is one way of reading it.
Breaks down when: a historian can read the officers' letters to fill a gap, while a replay has only what each event recorded.
Contrasting case: the orderly room's chalked roll, rubbed out and rewritten each morning: today is there, yesterday is gone.
Examples, run for real
4.1 ExamHall: the disputed tab-hidden flag
EXH-4750: during an ECON-201 sitting, the proctor dashboard kept one row per attempt and changed it in place. Attempt a-88's row said "tab hidden 3"; the candidate disputed it. MR !5050 keeps each attempt's events as the record and builds the dashboard row as a projection of them.
4.1 EXH-4750 attempt a-88's row: answers changed 5 · tab hidden 3 · reconnects 1 · when each happened: not stored
4.1 MR !5050 attempt a-88: 12 events stored · row from a replay of event 1 onwards: answers changed 5 · tab hidden 3 · reconnects 1 · same as the live row: yes · tab hidden at 09:12, 09:31, 10:02
The row is now derived from the events (steps 3, 7); the dispute reads the events themselves, and the dashboard can be rebuilt at any time (step 8).
4.2 Chat: the two ideas apart
Room #lobby, one card on the room list, built two ways from the same five changes.
4.2 CQRS, no event sourcing: writes change the room tables; the card's read table is rewritten after each change · events stored 0 · read tables 1 · card "lunch · 2 members"
4.2 event sourcing, no CQRS: #lobby's events are the record, replayed for every read · events stored 5 · read tables 0 · card "lunch · 2 members"
The same card both ways: CQRS without events, and event sourcing without a read model (step 5).
Try it
Answer first, then check. Nothing is saved.
Question 1 of 7
In a mock interview you say a chat app's new per-room mention counts can be filled for the past by replaying its event log from launch. The interviewer points out that those counts are kept by the mention handler, which also sends a push to each user it counts, and asks what happens on that replay and what you would do.
Which answer fills every count for the past without pushing anyone?
Question 2 of 7
A chat app stores each message as a MessagePosted event. Its 1,000 stored version 1 events wrote at in the servers' local time, UTC+3; version 2, used from now on, writes at in UTC and changes nothing else. A new read model counting messages per UTC hour is built by replaying the log, and 20 more messages are posted after the change. The dispute tool checks each event against the hash recorded when it was written.
Which change gives every hour its right count while every hash still checks?
Question 3 of 7
A chat platform has three parts. Account settings: forms that save and show the same fields. The moderation console: bans, mutes and appeals each check many rules, its screens combine data from six places, and it is read about 200 times for every write. Analytics: one heavy query a month that slows the main database.
Which plan does Fowler's CQRS article lead you to?
Question 4 of 7
A teammate writes in a design review: "The proctor dashboard reads its own read model, so ExamHall is already event-sourced; and once we event-source the attempt audit trail, every attempt screen must be split into commands and queries too." Write three sentences to the teammate: what CQRS is, what event sourcing is, and whether either needs the other.
What do you write to the teammate?
Question 5 of 7
On this lesson's projection model, run on a virtual clock in seconds, room #ops has 42 members, @spam among them. Its screen shows the member list and the member count from the room's read model, which a projection updates from the log in batches every 2 s. A moderator bans @spam; the ban command stores its event and returns it at t=0.5 s.
Which handling shows @spam gone at once and, once everything has caught up, the right member count?
Question 6 of 7
A chat platform's admins want a page listing, for each room, every topic change with the old topic, the new one, who made it and when. Rooms are otherwise edited through ordinary forms and read one room at a time, and nobody asks what a room looked like at an earlier moment.
Which design does Fowler's Event Sourcing article lead you to?
Question 7 of 7
A chat app's "messages per member" read model has counted every MessageEdited as a new message since launch; the log holds 2,000 events. To make restarts fast, a snapshot of the read model was saved at event 1,500. The handler is now fixed.
Which way leaves every member's count right while the log still holds only what members did?
Go deeper
Pro tip
- Save a read model and the position it reached in one write. If they are saved apart, a crash between the two makes the restart apply some events twice. When: building any projection (step 7). Verify:
Status: RAN.tip 500 events, a crash while saving after event 300 · counts and position saved apart: count after restart 600 · saved in one write: count after restart 500
Sources
Valid as of 2026-10-17.
- Martin Fowler, CQRS (bliki, 14 July 2011): https://martinfowler.com/bliki/CQRS.html ·
fowler-cqrs.txt. Named in the curriculum row. "At its heart is the notion that you can use a different model to update information than the model you use to read information."; "for most systems CQRS adds risky complexity"; "Having separate models raises questions about how hard to keep those models consistent, which raises the likelihood of using eventual consistency." Supports steps 1–2, 5 and 11. - Martin Fowler, Event Sourcing (12 December 2005; the page says it is a draft he has not updated): https://martinfowler.com/eaaDev/EventSourcing.html ·
fowler-event-sourcing.txt. Named in the curriculum row. "Event Sourcing ensures that all changes to application state are stored as a sequence of events."; "Complete Rebuild: We can discard the application state completely and rebuild it by re-running the events from the event log on an empty application."; "Temporal Query: We can determine the application state at any point in time."; "Bug fixes occur when you look at past processing and realize it was incorrect. For internal stuff this is really easy to fix, all you need to do is make the fix and reprocess the events." Supports steps 3–4, 8 and 14.
Upcasting (steps 12–13) is in neither page; it is executed, not quoted.
Related lessons
Checked against the tools and docs as of 2026-10-17.