Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Real-World Event Sourcing

chapter1.livemd

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