Skip to content

Accessing Read Models from the Write Side

In a system built with CQRS and Event Sourcing, it's tempting to reuse the read models inside the write-side logic. After all, they already contain the information you need – right?

But using read models on the write side is almost always a bad idea.

Let's explore why.

Why It's Tempting

Say you're handling a BorrowBook command. To check if the user has already reached their borrowing limit, you could look into a read model like:

SELECT COUNT(*) FROM active_loans WHERE member_id = 'm-1234'

Seems straightforward – but it violates a core principle of event-driven systems.

Why It's Dangerous

Read models are:

  • Denormalized
  • Optimized for display
  • Eventually consistent

They're not guaranteed to be up-to-date at the moment a command is processed. Relying on them introduces subtle race conditions and correctness issues.

More importantly, using them from the write side reverses the direction of flow:

In event-driven systems, events flow from write → read, not the other way around.

Better Approaches

There are two main alternatives:

Use the Event Stream

Reconstruct the necessary information from the event history. For example:

  • Load the member's event stream
  • Count the number of active BookBorrowed events minus BookReturned

This approach is correct, but may become expensive if the stream is long.

Use Write-Side Projections

Instead of using a read model, maintain a small write-side index – an up-to-date, internal summary of state that can be accessed reliably within command handlers.

For example:

  • A precomputed count of active loans per member
  • Stored as part of the write model, updated on each event

This way, you stay consistent without relying on read-side projections.

Summary

The read side is for reading. The write side is for deciding.

Don't mix the two.

Next up: See what eventual consistency really means – and how to deal with it in the user experience.