evoq_pm_compensation (evoq v1.30.1)

View Source

Recording 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

clear(State)

-spec clear(term()) -> term().

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.

get_executed_commands(State)

-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.

record_command(Evoq_command, State)

-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.