defmodule Incident.EventStore.InMemory.Adapter do
@moduledoc """
Implements an in-memory Event Store using Agents.
"""
@behaviour Incident.EventStore.Adapter
use Agent
alias Incident.EventStore.InMemory.Event
@spec start_link(keyword) :: GenServer.on_start()
def start_link(opts \\ []) do
initial_state = Keyword.get(opts, :initial_state, [])
Agent.start_link(fn -> initial_state end, name: __MODULE__)
end
@impl true
def get(aggregate_id) do
__MODULE__
|> Agent.get(& &1)
|> Enum.filter(&(&1.aggregate_id == aggregate_id))
|> Enum.sort(&(&1.id < &2.id))
end
@impl true
def append(event) do
persisted_event = %Event{
id: :erlang.unique_integer([:positive, :monotonic]),
event_id: Ecto.UUID.generate(),
aggregate_id: event.aggregate_id,
event_type: event.__struct__ |> Module.split() |> List.last(),
version: event.version,
event_date: DateTime.utc_now(),
event_data: event |> Map.from_struct() |> stringify_keys()
}
Agent.update(__MODULE__, &([persisted_event] ++ &1))
{:ok, persisted_event}
end
@spec stringify_keys(map) :: map
defp stringify_keys(enumerable) when is_map(enumerable) do
Enum.into(enumerable, %{}, fn {k, v} -> {stringify_key(k), v} end)
end
@spec stringify_key(atom | String.t()) :: String.t()
defp stringify_key(key) when is_atom(key), do: Atom.to_string(key)
defp stringify_key(key), do: key
end