lib/one_piece/commanded/aggregate.ex

defmodule OnePiece.Commanded.Aggregate do
  @moduledoc """
  Defines "Aggregate" modules.
  """

  @type t :: struct()

  @type event :: struct()

  @doc """
  Apply a given event to the aggregate returning the new aggregate state.

  ## Example

      def apply(account, event) do
        account
        |> Map.put(:name, event.name)
        |> Map.put(:description, event.description)
      end
  """
  @callback apply(aggregate :: t(), event :: event()) :: t()

  @doc """
  Convert the module into a `Aggregate` behaviour.

  ## Usage

      defmodule Account do
        use OnePiece.Commanded.Aggregate

        @impl OnePiece.Commanded.Aggregate
        def apply(account, event) do
          account
          |> Map.put(:name, event.name)
          |> Map.put(:description, event.description)
        end
      end
  """
  @spec __using__(opts :: []) :: any()
  defmacro __using__(opts \\ []) do
    quote do
      use OnePiece.Commanded.Entity, unquote(opts)
      @behaviour OnePiece.Commanded.Aggregate
      @before_compile OnePiece.Commanded.Aggregate
    end
  end

  defmacro __before_compile__(env) do
    quote do
      @impl OnePiece.Commanded.Aggregate
      def apply(%unquote(env.module){} = aggregate, _event) do
        aggregate
      end
    end
  end
end