Exercise 04 · Command → Write model → Event → Projection (Read model) → Query

CQRS: commands and queries

  • CQRS
  • write/read model
  • eventual consistency
  • projection lag
  • CQRS ≠ Event Sourcing

Principle

CQRS (Command Query Responsibility Segregation) separates two different jobs. A command changes state and enforces the rules. A query only reads and changes nothing. Each side has its own model, which can differ in shape and in storage.

Flow: Command → Write model (state change) → Event → Projection (Read model) → Query. The event is the link: the write side publishes it and the projection continuously builds the read model from it in exactly the shape the query needs.

The price is eventual consistency: the read model is always slightly behind. Projection lag can be measured (versions behind, milliseconds) and handled, for example with a version token from the command (read-your-writes).

CQRS ≠ Event Sourcing. CQRS is about separating writes and reads. Event Sourcing is about how the write side is stored (as events instead of an overwritten row). They can be combined, but neither requires the other: CQRS also works on top of an ordinary table (the “Write model without Event Sourcing” switch). And Event Sourcing can do without CQRS if you read by replaying the stream directly, which is exactly the problem in this step.

What to watch

  • Without CQRS: each query replays the whole stream (cost “read N×”). With CQRS: the query reads a ready-made view (1×).
  • Right after a command the read model is behind. The panel shows versions behind, milliseconds of lag and a chart.
  • With read-your-writes enabled, the query waits for the version from the command and returns current data.
  • Switch the write model to a table: the flow and lag stay the same, only the source of truth is not a stream.

Without the solution / With the solution

Without the solution

Run without CQRS (reading from the write model)

The adviser has made 250 changes. Every calculation query goes to the write model and replays the whole stream. Reads and writes share one structure.

Run in the simulator without the solution

With the solution

Run with CQRS and read-your-writes

The same 250 changes, but queries read a separate read model: cheap (1×) and independent of writes. The read model is briefly behind, which shows in the lag. A query after a command carries a version token and waits for the projection, so the user sees their own change.

Run in the simulator with the solution

What to do: Click “Run without CQRS (reading from the write model)”. What to look at: Each query read the whole stream (in the Queries panel: read 251×). The simulator needs JavaScript and runs as a separate application.

Try it in your own project

Copy the prompt into your coding agent (Claude Code, Copilot, Codex…) and run it in your project's repository.

Separate the calculator's writes and reads: commands change the write model, queries read a separate read model, and measure the lag between them.

Language
Variant
# Task for a coding agent: CQRS: commands and queries

Stack: Java. Mode: guided.
Goal: Separate the calculator's writes and reads: commands change the write model, queries read a separate read model, and measure the lag between them.

## Context of the existing code

You are working in an existing project (Java backend (typically Java 21, Spring Boot, JPA or JDBC). Use the build tool and style the project already has (Maven/Gradle, JUnit 5).).
Domain: a mortgage calculator that is gradually turning into an event-driven system (calculator, risk, approval, identity, account, documents, payout).
Prerequisite: the project has an event store for calculations (Event Sourcing) and a projection with a checkpoint that can be stopped and started. If not, first create a minimal one: events with a global position and a subscription that builds the read model from them and stores the position.

Read first, change nothing. Find and briefly summarise:
- Calculator endpoints that mix state changes and reads in one service or one model (DTO, entity, repository).
- Queries that currently assemble a view from several places, replay history or run expensive queries against the write table.
- An existing mechanism for publishing changes (events, outbox, broker) that can serve as the link between the sides.
- Places where the UI reads data right after saving (read-your-writes).
- Which of these parts already exist in the project and which are missing. Before writing your own infrastructure (read model, waiting for a version, metrics), check whether the project or organisation already has it as a ready-made library, and use it.

## Expected behaviour

A command changes state and enforces the rules, a query only reads; each side has its own model and the event is the link. The read model is briefly behind (eventual consistency): the lag can be measured and a query with the version from the command waits for the projection (read-your-writes). CQRS does not require Event Sourcing. Example: a calculation with 250 changes; without CQRS each query replays 250 events, with CQRS it reads one row of the read model.

## Specific task

Technical specifics (Java): Commands and queries as separate handlers (e.g. `CommandHandler` / `QueryHandler`); the read model in a separate table or index populated by an event listener. Keep the offset on top of the Spring Eventing/Kafka listener.

