Skip to content
Muhammet Şafak
tr
Asked by: Rüzgar Answered:

When should I apply CQRS, and how do I sync the read and write models?


Question

In our app the write operations are very few (~5,000 records/day) but reads and complex reporting are very heavy (~2,000 queries/sec). The relational DB locks up because of the reporting queries. We want to fully separate the write model (PostgreSQL) and the read model (Elasticsearch or an optimized read-replica SQL) — CQRS. How do I keep the two models in sync, event-driven, with no lag and reliably?

Answer

Short answer: 2k reads/sec against 5k writes/day — that asymmetry is a real CQRS case. But start with the cheapest version; don’t jump straight to full CQRS.

The problem is clear: heavy reporting queries lock the write path too. Most of the time you don’t need a full architectural split to fix that.

  1. Add a read replica first. Put one or more read replicas on PostgreSQL and route all reporting queries there. Writes stay on the primary, reads go to the replica; the locking usually ends with just this. Don’t take on CQRS complexity before trying it.
  2. Understand when full CQRS earns its keep. A separate read model (Elasticsearch or denormalized SQL) is only worth it when query shapes diverge so much that one schema can’t serve both well. Needs like full-text search or heavy aggregation — where even a replica isn’t enough — are what justify splitting out the read model.
  3. Sync the models event-driven. Have the write side emit an event on every change; a projector consumes those events to update the read model. For reliability, use the outbox pattern: write the event to an outbox table in the same transaction as the business data, and have a separate process publish it — so you never get “the DB committed but the event was lost.”
  4. Accept eventual consistency, don’t dual-write. The read model lags the write by ms–seconds; design the UI for it (e.g. “saved” instantly, visible in the list a moment later). Never write to both stores from the app (dual-write) — they inevitably drift. Always drive the read model from the write side’s events.

Bottom line: replicas first, move reporting there. Adopt full CQRS only when query needs genuinely diverge, with outbox-driven projections — and treat the added complexity as a cost. The answer to “is PostgreSQL enough for me?” is still yes under most loads; push it to its limit before reaching for CQRS.

Related Reading

Tags: #data#architecture#elasticsearch
Share:

Comments

Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.

More Questions

All questions

Search the site

Start typing to search posts, projects and pages.

Esc to close Powered by Pagefind