Olivier Lowe

Dieser technische Artikel ist auf Englisch verfügbar.

When CQRS Earns Its Place — and When It Doesn't

I split reads from writes. I still did not introduce CQRS.

Different responsibilities can earn segregation without earning CQRS.

The First Split Wasn't CQRS

The Order detail view could continue using the existing Order. The collection needed a different representation.

getOrder()
→ Order

listOrders()
→ OrderSummary[]

Adding the collection capability to the original OrderApi also caused unrelated consumers to change. That pressure earned narrower OrderReadApi and OrderWriteApi capabilities.

Read/write interface segregation had been earned. CQRS had not.

Then the Write Side Began to Diverge

Cancellation introduced a second real write intention.

Adding cancellation to a broader write contract would make placement-only consumers depend on behavior they did not need.

PlaceOrderApi
└── placeOrder()

CancelOrderApi
└── cancelOrder()

The write side had earned narrower capabilities because its consumers were beginning to need different things.

Now CQRS Became a Legitimate Question

By this point, the architecture had accumulated several real forms of divergence.

different read representations
different read and write responsibilities
multiple write intentions
narrow write capabilities
different application flows

That was enough to ask whether CQRS had become the smallest responsible design.

It was not enough to answer yes.

But What Problem Would CQRS Solve?

The Application was still understandable as a small set of ordinary use cases.

loadOrder()
listOrders()
placeOrder()
cancelOrder()

The detail read still returned Order, and the command workflows still operated on that same frontend Order concept.

There was no demonstrated problem requiring separate command and query execution architectures.

What I Deliberately Did Not Add

CQRS could have introduced a much larger execution model. But none of these mechanisms solved a problem the implementation had demonstrated.

WHAT WE EARNED

OrderSummary
OrderReadApi
PlaceOrderApi
CancelOrderApi

WHAT WE DIDN'T EARN

CommandHandler
QueryHandler
CommandBus
QueryBus
separate read persistence
separate write persistence
eventual consistency

Recognizing a familiar architectural shape was not enough reason to introduce the pattern.

Where the Architecture Stopped

The smallest responsible architecture remained:

simple Application functions
+
narrow consumer capabilities
+
composed adapters

The architecture had changed where pressure required it and stopped where the existing design was still sufficient.

The Lesson

Different responsibilities can earn segregation without earning CQRS.

CONTINUE THE ARCHITECTURE JOURNEY

The substitution experiment is only part of the story.

This experiment comes from my book Building Software Around the Business — Not Around the Framework — Frontend Architecture with TypeScript, Angular, and React.

The book follows the architecture from the first frontend behavior through Domain and Application boundaries, state ownership, capability segregation, runtime composition, and the final REST-to-GraphQL substitution proof.

Explore the frontend architecture book →