Skip to main content

Command Queues, Logging, and Replayable Systems

What this concept is​

A command records intent; an accepted event records what happened. A command can be rejected, depend on current prices, or perform an external effect. A log of attempted commands is therefore not automatically a log that can reconstruct accepted state. Event sourcing stores a sequence of accepted state-changing facts.

Why it matters here​

An earlier queue sketch logged intent before executing it and replayed every entry as if it had succeeded. Failures, mutable payloads and changed handlers can make that replay diverge. A durable queue, a command audit log and an event store answer different questions. CQRS does not require event sourcing.

Concrete example​

In L04 a document accepts submit while draft, then approve while in review. The accepted records are:

records = (
('draft', 'submit', 'review'),
('review', 'approve', 'published'),
)

An attempted archive while draft raises and produces no accepted record. Replay validates each before-state and transition, starting from draft, and returns published for the two records above. It performs no notification or network call. This is a bounded in-memory fact log; it has no crash durability.

For a real effect, consider send email -> provider accepts -> acknowledgement is lost -> worker retries. Writing processed[id] after the send leaves a duplicate window. Writing it before the send leaves a lost-work window. A local set alone does not close either window. Specify the provider's idempotency contract or retain an uncertain outcome and reconcile. A transaction around local state and an outbox can record delivery intent consistently, but an outbox consumer can still redeliver and needs an effect-specific duplicate policy.

Common confusion / misconception​

The GoF Command pattern does not promise serialization, exactly-once effects or deterministic replay. Reexecuting an old command under a new discount policy may produce a different fact. Recording a timestamp does not make a log durable. An immutable outer record that shares a mutable payload can still change history.

How to use it​

Specify request schema, accepted/rejected result, state authority, persistence boundary, duplicate identity scope and retention, and crash points. Keep replay free of live external effects. If replay requires nondeterministic inputs, record their accepted results or model an explicit dependency on them. Version schemas and define what happens to unsupported records.

Check yourself​

Why is a rejected command absent from the accepted transition log? What happens if a log append succeeds but the following external send times out? Which crash claims can a sequential in-memory test establish? None about durable storage.

Mini drill or application​

Complete L04 and P13. Draw a timeline with one crash before and one after an external effect. Label outcomes known failed, known succeeded or uncertain. Propose a recovery action for each without asserting that a command ID itself makes the external operation idempotent. Inject an invalid record into replay.

Read this only if stuck​

GoF book index, 066-command-implementation.md for request/undo responsibilities; compare Fowler's Event Sourcing for event-based state reconstruction and external system concerns.

Request identity under retries​

Specify key scope, retention, concurrent retries and reuse with conflicting payloads. A stable key must be coordinated with the actual effect; storing a local ID does not atomically cover a separate send. Continue with the retained runtime failure lesson.