Real-World Event Sourcing
Mix.install([
{:kino, "~> 0.14.0"}
])
Event Sourced Calculator
defmodule EventSourcedCalculator.V3 do
@max_state_value 10_000
@min_state_value 0
def handle_command(%{value: val}, %{cmd: :add, value: v}) do
%{event_type: :value_added, value: min(@max_state_value - val, v)}
end
def handle_command(%{value: val}, %{cmd: :sub, value: v}) do
%{event_type: :value_subtracted, value: max(@min_state_value, val - v)}
end
def handle_command(%{value: val}, %{cmd: :mul, value: v}) when val * v > @max_state_value do
{:error, :mul_failed}
end
def handle_command(%{value: _val}, %{cmd: :mul, value: v}) do
%{event_type: :value_multiplied, value: v}
end
def handle_command(%{value: _val}, %{cmd: :div, value: 0}) do
{:error, :divide_failed}
end
def handle_command(%{value: _val}, %{cmd: :div, value: v}) do
%{event_type: :value_divided, value: v}
end
def handle_event(%{value: val}, %{event_type: :value_added, value: v}) do
%{value: val + v}
end
def handle_event(%{value: val}, %{event_type: :value_subtracted, value: v}) do
%{value: val - v}
end
def handle_event(%{value: val}, %{event_type: :value_multiplied, value: v}) do
%{value: val * v}
end
def handle_event(%{value: val}, %{event_type: :value_divided, value: v}) do
%{value: val / v}
end
def handle_event(%{value: _val}, %{}) do
%{}
end
def handle_event(%{value: _val} = state, _) do
state
end
end
cmds = [
%{value: 50, cmd: :add},
%{value: 10, cmd: :add},
%{value: 0, cmd: :div},
%{value: 2, cmd: :add}
]
initial = %{value: 0}
cmds
|> List.foldl(initial, fn cmd, acc ->
EventSourcedCalculator.V3.handle_event(
acc,
EventSourcedCalculator.V3.handle_command(acc, cmd) |> IO.inspect()
)
|> IO.inspect()
end)
Projector