# Command Query Responsibility Segregation — Event-Driven Architecture & CQRS

Source: https://www.skillbyai.com/en/event-driven-architecture/q-cqrs

> Explain CQRS and separate write models from read models.

## Different models for different jobs

**CQRS** splits a system into a **command side**, which validates and applies changes using a model built to enforce business rules, and a **query side**, which serves reads from models shaped for screens and reports. The idea comes from Bertrand Meyer's command-query separation, applied at the architectural level by Greg Young. Why bother? In many systems reads outnumber writes heavily and need very different shapes: the order-entry rules care about stock and credit limits, while the "My orders" page wants a denormalised list with product names and delivery status. With CQRS each side can be optimised and scaled separately: the write side might use a normalised relational model; the read side might use denormalised tables, a search index or a cache. CQRS does **not** require event sourcing, multiple databases or a message broker; those are separate choices.

## Commands and queries take different paths

Commands go through the write model; queries read from purpose-built read models kept up to date from changes.

![A user icon with two arrows: one into a write box with a rules shield, one into several read boxes; a dashed arrow flows from the write box to the read boxes.](assets/figures/event-driven-architecture/section-5-map.svg) — Figure 5.1 — A write model and read models updated from its changes.

## Separate command and query handlers

The command handler enforces rules; the query handler simply reads a ready-made view.

```python
# command side: enforce invariants on the aggregate
def handle_place_order(cmd, repo, outbox):
    customer = repo.get_customer(cmd.customer_id)
    if customer.credit_blocked:
        raise Rejected("credit blocked")
    order = Order.place(cmd.customer_id, cmd.items)   # validates stock rules
    repo.save(order)
    outbox.add(order.pending_events())               # e.g. OrderPlaced

# query side: no rules, just a fast read of a denormalised view
def get_my_orders(customer_id, read_db):
    return read_db.query(
        "SELECT order_id, placed_at, item_summary, status, eta "
        "FROM customer_orders_view WHERE customer_id = %s "
        "ORDER BY placed_at DESC LIMIT 20",
        (customer_id,),
    )
```

## CQRS is per component, not per company

Apply CQRS to the parts of a system with complex rules or very different read and write needs. A simple CRUD admin screen gains nothing from it and becomes harder to maintain.

**Quiz:** Does CQRS require event sourcing?

- [x] No; CQRS separates read and write models, and event sourcing is an independent choice
- [ ] Yes, always
- [ ] Only with Kafka
- [ ] Only for read models

*Answer:* No; CQRS separates read and write models, and event sourcing is an independent choice. CQRS and event sourcing combine well but neither requires the other.