Proceed step by step. After point 1 (plan), write what you found and what you propose, and wait for confirmation:
1. Plan: list the commands (change state, return only an acknowledgement and version) and queries (change nothing). Draw the flow Command → Write model → Event → Projection → Query.
2. Introduce separate models: the write model holds the rules (invariants), the read model is shaped exactly for the query. Commands do not return the read model. Keep existing endpoints that return state after a write; add commands and queries as new ones (e.g. `/api/v2/commands/…`, `/api/v2/queries/…`).
3. Publish the change as an event (in one transaction with the write) and let the projection build the read model from it. With an event store, the event is the write itself; only describe the plain-table variant (row + outbox in one transaction) in the documentation.
4. The command returns a version. The query accepts `minVersion` and waits at most 1 s (configurable), then returns the latest state with `stale: true`. Without `minVersion` it immediately returns whatever the read model has.
5. Measure projection lag: the number of events behind (latest position in the event store − checkpoint) and the age of the oldest unprocessed event. Expose them as a metric, e.g. `GET /metrics` in Prometheus format. If the event store cannot return the latest position, add it.
6. Test: after a command, a query with the token returns the new version; without the token it may return an older one; with the projection stopped (`stop()` of the subscription) writes keep working and after `start()` the read model catches up.

Watch out for:
- Do not confuse CQRS with Event Sourcing: separating models does not require events as storage.
- Do not apply CQRS everywhere: for simple CRUD screens it only adds complexity. In the calculator it pays off for the calculation detail from the stream and for summaries; leave the CRUD form as it is.
- After saving, the UI must not assume the read model is immediately up to date (version token, optimistic display).
- A command must not read from the read model to decide on a write: the decision belongs to the write side.

## What you must not change

- The existing public API and the behaviour of the calculator endpoints that currently work.
- The database schema of existing tables (new tables/migrations are fine; renaming and deleting are not).
- Other people's tests, seed data and existing values. If a change is necessary, put it in a separate commit and state it. If a change to an event's shape or a new consumer necessarily changes an existing test, update it in a separate commit and explain why in the summary.
- Public contracts (APIs, event schemas) without backward compatibility.

## Done when

- Commands do not return the read model, queries change nothing; existing endpoints return the same responses as before.
- The read model can be deleted and rebuilt from events or from the write table.
- There is a test for stale reads, for read-your-writes and for a projection outage.
- Projection lag is measurable.
- The documentation states that CQRS does not require Event Sourcing, and the choice of write side is justified.
- JUnit 5 tests in the existing `src/test/java` structure pass with the project's build tool.
- In the summary, state: what you changed, what you assumed, what remains.
# Task for a coding agent: CQRS: commands and queries

Stack: Java. Mode: challenge.
Goal: Separate the calculator's writes and reads: commands change the write model, queries read a separate read model, and measure the lag between them.

## Context of the existing code

You are working in an existing project (Java backend (typically Java 21, Spring Boot, JPA or JDBC). Use the build tool and style the project already has (Maven/Gradle, JUnit 5).).
Domain: a mortgage calculator that is gradually turning into an event-driven system (calculator, risk, approval, identity, account, documents, payout).
Prerequisite: the project has an event store for calculations (Event Sourcing) and a projection with a checkpoint that can be stopped and started. If not, first create a minimal one: events with a global position and a subscription that builds the read model from them and stores the position.

Read first, change nothing. Find and briefly summarise:
- Calculator endpoints that mix state changes and reads in one service or one model (DTO, entity, repository).
- Queries that currently assemble a view from several places, replay history or run expensive queries against the write table.
- An existing mechanism for publishing changes (events, outbox, broker) that can serve as the link between the sides.
- Places where the UI reads data right after saving (read-your-writes).
- Which of these parts already exist in the project and which are missing. Before writing your own infrastructure (read model, waiting for a version, metrics), check whether the project or organisation already has it as a ready-made library, and use it.

## Expected behaviour

A command changes state and enforces the rules, a query only reads; each side has its own model and the event is the link. The read model is briefly behind (eventual consistency): the lag can be measured and a query with the version from the command waits for the projection (read-your-writes). CQRS does not require Event Sourcing. Example: a calculation with 250 changes; without CQRS each query replays 250 events, with CQRS it reads one row of the read model.

## Brief (Challenge)

Next to the existing endpoint, add a separate calculator read model built from events, with a version token for read-your-writes and a projection lag metric. Keep the existing endpoint running.

