Commands vs Events (CQRS)
Commands are imperative requests that may be rejected; events are immutable facts that already happened. The stack keeps them strictly separate.
Two different things
A command is a request to change the machine: 'ramp coil PF3 to setpoint'. It is addressed to one handler, can be validated, and can be rejected. An event is an immutable record that something happened: 'coil PF3 reached setpoint'. It is broadcast, cannot be rejected, and is never mutated. Confusing the two is a common source of control-plane bugs; Layer 4 keeps them on separate topics with separate semantics.
| Aspect | Command | Event |
|---|---|---|
| mood | imperative | past-tense fact |
| can reject | yes | no |
| recipients | one handler | many consumers |
| mutable | no (single-use) | no (immutable) |
CQRS separation
Command handling (the write path through the gating pipeline) is separated from query/read models built by consuming events. The read side, for instance a live breeder equilibrium view or a burner plug-field dashboard, is a projection rebuilt from the event log and never actuates anything. This keeps the safety-critical write path small and auditable while read models can be many and disposable.
Command lifecycle
command issued -> gated -> ACCEPTED or REJECTED
if ACCEPTED and actuator acts -> emits event(s): STARTED, PROGRESS, COMPLETED|FAILED
consumers build read models from events; they never re-issue the command
Why it matters here
- The safe default is possible only because a command CAN be rejected; an event cannot.
- Read models can be rebuilt or reshaped without risk because they are pure projections.
- Auditability: the lineage bus records both the command and the events it caused.
Command emission is made reliable by the transactional outbox, and command uniqueness by idempotency keys.