command-query-separation
A command changes aggregates, a query reads views. Each handler keeps to the repositories of its side, so a read never writes and a write never decides on a view.
ts
export class PlaceOrderHandler extends CommandHandler<PlaceOrder> {
constructor(private readonly summaries: OrderSummaries) {
super();
}
}ts
export class PlaceOrderHandler extends CommandHandler<PlaceOrder> {
constructor(private readonly orders: Orders) {
super();
}
}Orders extends CommandRepository<Order>; OrderSummaries extends QueryRepository<OrderSummary>.
What it checks
The constructor parameters of each handler:
| Handler | Never receives |
|---|---|
CommandHandler | A QueryRepository. |
QueryHandler | A CommandRepository, an Outbox, a UnitOfWork or an EventPublisher. |
Why
A view is shaped for reading: it may be denormalised, stale or partial. A command that decides from a view decides from something that does not protect the rules; the aggregate does. A query that receives a repository of aggregates or an outbox can change state while it reads. Keeping each side to its own repositories keeps both honest, without going as far as separate read and write models.
What it reports
src/ordering/application/commands/place-order.command.ts:2
command-query-separation: The CommandHandler PlaceOrderHandler receives OrderSummaries, a QueryRepository: keep commands and queries apart.Turn it off
ts
rules: { "command-query-separation": "off" }See also
placement, for where handlers and repositories live