Technical specifics (Java): Commands and queries as separate handlers (e.g. `CommandHandler` / `QueryHandler`); the read model in a separate table or index populated by an event listener. Keep the offset on top of the Spring Eventing/Kafka listener.

Choose your own approach. The pitfalls in this topic are not listed: find them (concurrency, redelivery, ordering, failure midway) and for each write a test that would expose it. At the end, list which pitfalls you found and which you deliberately left open.

## What you must not change

- The existing public API and the behaviour of the calculator endpoints that currently work.
- The database schema of existing tables (new tables/migrations are fine; renaming and deleting are not).
- Other people's tests, seed data and existing values. If a change is necessary, put it in a separate commit and state it. If a change to an event's shape or a new consumer necessarily changes an existing test, update it in a separate commit and explain why in the summary.
- Public contracts (APIs, event schemas) without backward compatibility.

## Done when

- Commands do not return the read model, queries change nothing; existing endpoints return the same responses as before.
- The read model can be deleted and rebuilt from events or from the write table.
- There is a test for stale reads, for read-your-writes and for a projection outage.
- Projection lag is measurable.
- The documentation states that CQRS does not require Event Sourcing, and the choice of write side is justified.
- JUnit 5 tests in the existing `src/test/java` structure pass with the project's build tool.
- In the summary, state: what you changed, what you assumed, what remains.
# Task for a coding agent: CQRS: commands and queries

Stack: C#. Mode: guided.
Goal: Separate the calculator's writes and reads: commands change the write model, queries read a separate read model, and measure the lag between them.

## Context of the existing code

