CQRS
Separate reads from writes so combined views don’t fan out across every service database.
CQRS stands for Command Query Responsibility Segregation.
It shows up after you pick database per service. Writes are fine when each service owns its DB. Reads that need data from many services? That’s where it hurts.
The problem
Let’s say we have two services. Service 1 talks to its DB. Service 2 talks to its DB. Each does create / update / read on its own database. Normal.
Now another service — call it service 3 or 4 — wants a combined view. Data from service 1 and service 2 and maybe more.
As per the rules, it cannot touch those databases directly. So it asks each service separately, processes the responses, then returns a result.
You see the issue. Lots of network calls. Call this service, then that one, then another. Latency stacks. For any operation that needs that combined data, it becomes heavy.
How CQRS fixes it
CQRS says: separate reads from writes.
- One model handles reads (queries).
- The other side handles create / update / delete (commands).
In practice: service 1 and service 2 still have their own write databases. On top of that you add a read-only database — a view database — that holds a combination of the data those services care about together. Or a denormalized view shaped for how clients actually ask.
Read traffic goes to that read DB. You’re not fan-outing to five services on every query. That’s how CQRS cuts the network latency for those cross-service reads.
How the read DB stays in sync
The read database is eventually consistent with the real (write) databases.
You’re not doing a giant synchronous join across services on every request. Instead, when write-side services change data, they publish events. The read DB listens to those events and updates its view.
So yes — the read side can lag a bit. That’s the deal. Faster reads, simpler query path, eventual consistency.
Client calls Svc 1…
Every combined read fans out — latency stacks.Writes (commands) Reads (queries)
[Svc 1]──► DB1 ──events─┐
├──► Read / view DB ──► Client query
[Svc 2]──► DB2 ──events─┘
Without CQRS: Client → Svc1 → Svc2 → Svc3 → merge (slow)
When you’d actually use it
Use CQRS when “ask every service and merge” is already hurting you. If one model is forcing bad compromises between write integrity and query performance, that’s another signal.
When not to use this
Don’t introduce CQRS early just because it sounds fancy in an interview. If your reads are simple and mostly hit one service, a normal query is enough.
Also skip it when the business cannot tolerate stale reads at all for that screen — unless you’re ready to pay for stronger sync (and then you may not want this pattern).
Production pitfall
Stale UI that looks like a bug. User updates a profile, refreshes, still sees old data for two seconds because the read model hasn’t caught the event. Either show an honest “updating…” state, read-your-writes from the write side for that session, or document the lag. Silent eventual consistency is how support tickets get written.