concept

Command vs Notification

also called Event Semantics, Obligation vs Fact

Whether a published message requests an action someone is responsible for, or states a fact the publisher does not care who consumes - and why conflating them causes outages.

event-drivencouplingorchestrationchoreographyintegration

A notification states that something happened: "order 123 was placed". The publisher has no expectation of any consumer. Adding a consumer requires no change to the publisher; a consumer failing affects only itself.

A command requests an action: "reserve inventory for order 123". There is an expected effect, someone is responsible for producing it, and the sender needs to know whether it happened.

On the wire they can be identical — a message on a topic — which is exactly why they get confused.

Why the confusion causes outages

A command treated as a notification produces work that silently does not happen. "Order placed" is published; the inventory service happens to subscribe and reserve stock. Inventory reservation is now an implicit side effect of a notification, with no owner, no acknowledgement, no timeout and no retry policy. When the inventory consumer is down, orders are accepted, nothing is reserved, and the discrepancy is discovered at fulfilment.

A notification treated as a command produces cascading failure. The order service waits for confirmation from analytics, search indexing and recommendations before completing. The availability of order placement is now the product of the availability of every optional consumer, so a slow analytics pipeline blocks revenue.

Implementation patterns

  • Name them differently. OrderPlaced versus ReserveInventory. A vocabulary that distinguishes them prevents most of the confusion at design time.
  • Orchestrate obligations. Commands with obligations belong in a durable workflow that owns the state machine, issues commands, awaits replies and enforces timeouts. Choreographing them means no component knows the overall state, timeouts have no owner, and concurrent transitions race with no arbiter.
  • Choreograph notifications. Fire-and-forget, with consumers added freely and no publisher change.
  • Every command has an owner, an acknowledgement and a timeout with a defined action. A wait state with no timeout produces work that stays stuck forever.
  • Idempotency on every command, because at-least-once delivery means every step occasionally reruns.

Industry example

An e-commerce order flow spanning inventory, payment, logistics and notification is the canonical case. The classification is:

Commands: reserve inventory, charge payment, request fulfilment. Each must happen, must be confirmed, and must be compensated if the order fails.

Notifications: order placed (to analytics, search, recommendations, marketing), order shipped (to email and tracking). Consumers can be added and can fail without affecting the order.

At extreme scale a second dimension appears: which commands must be synchronous. Inventory reservation must be strongly consistent and immediate, because overselling cannot be compensated — a double charge is refundable, a double sale of a limited item is not. Everything else can be asynchronous with compensating actions.

That asymmetry is the real insight: find the one invariant that cannot be compensated, protect it properly, and refuse to pay for strong consistency anywhere else.

Failure scenarios

  • Implicit commands, where a critical action is a side effect of a notification nobody knows about.
  • Publishers coupled to optional consumers, so an analytics outage stops revenue.
  • Compensation treated as rollback. A refund is a new business action with its own state and customer communication, not the absence of a charge.
  • No reconciliation. In a system this asynchronous, drift between the workflow's view and the payment provider's view is a certainty; the only question is whether you detect it or a customer does.

Trade-offs

Orchestration buys an authoritative state, a home for timeouts and an answerable question during an incident, at the cost of a central component every flow passes through and the risk that it accumulates business logic that belongs in services. Keep it to sequencing and timeouts, not pricing rules.

Choreography buys loose coupling and easy extension, at the cost of nobody being able to answer "why is this stuck?" Use it where that question does not need answering.

Interview question

"Walk me through an order flow across five services. For each message, tell me whether it is a command or a notification, who owns the timeout, and what happens if the consumer is down for an hour."