evoq_pm_compensation (evoq v1.30.1)
View SourceRecording what a process manager dispatched, for compensation.
The one compensation contract
When a command a process manager dispatched fails, its instance calls the module's compensate/2 with the state and the failed command, and dispatches the commands that returns. compensate/2 answers {ok, Commands} or skip.
There is no second, per-executed-command form: an earlier build_compensation_chain/2 described one, nothing called it, and two contracts for one word is how a framework teaches the wrong thing.
What this module is for is the other half: the list of commands that did run, so a compensate/2 (or an operator looking at get_state/1) can see what there is to undo. The instance records each successful dispatch here when the process manager exports compensate/2, and clears it once the compensation has run; a process manager that does not compensate is left alone.
Summary
Functions
Forget what was dispatched.
Every command this process manager dispatched, most recent first.
Record a dispatched command in the process manager's state.
Functions
Forget what was dispatched.
The instance calls this once a compensation has run: the list is a record of what is still un-undone, not a log that grows for the life of the process manager. Nothing needs it when the instance stops - the state goes with it - so this is for the compensation path and for an application that wants to reset it itself.
-spec get_executed_commands(term()) -> [#evoq_command{command_id :: binary() | undefined, command_type :: atom() | undefined, aggregate_type :: atom() | undefined, aggregate_id :: binary() | undefined, payload :: map(), metadata :: map(), causation_id :: binary() | undefined, correlation_id :: binary() | undefined, idempotency_key :: binary() | undefined}].
Every command this process manager dispatched, most recent first.
**In memory only.** It is not part of the event stream and does not survive a restart: after one, the list is empty until the instance dispatches again. A compensate/2 that must work across a restart keeps what it needs in its own state, rebuilt in apply/2.
-spec record_command(#evoq_command{command_id :: binary() | undefined, command_type :: atom() | undefined, aggregate_type :: atom() | undefined, aggregate_id :: binary() | undefined, payload :: map(), metadata :: map(), causation_id :: binary() | undefined, correlation_id :: binary() | undefined, idempotency_key :: binary() | undefined}, term()) -> term().
Record a dispatched command in the process manager's state.
Most recent first. State that is not a map is returned unchanged: only a process manager keeps its state in a map, and this must never break one that does not.