Chronik v0.1.3 Chronik.Aggregate behaviour View Source

The Chronik.Aggregate is the base for all aggregates in Chronik.

The module that uses Chronik.Aggregate module can be configured with a number of options:

  • shutdown_timeout indicates Chronik to shutdown the aggregate GenServer after a number of milliseconds. Defualt value is 15 minutes.
  • snapshot_every indicates that a snapshot must be done on the Store every snapshot_every domain events processed. Default value is 100. This configuration is looked up in the :chronik app under the given module.

Example:

defmodule DomainEvents do
  defmodule CounterCreated do
    defstruct [:id]
  end

  defmodule CounterIncremented do
    defstruct [:id, :increment]
  end
end

defmodule Counter do
  use Chronik.Aggregate

  alias DomainEvents.CounterCreated
  alias DomainEvents.CounterIncremented

  defstruct [:id]

  def handle_command({:create, id}) do
    Counter.call(id,
      fn state ->
        execute(state, &validate_create(&1, id))
      end)
  end
  def handle_command({:increment, id, increment}) do
    Counter.call(id,
      fn state ->
        execute(state, &validate_increment(&1, id, increment))
      end)
  end

  def handle_event(%CounterCreated{id: id}, nil) do
    %Counter{id: id}
  end

  def handle_event(%CounterIncremented{}, %Counter{} = state) do
    state
  end

  defp validate_create(nil, id) do
    %Counter{id: id}
  end
  defp validate_create(_state, _id) do
    raise "cannot create counter"
  end

  defp validate_increment(%Counter{}, id, increment) do
    %CounterIncremented{id: id, increment: increment}
  end
  defp validate_increment(_state, _id, _increment) do
    raise "cannot increment counter"
  end
end

The application code must implement the handle_command and handle_event callback.

The Chronik.Macros provides helper macros to define events and commands.

Link to this section Summary

Types

The state represents the state of an aggregate

t()

An aggregate is identified by its module and an id

Functions

The execute function is used to wrap the state and events in the handle_command

The get(module, id) function returns the currente aggregate state. This should only be used for debugging purposes

Callbacks

The handle_command is the entry point for commands on an aggregate

The handle_event is the transition function for the aggregate. After command validation, the aggregate generates a number of domain events and then the aggregate state is updated for each event with this function

Link to this section Types

Link to this type state() View Source
state() :: term

The state represents the state of an aggregate.

Is used in the handle_event function

An aggregate is identified by its module and an id.

Link to this section Functions

Link to this function call(module, id, function) View Source
Link to this function execute(module, arg, fun) View Source

The execute function is used to wrap the state and events in the handle_command.

The get(module, id) function returns the currente aggregate state. This should only be used for debugging purposes.

Link to this section Callbacks

Link to this callback handle_command(command) View Source
handle_command(command :: Chronik.command) ::
  :ok |
  {:error, String.t}

The handle_command is the entry point for commands on an aggregate.

The command is application dependend. Throughout Chronik, commands are tagged tuples where the first element is an atom indicating the command to execute and the remaining elements are arguments to the command. E.g: {:add_item, 13, "Elixir Book", "$15.00"}

Example:

def handle_command({:add_item, id, book, price}) do
  Counter.call(id,
    fn state ->
      execute(state, &validate_add_item(&1, id, book, price))
    end)
end

def validate_add_item(%Cart{}, id, book, price) do
  %ItemsAdded{id: id, book: book, price: price}
end
def validate_add_item(nil, id, book, price) do
  raise "items cannot be added in the current state"
end

This command calls the validate_add_item to validate the command. If the command is valid on the given state, the function should return a list (or a single) of domain events.

The return values are :ok indicating a success execution or {:error, reason} otherwise.

Link to this callback handle_event(event, state) View Source
handle_event(event :: Chronik.domain_event, state :: state) :: state

The handle_event is the transition function for the aggregate. After command validation, the aggregate generates a number of domain events and then the aggregate state is updated for each event with this function.

Note that this function can not fail since the domain event where generated by a valid command.