All posts
CQRSArchitectureDDD

CQRS Is Not About Databases

May 2026 · 5 min read

The most common mistake I see when teams adopt CQRS is treating it as a database pattern. You hear it all the time: 'we have a write database and a read database, so we're doing CQRS.' That's not CQRS. That's replication with a name attached to it.

CQRS — Command Query Responsibility Segregation — is about separating the intent of operations, not their storage backends. Commands change state and express intent: PlaceOrder, ConfirmPayment, CancelSubscription. Queries read state and express information needs: GetOrderSummary, ListActiveSubscriptions. These two concerns have fundamentally different shapes, different consistency requirements, and different failure modes. Mixing them in the same model is where most of the accidental complexity in backend systems comes from.

The Model Is The Problem

When you have a single domain model that serves both commands and queries, you start making compromises. You add properties to entities for display purposes. You load aggregates with more data than the business operation actually needs. You add query-specific methods to objects that are supposed to enforce business invariants. The model gets fat, the boundaries blur, and eventually nobody is sure whether a particular method changes state or just reads it.

Separating commands and queries means your write model can be lean and focused on enforcing business rules. Your read model can be shaped exactly for the UI or the API consumer — denormalized, pre-computed, optimized for reads. They don't have to look the same, because they're solving different problems.

The Database Split Is Optional

You can implement CQRS with a single database. The separation lives in your application layer, not your infrastructure. Start there. Split your handlers into command handlers that write and query handlers that read. Keep them from sharing application logic. That alone will clarify your codebase significantly.

The separate read store — whether that's a read-optimized projection in Elasticsearch, a denormalized view in a second database, or a cache — is a performance optimization you reach for when the single-store approach becomes a bottleneck. Most systems never get there, and that's fine.

When It's Worth The Overhead

CQRS adds indirection. More handlers, more interfaces, more explicit boundaries. That overhead is worth it when your domain is genuinely complex — when commands need to enforce non-trivial invariants, when read requirements diverge significantly from your write model, or when you need the audit trail that Event Sourcing gives you on the command side.

It's probably overkill for a CRUD service that updates user profiles. The pattern isn't universally applicable — it's a tool for specific problems. The mistake isn't using it wrong. The mistake is reaching for it before understanding what problem it actually solves.

Written by Stefan Burgić