You are working in an existing project (C# backend (.NET, ASP.NET Core, EF Core or Dapper). Use the solution, style and DI conventions the project already has (xUnit/NUnit).).
Domain: a mortgage calculator that is gradually turning into an event-driven system (calculator, risk, approval, identity, account, documents, payout).
Prerequisite: the project has an event store for calculations (Event Sourcing) and a projection with a checkpoint that can be stopped and started. If not, first create a minimal one: events with a global position and a subscription that builds the read model from them and stores the position.

Read first, change nothing. Find and briefly summarise:
- Calculator endpoints that mix state changes and reads in one service or one model (DTO, entity, repository).
- Queries that currently assemble a view from several places, replay history or run expensive queries against the write table.
- An existing mechanism for publishing changes (events, outbox, broker) that can serve as the link between the sides.
- Places where the UI reads data right after saving (read-your-writes).
- Which of these parts already exist in the project and which are missing. Before writing your own infrastructure (read model, waiting for a version, metrics), check whether the project or organisation already has it as a ready-made library, and use it.

## Expected behaviour

A command changes state and enforces the rules, a query only reads; each side has its own model and the event is the link. The read model is briefly behind (eventual consistency): the lag can be measured and a query with the version from the command waits for the projection (read-your-writes). CQRS does not require Event Sourcing. Example: a calculation with 250 changes; without CQRS each query replays 250 events, with CQRS it reads one row of the read model.

## Specific task

Technical specifics (C#): Commands and queries as separate handlers (MediatR `IRequest` separately for Command and Query, if the project already uses it); the read model populated by a `BackgroundService` from events, reads via Dapper/EF `AsNoTracking`.

Proceed step by step. After point 1 (plan), write what you found and what you propose, and wait for confirmation:
1. Plan: list the commands (change state, return only an acknowledgement and version) and queries (change nothing). Draw the flow Command → Write model → Event → Projection → Query.
2. Introduce separate models: the write model holds the rules (invariants), the read model is shaped exactly for the query. Commands do not return the read model. Keep existing endpoints that return state after a write; add commands and queries as new ones (e.g. `/api/v2/commands/…`, `/api/v2/queries/…`).
3. Publish the change as an event (in one transaction with the write) and let the projection build the read model from it. With an event store, the event is the write itself; only describe the plain-table variant (row + outbox in one transaction) in the documentation.
4. The command returns a version. The query accepts `minVersion` and waits at most 1 s (configurable), then returns the latest state with `stale: true`. Without `minVersion` it immediately returns whatever the read model has.
5. Measure projection lag: the number of events behind (latest position in the event store − checkpoint) and the age of the oldest unprocessed event. Expose them as a metric, e.g. `GET /metrics` in Prometheus format. If the event store cannot return the latest position, add it.
6. Test: after a command, a query with the token returns the new version; without the token it may return an older one; with the projection stopped (`stop()` of the subscription) writes keep working and after `start()` the read model catches up.

Watch out for:
- Do not confuse CQRS with Event Sourcing: separating models does not require events as storage.
- Do not apply CQRS everywhere: for simple CRUD screens it only adds complexity. In the calculator it pays off for the calculation detail from the stream and for summaries; leave the CRUD form as it is.
- After saving, the UI must not assume the read model is immediately up to date (version token, optimistic display).
- A command must not read from the read model to decide on a write: the decision belongs to the write side.

## What you must not change

- The existing public API and the behaviour of the calculator endpoints that currently work.
- The database schema of existing tables (new tables/migrations are fine; renaming and deleting are not).
- Other people's tests, seed data and existing values. If a change is necessary, put it in a separate commit and state it. If a change to an event's shape or a new consumer necessarily changes an existing test, update it in a separate commit and explain why in the summary.
- Public contracts (APIs, event schemas) without backward compatibility.

## Done when

- Commands do not return the read model, queries change nothing; existing endpoints return the same responses as before.
- The read model can be deleted and rebuilt from events or from the write table.
- There is a test for stale reads, for read-your-writes and for a projection outage.
- Projection lag is measurable.
- The documentation states that CQRS does not require Event Sourcing, and the choice of write side is justified.
- Tests in the existing test project pass (`dotnet test`).
- In the summary, state: what you changed, what you assumed, what remains.
# Task for a coding agent: CQRS: commands and queries

Stack: C#. Mode: challenge.
Goal: Separate the calculator's writes and reads: commands change the write model, queries read a separate read model, and measure the lag between them.

## Context of the existing code

You are working in an existing project (C# backend (.NET, ASP.NET Core, EF Core or Dapper). Use the solution, style and DI conventions the project already has (xUnit/NUnit).).
Domain: a mortgage calculator that is gradually turning into an event-driven system (calculator, risk, approval, identity, account, documents, payout).
Prerequisite: the project has an event store for calculations (Event Sourcing) and a projection with a checkpoint that can be stopped and started. If not, first create a minimal one: events with a global position and a subscription that builds the read model from them and stores the position.

Read first, change nothing. Find and briefly summarise:
- Calculator endpoints that mix state changes and reads in one service or one model (DTO, entity, repository).
- Queries that currently assemble a view from several places, replay history or run expensive queries against the write table.
- An existing mechanism for publishing changes (events, outbox, broker) that can serve as the link between the sides.
- Places where the UI reads data right after saving (read-your-writes).
- Which of these parts already exist in the project and which are missing. Before writing your own infrastructure (read model, waiting for a version, metrics), check whether the project or organisation already has it as a ready-made library, and use it.

## Expected behaviour

A command changes state and enforces the rules, a query only reads; each side has its own model and the event is the link. The read model is briefly behind (eventual consistency): the lag can be measured and a query with the version from the command waits for the projection (read-your-writes). CQRS does not require Event Sourcing. Example: a calculation with 250 changes; without CQRS each query replays 250 events, with CQRS it reads one row of the read model.

## Brief (Challenge)

Next to the existing endpoint, add a separate calculator read model built from events, with a version token for read-your-writes and a projection lag metric. Keep the existing endpoint running.

Technical specifics (C#): Commands and queries as separate handlers (MediatR `IRequest` separately for Command and Query, if the project already uses it); the read model populated by a `BackgroundService` from events, reads via Dapper/EF `AsNoTracking`.

Choose your own approach. The pitfalls in this topic are not listed: find them (concurrency, redelivery, ordering, failure midway) and for each write a test that would expose it. At the end, list which pitfalls you found and which you deliberately left open.

## What you must not change

- The existing public API and the behaviour of the calculator endpoints that currently work.
- The database schema of existing tables (new tables/migrations are fine; renaming and deleting are not).
- Other people's tests, seed data and existing values. If a change is necessary, put it in a separate commit and state it. If a change to an event's shape or a new consumer necessarily changes an existing test, update it in a separate commit and explain why in the summary.
- Public contracts (APIs, event schemas) without backward compatibility.

## Done when

- Commands do not return the read model, queries change nothing; existing endpoints return the same responses as before.
- The read model can be deleted and rebuilt from events or from the write table.
- There is a test for stale reads, for read-your-writes and for a projection outage.
- Projection lag is measurable.
- The documentation states that CQRS does not require Event Sourcing, and the choice of write side is justified.
- Tests in the existing test project pass (`dotnet test`).
- In the summary, state: what you changed, what you assumed, what remains.
# Task for a coding agent: CQRS: commands and queries

Stack: Web (HTML/JS or the app's stack). Mode: guided.
Goal: Separate the calculator's writes and reads: commands change the write model, queries read a separate read model, and measure the lag between them.

## Context of the existing code

You are working in an existing project (web application: a browser frontend, possibly with its own backend. If the project has a backend, the event store, broker and services belong on the backend, and the points about endpoints and tables refer to it. If it has no backend, use IndexedDB in the browser and `fake-indexeddb` in tests. Keep the project's framework and test runner; add new dependencies only with a justification.).
Domain: a mortgage calculator that is gradually turning into an event-driven system (calculator, risk, approval, identity, account, documents, payout).
Prerequisite: the project has an event store for calculations (Event Sourcing) and a projection with a checkpoint that can be stopped and started. If not, first create a minimal one: events with a global position and a subscription that builds the read model from them and stores the position.

Read first, change nothing. Find and briefly summarise:
- Calculator endpoints that mix state changes and reads in one service or one model (DTO, entity, repository).
- Queries that currently assemble a view from several places, replay history or run expensive queries against the write table.
- An existing mechanism for publishing changes (events, outbox, broker) that can serve as the link between the sides.
- Places where the UI reads data right after saving (read-your-writes).
- Which of these parts already exist in the project and which are missing. Before writing your own infrastructure (read model, waiting for a version, metrics), check whether the project or organisation already has it as a ready-made library, and use it.

## Expected behaviour

A command changes state and enforces the rules, a query only reads; each side has its own model and the event is the link. The read model is briefly behind (eventual consistency): the lag can be measured and a query with the version from the command waits for the projection (read-your-writes). CQRS does not require Event Sourcing. Example: a calculation with 250 changes; without CQRS each query replays 250 events, with CQRS it reads one row of the read model.

## Specific task

Technical specifics (Web (HTML/JS or the app's stack)): The command returns `{ id, version }` (the stream version; for queries across several calculations, the global position). Separate API clients `commands` and `queries`; after a command, store the returned version and repeat the query until `version >= minVersion` (at most 3–5 attempts); show “updating…”. Do not switch over a form that calls an endpoint outside the event store: show read-your-writes in a separate part of the UI or in a client test.

Proceed step by step. After point 1 (plan), write what you found and what you propose, and wait for confirmation:
1. Plan: list the commands (change state, return only an acknowledgement and version) and queries (change nothing). Draw the flow Command → Write model → Event → Projection → Query.
2. Introduce separate models: the write model holds the rules (invariants), the read model is shaped exactly for the query. Commands do not return the read model. Keep existing endpoints that return state after a write; add commands and queries as new ones (e.g. `/api/v2/commands/…`, `/api/v2/queries/…`).
3. Publish the change as an event (in one transaction with the write) and let the projection build the read model from it. With an event store, the event is the write itself; only describe the plain-table variant (row + outbox in one transaction) in the documentation.
4. The command returns a version. The query accepts `minVersion` and waits at most 1 s (configurable), then returns the latest state with `stale: true`. Without `minVersion` it immediately returns whatever the read model has.
5. Measure projection lag: the number of events behind (latest position in the event store − checkpoint) and the age of the oldest unprocessed event. Expose them as a metric, e.g. `GET /metrics` in Prometheus format. If the event store cannot return the latest position, add it.
6. Test: after a command, a query with the token returns the new version; without the token it may return an older one; with the projection stopped (`stop()` of the subscription) writes keep working and after `start()` the read model catches up.

Watch out for:
- Do not confuse CQRS with Event Sourcing: separating models does not require events as storage.
- Do not apply CQRS everywhere: for simple CRUD screens it only adds complexity. In the calculator it pays off for the calculation detail from the stream and for summaries; leave the CRUD form as it is.
- After saving, the UI must not assume the read model is immediately up to date (version token, optimistic display).
- A command must not read from the read model to decide on a write: the decision belongs to the write side.

## What you must not change

- The existing public API and the behaviour of the calculator endpoints that currently work.
- The database schema of existing tables (new tables/migrations are fine; renaming and deleting are not).
- Other people's tests, seed data and existing values. If a change is necessary, put it in a separate commit and state it. If a change to an event's shape or a new consumer necessarily changes an existing test, update it in a separate commit and explain why in the summary.
- Public contracts (APIs, event schemas) without backward compatibility.

## Done when

- Commands do not return the read model, queries change nothing; existing endpoints return the same responses as before.
- The read model can be deleted and rebuilt from events or from the write table.
- There is a test for stale reads, for read-your-writes and for a projection outage.
- Projection lag is measurable.
- The documentation states that CQRS does not require Event Sourcing, and the choice of write side is justified.
- Tests pass with the existing test runner; lint and build pass if the project has them.
- In the summary, state: what you changed, what you assumed, what remains.
# Task for a coding agent: CQRS: commands and queries

Stack: Web (HTML/JS or the app's stack). Mode: challenge.
Goal: Separate the calculator's writes and reads: commands change the write model, queries read a separate read model, and measure the lag between them.

## Context of the existing code

You are working in an existing project (web application: a browser frontend, possibly with its own backend. If the project has a backend, the event store, broker and services belong on the backend, and the points about endpoints and tables refer to it. If it has no backend, use IndexedDB in the browser and `fake-indexeddb` in tests. Keep the project's framework and test runner; add new dependencies only with a justification.).
Domain: a mortgage calculator that is gradually turning into an event-driven system (calculator, risk, approval, identity, account, documents, payout).
Prerequisite: the project has an event store for calculations (Event Sourcing) and a projection with a checkpoint that can be stopped and started. If not, first create a minimal one: events with a global position and a subscription that builds the read model from them and stores the position.

Read first, change nothing. Find and briefly summarise:
- Calculator endpoints that mix state changes and reads in one service or one model (DTO, entity, repository).
- Queries that currently assemble a view from several places, replay history or run expensive queries against the write table.
- An existing mechanism for publishing changes (events, outbox, broker) that can serve as the link between the sides.
- Places where the UI reads data right after saving (read-your-writes).
- Which of these parts already exist in the project and which are missing. Before writing your own infrastructure (read model, waiting for a version, metrics), check whether the project or organisation already has it as a ready-made library, and use it.

## Expected behaviour

A command changes state and enforces the rules, a query only reads; each side has its own model and the event is the link. The read model is briefly behind (eventual consistency): the lag can be measured and a query with the version from the command waits for the projection (read-your-writes). CQRS does not require Event Sourcing. Example: a calculation with 250 changes; without CQRS each query replays 250 events, with CQRS it reads one row of the read model.

## Brief (Challenge)

Next to the existing endpoint, add a separate calculator read model built from events, with a version token for read-your-writes and a projection lag metric. Keep the existing endpoint running.

Technical specifics (Web (HTML/JS or the app's stack)): The command returns `{ id, version }` (the stream version; for queries across several calculations, the global position). Separate API clients `commands` and `queries`; after a command, store the returned version and repeat the query until `version >= minVersion` (at most 3–5 attempts); show “updating…”. Do not switch over a form that calls an endpoint outside the event store: show read-your-writes in a separate part of the UI or in a client test.

Choose your own approach. The pitfalls in this topic are not listed: find them (concurrency, redelivery, ordering, failure midway) and for each write a test that would expose it. At the end, list which pitfalls you found and which you deliberately left open.

## What you must not change

- The existing public API and the behaviour of the calculator endpoints that currently work.
- The database schema of existing tables (new tables/migrations are fine; renaming and deleting are not).
- Other people's tests, seed data and existing values. If a change is necessary, put it in a separate commit and state it. If a change to an event's shape or a new consumer necessarily changes an existing test, update it in a separate commit and explain why in the summary.
- Public contracts (APIs, event schemas) without backward compatibility.

## Done when

- Commands do not return the read model, queries change nothing; existing endpoints return the same responses as before.
- The read model can be deleted and rebuilt from events or from the write table.
- There is a test for stale reads, for read-your-writes and for a projection outage.
- Projection lag is measurable.
- The documentation states that CQRS does not require Event Sourcing, and the choice of write side is justified.
- Tests pass with the existing test runner; lint and build pass if the project has them.
- In the summary, state: what you changed, what you assumed, what remains.

Training for your team

Would you like this training for your team?

We run the training on your code and your domain problem. We agree the scope, dates and format with you.

Discuss the training