Most applications start with one database and one model. The same tables, the same ORM entities, and the same service layer handle every request, whether it's a user placing an order or a thousand users browsing the catalog.
This works well for a long time. But as traffic grows, the cracks start to show, especially in apps like e-commerce where reads outnumber writes by 100 to 1 or more.
One model is being asked to serve two very different jobs. That is the problem CQRS is designed to solve.
CQRS stands for Command Query Responsibility Segregation. The idea is simple: split your system into two sides, one that changes data and one that reads it.
PlaceOrder, UpdateStock, CancelOrder.GetProductDetails, ListOrdersForUser.In a traditional setup, both go through the same model. In CQRS, they get separate modules, and often separate databases.
1┌──────────────┐ ┌──────────────┐2 Command ──▶│ Write Model │───────▶│ Write DB │3 │ (validation, │ │ (PostgreSQL) │4 │ business │ └──────┬───────┘5 │ rules) │ │ publishes events6 └──────────────┘ ▼7 ┌──────────────┐8 │ Event Broker │9 │ (Kafka/SQS) │10 └──────┬───────┘11 │ consumes events12 ┌──────────────┐ ┌──────▼───────┐13 Query ───▶│ Read Model │◀───────│ Read DB │14 │ (simple, │ │ (MongoDB) │15 │ no rules) │ └──────────────┘16 └──────────────┘
Because reads and writes want different things:
The write side can stay strict with transactions, constraints, and validation. The read side can store data exactly the way the UI needs it, with no joins at query time, and scale independently across regions.
The moment you have two databases, a new question appears: how does the read DB learn about changes made in the write DB?
The most common answer is events. If you've read my post on Event-Driven Architecture, this will feel familiar.
OrderPlaced) to a broker like Kafka, SQS, or SNS.Take an e-commerce example. A customer clicks "Place Order":
OrderPlaced event.The two sides never talk to each other directly. They only share events, which is what keeps them independent.
Events take time to travel. Between the write and the projector finishing, the read DB is stale. This is called eventual consistency: the two stores will agree, just not instantly.
Picture this on an e-commerce site: a user places an order, gets redirected to "My Orders", and the new order isn't there yet. To them it looks like the order was lost.
Common ways to deal with it:
CQRS is powerful, but it is not a default choice. It adds real complexity, so it has to earn its place.
At this size, the operational cost of two databases, a broker, and projectors is larger than the problem it solves.
CQRS is a simple idea (separate reads from writes) with big consequences. It lets each side use the database and data shape it's best at, and it scales well for global, read-heavy systems like e-commerce. The price is eventual consistency and a more complex system.
A good rule of thumb:
Start with one database. Add caching and read replicas first. Reach for CQRS only when the read and write workloads have clearly outgrown a shared model.
If you're under a million users, you likely don't need it yet. But understanding the pattern now will make the decision much easier when your system grows into it.