ADR-008: Rejection Is Not an Event
Context
When a require guard fails in a decide clause, the system must handle the failure. Two
schools of thought exist in Event Sourcing:
| Approach | Mechanism | Event stream impact |
|---|---|---|
| Emit a rejection event | CommandRejected event | Stream records the attempt |
| Return an error | Err(RejectionError) | Stream unchanged |
Source: specs/theory.allium resolved question T-1; specs/core.allium Co-4
(RejectionError value type).
Decision
A rejection means nothing happened. decide returns Result<Events[], RejectionError>.
On require failure: Err with rejection message. No events are produced and the event
stream is left untouched.
The rationale: events represent domain facts — things that happened. A rejected command is not a fact about the domain; it is an operational result. Audit trails for failed attempts belong in a separate bounded context (Audit/Logging) that subscribes to command results — not the domain decider’s concern.
Consequences
Positive
- Event stream contains only domain facts; replaying it always produces a correct state.
- Decider logic stays pure — no side effects on rejection path.
- Clear separation of concerns: domain integrity vs. audit/observability.
- Generated TypeScript code expresses rejection naturally via
Resultwithout special cases.
Negative
- Systems requiring attempt tracking must implement a separate audit bounded context.
- It is not possible to reconstruct “what was tried” from the domain event stream alone; that information lives only in the audit context.