From ab0d523eefb0d423b7523387d6df87897950f6e6 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Balsini Moura Date: Tue, 18 Oct 2022 11:54:28 -0300 Subject: [PATCH 1/3] Multi with named steps --- lib/commanded/aggregates/aggregate.ex | 2 +- lib/commanded/aggregates/multi.ex | 149 +++++++++++++++++++++----- test/aggregates/multi_test.exs | 142 +++++++++++++++++++++++- test/support/aggregate_case.ex | 2 +- 4 files changed, 261 insertions(+), 34 deletions(-) diff --git a/lib/commanded/aggregates/aggregate.ex b/lib/commanded/aggregates/aggregate.ex index f7fc1245..7ac0f072 100644 --- a/lib/commanded/aggregates/aggregate.ex +++ b/lib/commanded/aggregates/aggregate.ex @@ -499,7 +499,7 @@ defmodule Commanded.Aggregates.Aggregate do {:error, _error} = reply -> {reply, state} - {aggregate_state, pending_events} -> + {aggregate_state, _steps, pending_events} -> persist_events(pending_events, aggregate_state, context, state) end diff --git a/lib/commanded/aggregates/multi.ex b/lib/commanded/aggregates/multi.ex index bc316c9e..585a3883 100644 --- a/lib/commanded/aggregates/multi.ex +++ b/lib/commanded/aggregates/multi.ex @@ -49,7 +49,7 @@ defmodule Commanded.Aggregate.Multi do @type t :: %__MODULE__{ aggregate: struct(), - executions: list(function()) + executions: list({step_name :: atom(), function()}) } defstruct [:aggregate, executions: []] @@ -62,12 +62,32 @@ defmodule Commanded.Aggregate.Multi do @doc """ Adds a command execute function to the multi. + + If `step_name` is provided, the aggregate state after that step is + stored under that name. That can be useful in a long multi step multi + in which one needs to know what was the agg state while procesisng + the multi. It's possible, then, to pattern match the step name in the + second parameter of the anonymous function to be executed. + + ## Example + + alias Commanded.Aggregate.Multi + + aggregate + |> Multi.new() + |> Multi.execute(:interesting_event, fn aggregate -> + %Event{data: 1} + end) + |> Multi.execute(fn aggregate, %{interesting_events: _aggregate_after_interesting_event} -> + %Event{data: 2} + end) """ - @spec execute(Multi.t(), function()) :: Multi.t() - def execute(%Multi{} = multi, execute_fun) when is_function(execute_fun, 1) do + @spec execute(Multi.t(), atom(), function()) :: Multi.t() + def execute(%Multi{} = multi, step_name \\ false, execute_fun) + when is_function(execute_fun, 1) or is_function(execute_fun, 2) do %Multi{executions: executions} = multi - %Multi{multi | executions: [execute_fun | executions]} + %Multi{multi | executions: [{step_name, execute_fun} | executions]} end @doc """ @@ -78,6 +98,12 @@ defmodule Commanded.Aggregate.Multi do state based upon events produced by previous items in the enumerable, such as running totals. + If `step_name` is provided, the aggregate state after that step will be + stored under that name. That can be useful in a long multi step multi + in which one needs to know what was the agg state while procesisng + the multi. It's possible, then, to pattern match the step name in the + third parameter of the anonymous function to be executed. + ## Example alias Commanded.Aggregate.Multi @@ -88,57 +114,122 @@ defmodule Commanded.Aggregate.Multi do %AnEvent{item: item, total: aggregate.total + item} end) + ## Example with named steps + + alias Commanded.Aggregate.Multi + + aggregate + |> Multi.new() + |> Multi.execute(:start, fn aggregate -> + %AnEvent{item: nil, total: 0} + end) + |> Multi.reduce(:interesting_event, [1, 2, 3], fn aggregate, item -> + %AnEvent{item: item, total: aggregate.total + item} + end) + |> Multi.reduce([4, 5, 6], fn aggregate, + item, + %{ + start: aggregate_state_after_start, + interesting_event: aggregate_state_after_interesting_event + } -> + %AnEvent{item: item, total: aggregate.total + item} + end) + """ - @spec reduce(Multi.t(), Enum.t(), function()) :: Multi.t() - def reduce(%Multi{} = multi, enumerable, execute_fun) when is_function(execute_fun, 2) do + @spec reduce(Multi.t(), atom(), Enum.t(), function()) :: Multi.t() + def reduce(multi, step_name \\ false, enumerable, execute_fun) + + def reduce(%Multi{} = multi, step_name, enumerable, execute_fun) + when is_function(execute_fun, 2) do + Enum.reduce(enumerable, multi, fn item, %Multi{} = multi -> + execute(multi, step_name, &execute_fun.(&1, item)) + end) + end + + def reduce(%Multi{} = multi, step_name, enumerable, execute_fun) + when is_function(execute_fun, 3) do Enum.reduce(enumerable, multi, fn item, %Multi{} = multi -> - execute(multi, &execute_fun.(&1, item)) + execute(multi, step_name, &execute_fun.(&1, item, &2)) end) end @doc """ Run the execute functions contained within the multi, returning the updated - aggregate state and all created events. + aggregate state, the aggregate state for each named step and all created events. """ @spec run(Multi.t()) :: - {aggregate :: struct(), list(event :: struct())} | {:error, reason :: any()} + {aggregate :: struct(), steps :: %{atom() => struct()}, list(event :: struct())} + | {:error, reason :: any()} def run(%Multi{aggregate: aggregate, executions: executions}) do try do executions |> Enum.reverse() - |> Enum.reduce({aggregate, []}, fn execute_fun, {aggregate, events} -> - case execute_fun.(aggregate) do - {:error, _reason} = error -> - throw(error) + |> Enum.reduce({aggregate, %{}, []}, fn + {step_name, execute_fun}, {aggregate, steps, events} + when is_function(execute_fun, 1) or is_function(execute_fun, 2) -> + case execute_function(execute_fun, aggregate, steps) do + {:error, _reason} = error -> + throw(error) - %Multi{} = multi -> - case Multi.run(multi) do - {:error, _reason} = error -> - throw(error) + %Multi{} = multi -> + case Multi.run(multi) do + {:error, _reason} = error -> + throw(error) - {evolved_aggregate, pending_events} -> - {evolved_aggregate, events ++ pending_events} - end + # do not leak nested multi steps to outer multis + {evolved_aggregate, _nested_multi_steps, pending_events} -> + updated_steps = maybe_update_steps(step_name, steps, evolved_aggregate) - none when none in [:ok, nil, []] -> - {aggregate, events} + {evolved_aggregate, updated_steps, events ++ pending_events} + end - {:ok, pending_events} -> - pending_events = List.wrap(pending_events) + none when none in [:ok, nil, []] -> + updated_steps = maybe_update_steps(step_name, steps, aggregate) - {apply_events(aggregate, pending_events), events ++ pending_events} + {aggregate, updated_steps, events} - pending_events -> - pending_events = List.wrap(pending_events) + {:ok, pending_events} -> + pending_events = List.wrap(pending_events) - {apply_events(aggregate, pending_events), events ++ pending_events} - end + evolved_aggregate = apply_events(aggregate, pending_events) + + updated_steps = maybe_update_steps(step_name, steps, evolved_aggregate) + + {evolved_aggregate, updated_steps, events ++ pending_events} + + pending_events -> + pending_events = List.wrap(pending_events) + + evolved_aggregate = apply_events(aggregate, pending_events) + + updated_steps = maybe_update_steps(step_name, steps, evolved_aggregate) + + {evolved_aggregate, updated_steps, events ++ pending_events} + end end) catch {:error, _error} = error -> error end end + defp maybe_update_steps(step_name, actual_steps, aggregate_state_after_step) + + defp maybe_update_steps(false, actual_steps, _aggregate_state_after_step), do: actual_steps + + defp maybe_update_steps(step_name, actual_steps, aggregate_state_after_step) do + Map.put(actual_steps, step_name, aggregate_state_after_step) + end + + defp execute_function(execute_fun, aggregate, _steps) + when is_function(execute_fun, 1) do + execute_fun.(aggregate) + end + + defp execute_function(execute_fun, aggregate, steps) + when is_function(execute_fun, 2) do + execute_fun.(aggregate, steps) + end + defp apply_events(aggregate, events) do Enum.reduce(events, aggregate, &aggregate.__struct__.apply(&2, &1)) end diff --git a/test/aggregates/multi_test.exs b/test/aggregates/multi_test.exs index bec76f6e..fd217244 100644 --- a/test/aggregates/multi_test.exs +++ b/test/aggregates/multi_test.exs @@ -31,7 +31,7 @@ defmodule Commanded.Aggregate.MultiTest do amount: 100 }) - assert {account, events} = Multi.run(multi) + assert {account, steps, events} = Multi.run(multi) assert account == %BankAccount{ account_number: account_number, @@ -42,6 +42,8 @@ defmodule Commanded.Aggregate.MultiTest do assert events == [ %MoneyWithdrawn{account_number: account_number, amount: 100, balance: 900} ] + + assert steps == %{} end test "should return errors encountered by `Commanded.Aggregate.Multi`" do @@ -116,7 +118,7 @@ defmodule Commanded.Aggregate.MultiTest do alias ExampleAggregate.Event test "should be supported" do - {%ExampleAggregate{}, events} = + {%ExampleAggregate{}, steps, events} = %ExampleAggregate{} |> Multi.new() |> Multi.execute(fn %ExampleAggregate{events: events} -> @@ -146,10 +148,86 @@ defmodule Commanded.Aggregate.MultiTest do |> Multi.run() assert events == [%Event{data: 1}, %Event{data: 2}, %Event{data: 3}] + assert steps == %{} + end + + test "should store aggregate state under step_name if step name is passed on multi step nested multis" do + {%ExampleAggregate{}, steps, events} = + %ExampleAggregate{} + |> Multi.new() + |> Multi.execute(:event_1, fn %ExampleAggregate{events: events}, steps -> + assert events == [] + assert steps == %{} + + %Event{data: 1} + end) + |> Multi.execute(:event_2, fn %ExampleAggregate{} = aggregate, steps -> + assert steps == %{event_1: %ExampleAggregate{events: [%Event{data: 1}]}} + + aggregate + |> Multi.new() + # ensure :event_2_2 won't leak into outer multi + |> Multi.execute(:event_2_2, fn %ExampleAggregate{events: events} -> + assert length(events) == 1 + + %Event{data: 2} + end) + end) + |> Multi.execute(:event_3, fn %ExampleAggregate{events: events}, steps -> + assert length(events) == 2 + + assert steps == %{ + event_1: %ExampleAggregate{events: [%Event{data: 1}]}, + event_2: %ExampleAggregate{events: [%Event{data: 1}, %Event{data: 2}]} + } + + %Event{data: 3} + end) + |> Multi.execute(:event_4, fn %ExampleAggregate{events: events} -> + # if steps won't be used, we can pass function with arity 1 + assert length(events) == 3 + + [] + end) + |> Multi.execute(fn %ExampleAggregate{events: events} -> + # you can also not name the step even if you names previous ones + assert length(events) == 3 + + [] + end) + |> Multi.run() + + assert events == [%Event{data: 1}, %Event{data: 2}, %Event{data: 3}] + + assert steps == %{ + event_1: %Commanded.Aggregate.MultiTest.ExampleAggregate{ + events: [%Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 1}] + }, + event_2: %Commanded.Aggregate.MultiTest.ExampleAggregate{ + events: [ + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 1}, + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 2} + ] + }, + event_3: %Commanded.Aggregate.MultiTest.ExampleAggregate{ + events: [ + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 1}, + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 2}, + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 3} + ] + }, + event_4: %Commanded.Aggregate.MultiTest.ExampleAggregate{ + events: [ + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 1}, + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 2}, + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 3} + ] + } + } end test "should reduce enum" do - {%ExampleAggregate{}, events} = + {%ExampleAggregate{}, steps, events} = %ExampleAggregate{} |> Multi.new() |> Multi.reduce([1, 2, 3], fn %ExampleAggregate{events: events}, index -> @@ -160,6 +238,64 @@ defmodule Commanded.Aggregate.MultiTest do |> Multi.run() assert events == [%Event{data: 1}, %Event{data: 2}, %Event{data: 3}] + assert steps == %{} + end + + test "should store aggregate state under step_name on reduce" do + {%ExampleAggregate{}, steps, events} = + %ExampleAggregate{} + |> Multi.new() + |> Multi.reduce(:reduce_step, [1, 2, 3], fn %ExampleAggregate{events: events}, index -> + assert length(events) == index - 1 + + %Event{data: index} + end) + |> Multi.reduce(:reduce_step_2, [4, 5, 6], fn %ExampleAggregate{events: events}, + index, + %{reduce_step: reduce_step} -> + assert reduce_step == + %ExampleAggregate{ + events: [ + %ExampleAggregate.Event{data: 1}, + %ExampleAggregate.Event{data: 2}, + %ExampleAggregate.Event{data: 3} + ] + } + + assert length(events) == index - 1 + + %Event{data: index} + end) + |> Multi.run() + + assert events == [ + %Event{data: 1}, + %Event{data: 2}, + %Event{data: 3}, + %Event{data: 4}, + %Event{data: 5}, + %Event{data: 6} + ] + + assert steps == %{ + reduce_step: %ExampleAggregate{ + events: [ + %ExampleAggregate.Event{data: 1}, + %ExampleAggregate.Event{data: 2}, + %ExampleAggregate.Event{data: 3} + ] + }, + reduce_step_2: %ExampleAggregate{ + events: [ + %ExampleAggregate.Event{data: 1}, + %ExampleAggregate.Event{data: 2}, + %ExampleAggregate.Event{data: 3}, + %ExampleAggregate.Event{data: 4}, + %ExampleAggregate.Event{data: 5}, + %ExampleAggregate.Event{data: 6} + ] + } + } end end end diff --git a/test/support/aggregate_case.ex b/test/support/aggregate_case.ex index 6b174202..275cd904 100644 --- a/test/support/aggregate_case.ex +++ b/test/support/aggregate_case.ex @@ -67,7 +67,7 @@ defmodule Commanded.AggregateCase do {:error, _reason} = error -> throw(error) - {state, new_events} -> + {state, _steps, new_events} -> {state, events ++ new_events} end From 9591937b70f5e5ca8e6f41abdffefcb98ef19252 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Moura Date: Tue, 18 Oct 2022 13:59:18 -0300 Subject: [PATCH 2/3] Typo in documentation --- lib/commanded/aggregates/multi.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commanded/aggregates/multi.ex b/lib/commanded/aggregates/multi.ex index 585a3883..bb545d1e 100644 --- a/lib/commanded/aggregates/multi.ex +++ b/lib/commanded/aggregates/multi.ex @@ -78,7 +78,7 @@ defmodule Commanded.Aggregate.Multi do |> Multi.execute(:interesting_event, fn aggregate -> %Event{data: 1} end) - |> Multi.execute(fn aggregate, %{interesting_events: _aggregate_after_interesting_event} -> + |> Multi.execute(fn aggregate, %{interesting_event: _aggregate_after_interesting_event} -> %Event{data: 2} end) """ From 24df6f6da4365c73b05cd0ac1e5e5637af36b043 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Balsini Moura Date: Wed, 26 Oct 2022 21:51:10 -0300 Subject: [PATCH 3/3] Revert breaking changes --- lib/commanded/aggregates/aggregate.ex | 2 +- lib/commanded/aggregates/multi.ex | 70 +++++++-------- test/aggregates/multi_test.exs | 117 +++++++++++++------------- test/support/aggregate_case.ex | 2 +- 4 files changed, 96 insertions(+), 95 deletions(-) diff --git a/lib/commanded/aggregates/aggregate.ex b/lib/commanded/aggregates/aggregate.ex index 7ac0f072..f7fc1245 100644 --- a/lib/commanded/aggregates/aggregate.ex +++ b/lib/commanded/aggregates/aggregate.ex @@ -499,7 +499,7 @@ defmodule Commanded.Aggregates.Aggregate do {:error, _error} = reply -> {reply, state} - {aggregate_state, _steps, pending_events} -> + {aggregate_state, pending_events} -> persist_events(pending_events, aggregate_state, context, state) end diff --git a/lib/commanded/aggregates/multi.ex b/lib/commanded/aggregates/multi.ex index bb545d1e..fe188dc4 100644 --- a/lib/commanded/aggregates/multi.ex +++ b/lib/commanded/aggregates/multi.ex @@ -158,55 +158,57 @@ defmodule Commanded.Aggregate.Multi do aggregate state, the aggregate state for each named step and all created events. """ @spec run(Multi.t()) :: - {aggregate :: struct(), steps :: %{atom() => struct()}, list(event :: struct())} - | {:error, reason :: any()} + {aggregate :: struct(), list(event :: struct())} | {:error, reason :: any()} def run(%Multi{aggregate: aggregate, executions: executions}) do try do - executions - |> Enum.reverse() - |> Enum.reduce({aggregate, %{}, []}, fn - {step_name, execute_fun}, {aggregate, steps, events} - when is_function(execute_fun, 1) or is_function(execute_fun, 2) -> - case execute_function(execute_fun, aggregate, steps) do - {:error, _reason} = error -> - throw(error) + {evolved_aggregate, _steps, pending_events} = + executions + |> Enum.reverse() + |> Enum.reduce({aggregate, %{}, []}, fn + {step_name, execute_fun}, {aggregate, steps, events} + when is_function(execute_fun, 1) or is_function(execute_fun, 2) -> + case execute_function(execute_fun, aggregate, steps) do + {:error, _reason} = error -> + throw(error) - %Multi{} = multi -> - case Multi.run(multi) do - {:error, _reason} = error -> - throw(error) + %Multi{} = multi -> + case Multi.run(multi) do + {:error, _reason} = error -> + throw(error) - # do not leak nested multi steps to outer multis - {evolved_aggregate, _nested_multi_steps, pending_events} -> - updated_steps = maybe_update_steps(step_name, steps, evolved_aggregate) + # do not leak nested multi steps to outer multis + {evolved_aggregate, pending_events} -> + updated_steps = maybe_update_steps(step_name, steps, evolved_aggregate) - {evolved_aggregate, updated_steps, events ++ pending_events} - end + {evolved_aggregate, updated_steps, events ++ pending_events} + end - none when none in [:ok, nil, []] -> - updated_steps = maybe_update_steps(step_name, steps, aggregate) + none when none in [:ok, nil, []] -> + updated_steps = maybe_update_steps(step_name, steps, aggregate) - {aggregate, updated_steps, events} + {aggregate, updated_steps, events} - {:ok, pending_events} -> - pending_events = List.wrap(pending_events) + {:ok, pending_events} -> + pending_events = List.wrap(pending_events) - evolved_aggregate = apply_events(aggregate, pending_events) + evolved_aggregate = apply_events(aggregate, pending_events) - updated_steps = maybe_update_steps(step_name, steps, evolved_aggregate) + updated_steps = maybe_update_steps(step_name, steps, evolved_aggregate) - {evolved_aggregate, updated_steps, events ++ pending_events} + {evolved_aggregate, updated_steps, events ++ pending_events} - pending_events -> - pending_events = List.wrap(pending_events) + pending_events -> + pending_events = List.wrap(pending_events) - evolved_aggregate = apply_events(aggregate, pending_events) + evolved_aggregate = apply_events(aggregate, pending_events) - updated_steps = maybe_update_steps(step_name, steps, evolved_aggregate) + updated_steps = maybe_update_steps(step_name, steps, evolved_aggregate) - {evolved_aggregate, updated_steps, events ++ pending_events} - end - end) + {evolved_aggregate, updated_steps, events ++ pending_events} + end + end) + + {evolved_aggregate, pending_events} catch {:error, _error} = error -> error end diff --git a/test/aggregates/multi_test.exs b/test/aggregates/multi_test.exs index fd217244..ffb6ce0c 100644 --- a/test/aggregates/multi_test.exs +++ b/test/aggregates/multi_test.exs @@ -31,7 +31,7 @@ defmodule Commanded.Aggregate.MultiTest do amount: 100 }) - assert {account, steps, events} = Multi.run(multi) + assert {account, events} = Multi.run(multi) assert account == %BankAccount{ account_number: account_number, @@ -42,8 +42,6 @@ defmodule Commanded.Aggregate.MultiTest do assert events == [ %MoneyWithdrawn{account_number: account_number, amount: 100, balance: 900} ] - - assert steps == %{} end test "should return errors encountered by `Commanded.Aggregate.Multi`" do @@ -118,7 +116,7 @@ defmodule Commanded.Aggregate.MultiTest do alias ExampleAggregate.Event test "should be supported" do - {%ExampleAggregate{}, steps, events} = + {%ExampleAggregate{}, events} = %ExampleAggregate{} |> Multi.new() |> Multi.execute(fn %ExampleAggregate{events: events} -> @@ -140,24 +138,23 @@ defmodule Commanded.Aggregate.MultiTest do %Event{data: 3} end) - |> Multi.execute(fn %ExampleAggregate{events: events} -> + |> Multi.execute(fn %ExampleAggregate{events: events}, steps -> assert length(events) == 3 + assert steps == %{} [] end) |> Multi.run() assert events == [%Event{data: 1}, %Event{data: 2}, %Event{data: 3}] - assert steps == %{} end test "should store aggregate state under step_name if step name is passed on multi step nested multis" do - {%ExampleAggregate{}, steps, events} = + {%ExampleAggregate{}, events} = %ExampleAggregate{} |> Multi.new() - |> Multi.execute(:event_1, fn %ExampleAggregate{events: events}, steps -> + |> Multi.execute(:event_1, fn %ExampleAggregate{events: events} -> assert events == [] - assert steps == %{} %Event{data: 1} end) @@ -189,45 +186,45 @@ defmodule Commanded.Aggregate.MultiTest do [] end) - |> Multi.execute(fn %ExampleAggregate{events: events} -> + |> Multi.execute(fn %ExampleAggregate{events: events}, steps -> # you can also not name the step even if you names previous ones assert length(events) == 3 + assert steps == %{ + event_1: %Commanded.Aggregate.MultiTest.ExampleAggregate{ + events: [%Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 1}] + }, + event_2: %Commanded.Aggregate.MultiTest.ExampleAggregate{ + events: [ + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 1}, + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 2} + ] + }, + event_3: %Commanded.Aggregate.MultiTest.ExampleAggregate{ + events: [ + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 1}, + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 2}, + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 3} + ] + }, + event_4: %Commanded.Aggregate.MultiTest.ExampleAggregate{ + events: [ + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 1}, + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 2}, + %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 3} + ] + } + } + [] end) |> Multi.run() assert events == [%Event{data: 1}, %Event{data: 2}, %Event{data: 3}] - - assert steps == %{ - event_1: %Commanded.Aggregate.MultiTest.ExampleAggregate{ - events: [%Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 1}] - }, - event_2: %Commanded.Aggregate.MultiTest.ExampleAggregate{ - events: [ - %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 1}, - %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 2} - ] - }, - event_3: %Commanded.Aggregate.MultiTest.ExampleAggregate{ - events: [ - %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 1}, - %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 2}, - %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 3} - ] - }, - event_4: %Commanded.Aggregate.MultiTest.ExampleAggregate{ - events: [ - %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 1}, - %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 2}, - %Commanded.Aggregate.MultiTest.ExampleAggregate.Event{data: 3} - ] - } - } end test "should reduce enum" do - {%ExampleAggregate{}, steps, events} = + {%ExampleAggregate{}, events} = %ExampleAggregate{} |> Multi.new() |> Multi.reduce([1, 2, 3], fn %ExampleAggregate{events: events}, index -> @@ -238,11 +235,10 @@ defmodule Commanded.Aggregate.MultiTest do |> Multi.run() assert events == [%Event{data: 1}, %Event{data: 2}, %Event{data: 3}] - assert steps == %{} end test "should store aggregate state under step_name on reduce" do - {%ExampleAggregate{}, steps, events} = + {%ExampleAggregate{}, events} = %ExampleAggregate{} |> Multi.new() |> Multi.reduce(:reduce_step, [1, 2, 3], fn %ExampleAggregate{events: events}, index -> @@ -266,6 +262,29 @@ defmodule Commanded.Aggregate.MultiTest do %Event{data: index} end) + |> Multi.execute(fn %ExampleAggregate{events: _events}, steps -> + assert steps == %{ + reduce_step: %ExampleAggregate{ + events: [ + %ExampleAggregate.Event{data: 1}, + %ExampleAggregate.Event{data: 2}, + %ExampleAggregate.Event{data: 3} + ] + }, + reduce_step_2: %ExampleAggregate{ + events: [ + %ExampleAggregate.Event{data: 1}, + %ExampleAggregate.Event{data: 2}, + %ExampleAggregate.Event{data: 3}, + %ExampleAggregate.Event{data: 4}, + %ExampleAggregate.Event{data: 5}, + %ExampleAggregate.Event{data: 6} + ] + } + } + + [] + end) |> Multi.run() assert events == [ @@ -276,26 +295,6 @@ defmodule Commanded.Aggregate.MultiTest do %Event{data: 5}, %Event{data: 6} ] - - assert steps == %{ - reduce_step: %ExampleAggregate{ - events: [ - %ExampleAggregate.Event{data: 1}, - %ExampleAggregate.Event{data: 2}, - %ExampleAggregate.Event{data: 3} - ] - }, - reduce_step_2: %ExampleAggregate{ - events: [ - %ExampleAggregate.Event{data: 1}, - %ExampleAggregate.Event{data: 2}, - %ExampleAggregate.Event{data: 3}, - %ExampleAggregate.Event{data: 4}, - %ExampleAggregate.Event{data: 5}, - %ExampleAggregate.Event{data: 6} - ] - } - } end end end diff --git a/test/support/aggregate_case.ex b/test/support/aggregate_case.ex index 275cd904..6b174202 100644 --- a/test/support/aggregate_case.ex +++ b/test/support/aggregate_case.ex @@ -67,7 +67,7 @@ defmodule Commanded.AggregateCase do {:error, _reason} = error -> throw(error) - {state, _steps, new_events} -> + {state, new_events} -> {state, events ++ new_events} end