Commanded v1.2.0 Commanded.Aggregates.AggregateLifespan behaviour View Source

The Commanded.Aggregates.AggregateLifespan behaviour is used to control the aggregate GenServer process lifespan.

By default an aggregate instance process will run indefinitely once started. You can change this default by implementing the Commanded.Aggregates.AggregateLifespan behaviour in a module and configuring it in your router.

After a command successfully executes, and creates at least one domain event, the after_event/1 function is called passing the last created event.

When a command is successfully handled but results in no domain events (by returning nil or an empty list []), the command struct is passed to the after_command/1 function.

Finally, if there is an error executing the command, the error reason is passed to the after_error/1 function.

For all the above, the returned inactivity timeout value is used to shutdown the aggregate process if no other messages are received.

Supported return values

  • Non-negative integer - specify an inactivity timeout, in millisconds.
  • :infinity - prevent the aggregate instance from shutting down.
  • :hibernate - send the process into hibernation.
  • :stop - immediately shutdown the aggregate process with a :normal exit reason.
  • {:stop, reason} - immediately shutdown the aggregate process with the given reason.

Hibernation

A hibernated process will continue its loop once a message is in its message queue. Hibernating an aggregate causes garbage collection and minimises the memory used by the process. Hibernating should not be used aggressively as too much time could be spent garbage collecting.

Example

Define a module that implements the Commanded.Aggregates.AggregateLifespan behaviour:

defmodule BankAccountLifespan do
  @behaviour Commanded.Aggregates.AggregateLifespan

  def after_event(%MoneyDeposited{}), do: :timer.hours(1)
  def after_event(%BankAccountClosed{}), do: :stop
  def after_event(_event), do: :infinity

  def after_command(%CloseAccount{}), do: :stop
  def after_command(_command), do: :infinity

  def after_error(:invalid_initial_balance), do: :timer.minutes(5)
  def after_error(_error), do: :stop
end

Then specify the module as the lifespan option when registering the applicable commands in your router:

defmodule BankRouter do
  use Commanded.Commands.Router

  dispatch [OpenAccount, CloseAccount],
    to: BankAccount,
    lifespan: BankAccountLifespan,
    identity: :account_number
end

Link to this section Summary

Callbacks

Aggregate process will be stopped after specified inactivity timeout unless :infinity, :hibernate, or :stop are returned.

Aggregate process will be stopped after specified inactivity timeout unless :infinity, :hibernate, or :stop are returned.

Aggregate process will be stopped after specified inactivity timeout unless :infinity, :hibernate, or :stop are returned.

Link to this section Types

Specs

lifespan() :: timeout() | :hibernate | :stop | {:stop, reason :: term()}

Link to this section Callbacks

Specs

after_command(command :: struct()) :: lifespan()

Aggregate process will be stopped after specified inactivity timeout unless :infinity, :hibernate, or :stop are returned.

Specs

after_error(any()) :: lifespan()

Aggregate process will be stopped after specified inactivity timeout unless :infinity, :hibernate, or :stop are returned.

Specs

after_event(event :: struct()) :: lifespan()

Aggregate process will be stopped after specified inactivity timeout unless :infinity, :hibernate, or :stop are returned.