How CQRS separates read and write models

· Category: System Design

Short answer

CQRS splits data models into separate schemas optimized for commands (writes) and queries (reads), improving performance and scalability.

Steps

  1. Design a write model that enforces business rules and transactional integrity.
  2. Create read models tailored to specific query patterns and UI requirements.
  3. Propagate changes from the write side to read sides via events or replication.
  4. Scale read and write infrastructure independently based on load.
  5. Accept eventual consistency between write and read models.

Tips

  • Use CQRS when read and write patterns differ significantly in complexity or volume.
  • Combine with event sourcing for a natural synchronization mechanism.
  • Keep read models denormalized and purpose-built for queries.
  • Avoid premature adoption; simple CRUD applications rarely need CQRS.

Common issues

  • Increased complexity from maintaining multiple models.
  • Debugging challenges when read models lag behind writes.
  • Data synchronization failures causing stale or incorrect reads.
  • Team confusion over which model to use for new features.

Example

# Consistent hashing for service discovery
import hashlib

def get_node(key, nodes):
    hash_val = int(hashlib.md5(key.encode()).hexdigest(), 16)
    return nodes[hash_val % len(nodes)]

node = get_node('user-123', ['node-a', 'node-b', 'node-c'])

This snippet implements consistent hashing to distribute keys across nodes, a foundational technique in scalable distributed systems.