Current section

Files

Jump to
x3m_system lib aggregate.ex
Raw

lib/aggregate.ex

defmodule X3m.System.Aggregate do
defmodule State do
@type t :: %__MODULE__{version: integer, client_state: any}
defstruct version: -1, client_state: nil
end
@callback initial_state :: map()
@spec initial_state(atom) :: State.t()
def initial_state(aggregate_mod),
do: %State{client_state: apply(aggregate_mod, :initial_state, [])}
defmacro handle_msg(msg_name, fun) do
quote do
@spec unquote(msg_name)(X3m.System.Message.t(), X3m.System.Aggregate.State.t()) ::
{:block | :noblock, X3m.System.Message.t(), X3m.System.Aggregate.State.t()}
def unquote(msg_name)(%X3m.System.Message{} = message, state) do
X3m.System.Instrumenter.execute(:handle_msg, %{}, %{
aggregate: __MODULE__,
message: unquote(msg_name)
})
client_state = state.client_state || raise("Aggregate state wasn't set")
{label, %X3m.System.Message{} = message, client_state} =
unquote(fun).(message, client_state)
{label, message, %State{state | client_state: client_state}}
end
end
end
defmacro __using__(_opts) do
quote do
alias X3m.System.Aggregate
alias X3m.System.Aggregate.State, as: AggregateState
require Aggregate
import Aggregate
@behaviour Aggregate
@doc false
def apply_events([event | tail], last_version, %AggregateState{} = state) do
new_client_state = apply_event(event, state.client_state)
apply_events(tail, last_version, %AggregateState{state | client_state: new_client_state})
end
def apply_events([], last_version, %AggregateState{} = state),
do: %AggregateState{state | version: last_version}
@before_compile X3m.System.Aggregate
end
end
defmacro __before_compile__(_env) do
quote do
defp apply_event(_event, state),
do: state
end
end
end