Chronik v0.1.1 Chronik.Aggregate behaviour View Source

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

When using the Chronik.Aggregate module there are 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.

Example:

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

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

defmodule Counter do
  use Chronik.Aggregate, snapshot_every: 100, shutdown_timeout: 900000

  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 next_state(nil, %CounterCreated{id: id}) do
    %Counter{id: id}
  end

  def next_state(%Counter{} = state, %CounterIncremented{}) 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 next_state 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

Starts a new aggregate given by the module aggregate with id id

Callbacks

The handle_command is the entry point for commands on an aggregate

The next_state 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 next_state function

An aggregate is identified by its module and an id.

Link to this section Functions

Link to this function start_link(aggregate_module, id) View Source
start_link(aggregate_module :: atom, id :: Chronik.id) ::
  {:ok, pid} |
  {:error, String.t}

Starts a new aggregate given by the module aggregate with id id.

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 next_state(state, event) View Source
next_state(state :: state, event :: Chronik.domain_event) :: state

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