CQRS: Commands, Queries & Handlers
CQRS (Command Query Responsibility Segregation) separates the model/path for WRITING data (commands) from the model/path for READING data (queries) -- rather than one unified model serving both, as in traditional CRUD. It extends the older, method-level Command-Query Separation (CQS) principle up to full architectural scale.
Commands vs. Queries
// Command: intent to CHANGE state, typically returns little
// beyond an acknowledgment
interface PlaceOrderCommand {
customerId: string
items: OrderItem[]
}
// Query: request to READ data, never changes state
interface GetOrderHistoryQuery {
customerId: string
}Command Handlers
// Validates input, enforces business rules, applies the change --
// one focused handler per command type, not one sprawling function
class PlaceOrderCommandHandler {
async handle(command: PlaceOrderCommand): Promise<{ orderId: string }> {
validateInventory(command.items)
const order = createOrder(command)
await writeStore.save(order)
// Full order details are NOT returned here -- that's a
// separate query responsibility, keeping commands narrowly focused
return { orderId: order.id }
}
}Query Handlers
// Reads and shapes data -- no business rule enforcement,
// often reads from a read-optimized, denormalized store
class GetOrderHistoryQueryHandler {
async handle(query: GetOrderHistoryQuery): Promise<OrderSummary[]> {
return readStore.getOrdersByCustomer(query.customerId)
}
}Why Separate Them
A high-read-volume system (a social feed) can optimize its read model purely for fast querying (denormalized, cache-friendly), decoupled from the write model that enforces business rules on writes -- a single shared model trying to serve both well tends to compromise on each.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free