Repository: commanded/commanded
Pull request #265 (2019-03-27 02:02:02)

Provide missing typespec


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0c925b7..7dbe65f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,9 +5,7 @@
 ### Enhancements
 
 - Update typespec for `data` and `metadata` fields in `Commanded.EventStore.EventData` struct ([#246](https://github.com/commanded/commanded/pull/246)).
-- Add `include_execution_result` and `aggregate_version` to typespec for router command dispatch ([#262](https://github.com/commanded/commanded/pull/262)).
 - Add `.formatter.exs` to Hex package ([#247](https://github.com/commanded/commanded/pull/247)).
-- Event upcasting ([#263](https://github.com/commanded/commanded/pull/263)).
 
 ## v0.18.0
 
diff --git a/README.md b/README.md
index 5aea16c..1b90e01 100644
--- a/README.md
+++ b/README.md
@@ -61,21 +61,14 @@ MIT License
       - [Composite command routers](guides/Commands.md#composite-command-routers)
     - [Middleware](guides/Commands.md#middleware)
       - [Example middleware](guides/Commands.md#example-middleware)
-    - [Composite command routers](guides/Commands.md#composite-command-routers)
   - [Events](guides/Events.md)
     - [Domain events](guides/Events.md#domain-events)
     - [Event handlers](guides/Events.md#event-handlers)
       - [Consistency guarantee](guides/Events.md#consistency-guarantee)
-    - [Upcasting events](guides/Events.md#upcasting-events)
   - [Process managers](guides/Process%20Managers.md)
     - [Example process manager](guides/Process%20Managers.md#example-process-manager)
   - [Supervision](guides/Supervision.md)
   - [Serialization](guides/Serialization.md)
-    - [Default JSON serializer](guides/Serialization.md#default-json-serializer)
-    - [Configuring JSON serialization](guides/Serialization.md#configuring-json-serialization)
-    - [Decoding event structs](guides/Serialization.md#decoding-event-structs)
-    - [Using an alternative serialization format](guides/Serialization.md#using-an-alternative-serialization-format)
-    - [Customising serialization](guides/Serialization.md#customising-serialization)
   - [Read model projections](guides/Read%20Model%20Projections.md)
 - [Deployment](guides/Deployment.md)
   - [Single node deployment](guides/Deployment.md#single-node-deployment)
@@ -93,7 +86,9 @@ MIT License
 
 ## Used in production?
 
-Yes, see the [companies using Commanded](https://github.com/commanded/commanded/wiki/Companies-using-Commanded).
+Yes, Commanded is being used in production.
+
+- Case Study: [Building a CQRS/ES web application in Elixir using Phoenix](https://10consulting.com/2017/01/04/building-a-cqrs-web-application-in-elixir-using-phoenix/)
 
 ## Example application
 
diff --git a/guides/Events.md b/guides/Events.md
index 6f7eab9..ae9163b 100644
--- a/guides/Events.md
+++ b/guides/Events.md
@@ -206,35 +206,3 @@ The available options are `:eventual` (default) and `:strong`:
 - *Eventual consistency* offers low latency but read model queries may reply with stale data since they may not have processed the persisted events.
 
 You request the consistency guarantee, either `:strong` or `:eventual`, when dispatching a command. Strong consistency will block the command dispatch and wait for all strongly consistent event handlers to successfully process all events created by the command. Whereas eventual consistency will immediately return after command dispatch, without waiting for any event handlers, even those configured for strong consistency.
-
-## Upcasting events
-
-Commanded supports upcasting of events at runtime using the `Commanded.Event.Upcaster` protocol.
-
-By implementing the upcaster protocol you can transform an event before it is used by a consumer. This might be an aggregate, an event handler, or a process manager. Because the upcaster changes the event at runtime, handlers only need to support the latest version. You can also use upcasting to change the type of event.
-
-### Examples
-
-Change the shape of an event by renaming a field:
-
-```elixir
-defimpl Commanded.Event.Upcaster, for: AnEvent do
-  def upcast(%AnEvent{} = event, _metadata) do
-    %AnEvent{name: name} = event
-
-    %AnEvent{event | first_name: name}
-  end
-end
-```
-
-Change the type of event by replacing a historical event with a new event:
-
-```elixir
-defimpl Commanded.Event.Upcaster, for: HistoricalEvent do
-  def upcast(%HistoricalEvent{} = event, _metadata) do
-    %HistoricalEvent{id: id, name: name} = event
-
-    %NewEvent{id: id, name: name}
-  end
-end
-```
diff --git a/lib/commanded/aggregates/aggregate.ex b/lib/commanded/aggregates/aggregate.ex
index 05dee02..88c446b 100644
--- a/lib/commanded/aggregates/aggregate.ex
+++ b/lib/commanded/aggregates/aggregate.ex
@@ -45,7 +45,6 @@ defmodule Commanded.Aggregates.Aggregate do
 
   alias Commanded.Aggregates.{Aggregate, ExecutionContext}
   alias Commanded.Event.Mapper
-  alias Commanded.Event.Upcast
   alias Commanded.EventStore
   alias Commanded.EventStore.{RecordedEvent, SnapshotData}
   alias Commanded.Snapshotting
@@ -241,11 +240,6 @@ defmodule Commanded.Aggregates.Aggregate do
     Logger.debug(fn -> describe(state) <> " received events: #{inspect(events)}" end)
 
     try do
-      state =
-        events
-        |> Upcast.upcast_event_stream()
-        |> Enum.reduce(state, &handle_event/2)
-
       state = Enum.reduce(events, state, &handle_event/2)
 
       {:noreply, state, lifespan_timeout}
diff --git a/lib/commanded/commands/router.ex b/lib/commanded/commands/router.ex
index ced5019..b420cce 100644
--- a/lib/commanded/commands/router.ex
+++ b/lib/commanded/commands/router.ex
@@ -447,6 +447,8 @@ defmodule Commanded.Commands.Router do
       """
       @spec dispatch(command :: struct) ::
               :ok
+              | {:ok, execution_result :: Commanded.Commands.Dispatcher.ExecutionResult.t()}
+              | {:ok, aggregate_version :: integer}
               | {:error, :unregistered_command}
               | {:error, :consistency_timeout}
               | {:error, reason :: term}
diff --git a/lib/commanded/event/handler.ex b/lib/commanded/event/handler.ex
index 46705fa..40311b6 100644
--- a/lib/commanded/event/handler.ex
+++ b/lib/commanded/event/handler.ex
@@ -188,9 +188,7 @@ defmodule Commanded.Event.Handler do
 
   require Logger
 
-  alias Commanded.Event.FailureContext
-  alias Commanded.Event.Handler
-  alias Commanded.Event.Upcast
+  alias Commanded.Event.{FailureContext, Handler}
   alias Commanded.EventStore
   alias Commanded.EventStore.RecordedEvent
   alias Commanded.Subscriptions
@@ -428,10 +426,7 @@ defmodule Commanded.Event.Handler do
     Logger.debug(fn -> describe(state) <> " received events: #{inspect(events)}" end)
 
     try do
-      state =
-        events
-        |> Upcast.upcast_event_stream()
-        |> Enum.reduce(state, &handle_event/2)
+      state = Enum.reduce(events, state, &handle_event/2)
 
       {:noreply, state}
     catch
diff --git a/lib/commanded/event/upcast.ex b/lib/commanded/event/upcast.ex
deleted file mode 100644
index 2d2cf8a..0000000
--- a/lib/commanded/event/upcast.ex
+++ /dev/null
@@ -1,16 +0,0 @@
-defmodule Commanded.Event.Upcast do
-  alias Commanded.Event.Upcaster
-  alias Commanded.EventStore.RecordedEvent
-
-  def upcast_event_stream(%Stream{} = event_stream) do
-    Stream.map(event_stream, &upcast_event/1)
-  end
-
-  def upcast_event_stream(event_stream) do
-    Enum.map(event_stream, &upcast_event/1)
-  end
-
-  defp upcast_event(%RecordedEvent{data: data, metadata: metadata} = event) do
-    %{event | data: Upcaster.upcast(data, metadata)}
-  end
-end
diff --git a/lib/commanded/event/upcaster.ex b/lib/commanded/event/upcaster.ex
deleted file mode 100644
index f5dbb76..0000000
--- a/lib/commanded/event/upcaster.ex
+++ /dev/null
@@ -1,27 +0,0 @@
-defprotocol Commanded.Event.Upcaster do
-  @doc """
-  Protocol to allow an event to be transformed before being passed to a
-  consumer.
-
-  You can use an upcaster to change the shape of an event
-  (e.g. add a new field with a default, rename a field).
-
-  Because the upcaster changes any historical event to the latest version,
-  consumers (aggregates, event handlers, and process managers) only need
-  to support the latest version.
-  """
-
-  @fallback_to_any true
-  @spec upcast(event :: struct(), metadata :: map()) :: struct()
-  def upcast(event, metadata)
-end
-
-defimpl Commanded.Event.Upcaster, for: Any do
-  @moduledoc """
-  The default implementation of the `Commanded.Event.Upcaster`.
-
-  This will return an event unchanged.
-  """
-
-  def upcast(event, _metadata), do: event
-end
diff --git a/lib/commanded/event_store/event_store.ex b/lib/commanded/event_store/event_store.ex
index 281b7a6..af7303e 100644
--- a/lib/commanded/event_store/event_store.ex
+++ b/lib/commanded/event_store/event_store.ex
@@ -80,13 +80,11 @@ defmodule Commanded.EventStore do
 
   Subscribe to all streams:
 
-      {:ok, subscription} =
-        Commanded.EventStore.subscribe_to(:all, "Example", self(), :current)
+      {:ok, subscription} = Commanded.EventStore.subscribe_to(:all, "Example", self())
 
   Subscribe to a single stream:
 
-      {:ok, subscription} =
-        Commanded.EventStore.subscribe_to("stream1", "Example", self(), :origin)
+      {:ok, subscription} = Commanded.EventStore.subscribe_to("stream1234", "Example", self())
 
   """
   @callback subscribe_to(stream_uuid | :all, subscription_name, subscriber, start_from) ::
@@ -172,12 +170,7 @@ defmodule Commanded.EventStore do
 
   def stream_forward(stream_uuid, start_version, read_batch_size)
       when is_binary(stream_uuid) and is_integer(start_version) and is_integer(read_batch_size) do
-    alias Commanded.Event.Upcast
-
-    case event_store_adapter().stream_forward(stream_uuid, start_version, read_batch_size) do
-      {:error, _} = error -> error
-      stream -> Upcast.upcast_event_stream(stream)
-    end
+    event_store_adapter().stream_forward(stream_uuid, start_version, read_batch_size)
   end
 
   @doc """
diff --git a/lib/commanded/process_managers/process_router.ex b/lib/commanded/process_managers/process_router.ex
index 7886298..ad898b0 100644
--- a/lib/commanded/process_managers/process_router.ex
+++ b/lib/commanded/process_managers/process_router.ex
@@ -6,7 +6,6 @@ defmodule Commanded.ProcessManagers.ProcessRouter do
 
   require Logger
 
-  alias Commanded.Event.Upcast
   alias Commanded.EventStore
   alias Commanded.EventStore.RecordedEvent
   alias Commanded.ProcessManagers.FailureContext
@@ -175,10 +174,7 @@ defmodule Commanded.ProcessManagers.ProcessRouter do
 
     %State{pending_events: pending_events} = state
 
-    unseen_events =
-      events
-      |> Enum.reject(&event_already_seen?(&1, state))
-      |> Upcast.upcast_event_stream()
+    unseen_events = Enum.reject(events, &event_already_seen?(&1, state))
 
     state =
       case {pending_events, unseen_events} do


Test patch:
diff --git a/test/aggregates/aggregate_subscription_test.exs b/test/aggregates/aggregate_subscription_test.exs
index d481261..8c3d041 100644
--- a/test/aggregates/aggregate_subscription_test.exs
+++ b/test/aggregates/aggregate_subscription_test.exs
@@ -14,7 +14,9 @@ defmodule Commanded.Aggregates.AggregateSubscriptionTest do
       :append_event_to_stream
     ]
 
-    test "should notify aggregate and mutate its state", %{account_number: account_number} do
+    test "should notify aggregate and mutate its state", context do
+      %{account_number: account_number} = context
+
       assert Aggregate.aggregate_version(BankAccount, account_number) == 2
 
       assert Aggregate.aggregate_state(BankAccount, account_number) == %BankAccount{
@@ -24,7 +26,9 @@ defmodule Commanded.Aggregates.AggregateSubscriptionTest do
              }
     end
 
-    test "should ignore already seen events", %{account_number: account_number} do
+    test "should ignore already seen events", context do
+      %{account_number: account_number} = context
+
       pid = Registration.whereis_name({BankAccount, account_number})
       events = account_number |> EventStore.stream_forward() |> Enum.to_list()
 
@@ -43,9 +47,9 @@ defmodule Commanded.Aggregates.AggregateSubscriptionTest do
              }
     end
 
-    test "should stop aggregate process when unexpected event received", %{
-      account_number: account_number
-    } do
+    test "should stop aggregate process when unexpected event received", context do
+      %{account_number: account_number} = context
+
       pid = Registration.whereis_name({BankAccount, account_number})
       ref = Process.monitor(pid)
 
@@ -66,39 +70,39 @@ defmodule Commanded.Aggregates.AggregateSubscriptionTest do
 
       assert_receive {:DOWN, ^ref, :process, _, :unexpected_event_received}
     end
-  end
 
-  defp open_account(_context) do
-    account_number = UUID.uuid4()
+    defp open_account(_context) do
+      account_number = UUID.uuid4()
 
-    {:ok, ^account_number} = AggregateSupervisor.open_aggregate(BankAccount, account_number)
+      {:ok, ^account_number} = AggregateSupervisor.open_aggregate(BankAccount, account_number)
 
-    context = %ExecutionContext{
-      command: %OpenAccount{account_number: account_number, initial_balance: 1_000},
-      handler: OpenAccountHandler,
-      function: :handle,
-      retry_attempts: 1
-    }
+      context = %ExecutionContext{
+        command: %OpenAccount{account_number: account_number, initial_balance: 1_000},
+        handler: OpenAccountHandler,
+        function: :handle,
+        retry_attempts: 1
+      }
 
-    {:ok, 1, _events} = Aggregate.execute(BankAccount, account_number, context)
+      {:ok, 1, _events} = Aggregate.execute(BankAccount, account_number, context)
 
-    [
-      account_number: account_number
-    ]
-  end
+      [
+        account_number: account_number
+      ]
+    end
 
-  # Write an event to the aggregate's stream, bypassing the aggregate process
-  defp append_event_to_stream(%{account_number: account_number}) do
-    event = %Commanded.EventStore.EventData{
-      event_type: "Elixir.Commanded.ExampleDomain.BankAccount.Events.MoneyDeposited",
-      data: %MoneyDeposited{
-        account_number: account_number,
-        transfer_uuid: UUID.uuid4(),
-        amount: 500,
-        balance: 1_500
+    # Write an event to the aggregate's stream, bypassing the aggregate process
+    defp append_event_to_stream(%{account_number: account_number}) do
+      event = %Commanded.EventStore.EventData{
+        event_type: "Elixir.Commanded.ExampleDomain.BankAccount.Events.MoneyDeposited",
+        data: %MoneyDeposited{
+          account_number: account_number,
+          transfer_uuid: UUID.uuid4(),
+          amount: 500,
+          balance: 1_500
+        }
       }
-    }
 
-    EventStore.append_to_stream(account_number, 1, [event])
+      EventStore.append_to_stream(account_number, 1, [event])
+    end
   end
 end
diff --git a/test/event/support/upcast/event_handler.ex b/test/event/support/upcast/event_handler.ex
deleted file mode 100644
index a7dfead..0000000
--- a/test/event/support/upcast/event_handler.ex
+++ /dev/null
@@ -1,15 +0,0 @@
-defmodule Commanded.Event.Upcast.EventHandler do
-  use Commanded.Event.Handler, name: __MODULE__
-
-  alias Commanded.Event.Upcast.Events.{EventOne, EventTwo, EventThree, EventFour}
-
-  def handle(%EventOne{} = e, _), do: send_reply(e)
-  def handle(%EventTwo{} = e, _), do: send_reply(e)
-  def handle(%EventThree{} = e, _), do: send_reply(e)
-  def handle(%EventFour{} = e, _), do: send_reply(e)
-
-  defp send_reply(%{reply_to: reply_to} = e) do
-    send(:erlang.list_to_pid(reply_to), e)
-    :ok
-  end
-end
diff --git a/test/event/support/upcast/events.ex b/test/event/support/upcast/events.ex
deleted file mode 100644
index e087b30..0000000
--- a/test/event/support/upcast/events.ex
+++ /dev/null
@@ -1,42 +0,0 @@
-defmodule Commanded.Event.Upcast.Events do
-  alias Commanded.Event.Upcaster
-
-  defmodule EventOne do
-    @derive Jason.Encoder
-    defstruct [:version, :reply_to, :process_id]
-  end
-
-  defmodule EventTwo do
-    @derive Jason.Encoder
-    defstruct [:version, :reply_to, :process_id]
-
-    defimpl Upcaster do
-      def upcast(%EventTwo{} = event, _metadata) do
-        %EventTwo{event | version: 2}
-      end
-    end
-  end
-
-  defmodule EventThree do
-    @derive Jason.Encoder
-    defstruct [:version, :reply_to, :process_id]
-  end
-
-  defmodule EventFour do
-    @derive Jason.Encoder
-    defstruct [:version, :name, :reply_to, :process_id]
-  end
-
-  defimpl Upcaster, for: EventThree do
-    def upcast(%EventThree{} = event, _metadata) do
-      data = Map.from_struct(event) |> Map.put(:name, "Chris") |> Map.put(:version, 2)
-
-      struct(EventFour, data)
-    end
-  end
-
-  defmodule Stop do
-    @derive Jason.Encoder
-    defstruct [:process_id]
-  end
-end
diff --git a/test/event/support/upcast/process_manager.ex b/test/event/support/upcast/process_manager.ex
deleted file mode 100644
index a4be3b5..0000000
--- a/test/event/support/upcast/process_manager.ex
+++ /dev/null
@@ -1,39 +0,0 @@
-defmodule Commanded.Event.Upcast.ProcessManager do
-  defmodule(Ok, do: defstruct([:id]))
-
-  defmodule Aggregate do
-    defstruct []
-    def execute(_, _), do: []
-    def apply(agg, _), do: agg
-  end
-
-  defmodule Router do
-    use Commanded.Commands.Router
-    dispatch Ok, to: Aggregate, identity: :id
-  end
-
-  @derive Jason.Encoder
-  defstruct []
-
-  alias Commanded.Event.Upcast.Events.{EventOne, EventTwo, EventThree, EventFour, Stop}
-
-  use Commanded.ProcessManagers.ProcessManager,
-    name: "UpcastProcessManager",
-    router: Router
-
-  def interested?(%EventOne{process_id: process_id}), do: {:start, process_id}
-  def interested?(%EventTwo{process_id: process_id}), do: {:start, process_id}
-  def interested?(%EventThree{process_id: process_id}), do: {:start, process_id}
-  def interested?(%EventFour{process_id: process_id}), do: {:start, process_id}
-  def interested?(%Stop{process_id: process_id}), do: {:stop, process_id}
-
-  def handle(_, %EventOne{} = e), do: send_reply(e)
-  def handle(_, %EventTwo{} = e), do: send_reply(e)
-  def handle(_, %EventThree{} = e), do: send_reply(e)
-  def handle(_, %EventFour{} = e), do: send_reply(e)
-
-  defp send_reply(%{reply_to: reply_to} = e) do
-    send(:erlang.list_to_pid(reply_to), e)
-    %Ok{id: UUID.uuid4()}
-  end
-end
diff --git a/test/event/support/upcast/upcast_aggregate.ex b/test/event/support/upcast/upcast_aggregate.ex
deleted file mode 100644
index 0fb6c68..0000000
--- a/test/event/support/upcast/upcast_aggregate.ex
+++ /dev/null
@@ -1,15 +0,0 @@
-defmodule Commanded.Event.Upcast.UpcastAggregate do
-  @moduledoc false
-  defstruct [:id]
-
-  alias Commanded.Event.Upcast.UpcastAggregate
-  alias Commanded.Event.Upcast.Events.EventFour
-
-  def apply(%UpcastAggregate{} = aggregate, %EventFour{} = event) do
-    %EventFour{reply_to: reply_to} = event
-
-    send(:erlang.list_to_pid(reply_to), event)
-
-    aggregate
-  end
-end
diff --git a/test/event/upcaster_test.exs b/test/event/upcaster_test.exs
deleted file mode 100644
index ef6e8fc..0000000
--- a/test/event/upcaster_test.exs
+++ /dev/null
@@ -1,130 +0,0 @@
-defmodule Event.UpcasterTest do
-  use Commanded.StorageCase
-
-  alias Commanded.Aggregates.Aggregate
-  alias Commanded.EventStore
-  alias Commanded.EventStore.EventData
-  alias Commanded.EventStore.RecordedEvent
-  alias Commanded.Event.Upcast.Events.{EventOne, EventTwo, EventThree, EventFour, Stop}
-  alias Commanded.Event.Upcast.UpcastAggregate
-
-  describe "upcast events from event store stream forward" do
-    test "will not upcast an event without an upcaster" do
-      assert %EventOne{version: version} =
-               struct(EventOne, version: 1) |> write_events() |> read_event()
-
-      assert version == 1
-    end
-
-    test "will upcast using defined upcaster" do
-      assert %EventTwo{version: version} =
-               struct(EventTwo, version: 1) |> write_events() |> read_event()
-
-      assert version == 2
-    end
-
-    test "can adapt new event from old event" do
-      assert %EventFour{name: name, version: version} =
-               struct(EventThree, version: 1) |> write_events() |> read_event()
-
-      assert name == "Chris"
-      assert version == 2
-    end
-  end
-
-  describe "upcast events received by event handler" do
-    alias Commanded.Event.Upcast.EventHandler
-
-    setup do
-      {:ok, handler} = EventHandler.start_link()
-
-      [
-        handler: handler,
-        reply_to: :erlang.pid_to_list(self())
-      ]
-    end
-
-    test "will receive upcasted events", %{reply_to: reply_to} do
-      write_events([
-        struct(EventOne, version: 1, reply_to: reply_to),
-        struct(EventTwo, version: 1, reply_to: reply_to),
-        struct(EventThree, version: 1, reply_to: reply_to)
-      ])
-
-      assert_receive %EventOne{version: 1}
-      assert_receive %EventTwo{version: 2}
-      assert_receive %EventFour{version: 2, name: "Chris"}
-      refute_receive %EventThree{}
-    end
-  end
-
-  describe "upcast events received by process manager" do
-    alias Commanded.Event.Upcast.ProcessManager
-
-    setup do
-      {:ok, manager} = ProcessManager.start_link()
-
-      [
-        manager: manager,
-        reply_to: :erlang.pid_to_list(self()),
-        process_id: UUID.uuid4(:hex)
-      ]
-    end
-
-    test "will receive upcasted events", %{reply_to: reply_to, process_id: process_id} do
-      write_events([
-        struct(EventOne, version: 1, reply_to: reply_to, process_id: process_id),
-        struct(EventTwo, version: 1, reply_to: reply_to, process_id: process_id),
-        struct(EventThree, version: 1, reply_to: reply_to, process_id: process_id),
-        struct(Stop, process_id: process_id)
-      ])
-
-      refute_receive %EventThree{}
-
-      assert_receive %EventOne{version: 1}
-      assert_receive %EventTwo{version: 2}
-      assert_receive %EventFour{version: 2, name: "Chris"}
-    end
-  end
-
-  describe "upcast events received by aggregate" do
-    test "should receive upcasted events" do
-      {:ok, pid} = Aggregate.start_link(UpcastAggregate, "upcast")
-
-      event = %RecordedEvent{
-        event_id: UUID.uuid4(),
-        event_number: 1,
-        stream_id: "upcast",
-        stream_version: 1,
-        data: %EventThree{version: 1, reply_to: :erlang.pid_to_list(self())},
-        metadata: %{}
-      }
-
-      # Send event which needs to be upcast
-      send(pid, {:events, [event]})
-
-      assert_receive %EventFour{version: 2, name: "Chris"}
-    end
-  end
-
-  defp write_events(events) do
-    stream_uuid = UUID.uuid4()
-    events = events |> List.wrap() |> Enum.map(&create_event/1)
-
-    :ok = EventStore.append_to_stream(stream_uuid, :any_version, events)
-
-    stream_uuid
-  end
-
-  defp create_event(%{__struct__: event_type} = data) do
-    %EventData{
-      event_type: to_string(event_type),
-      data: data,
-      metadata: %{}
-    }
-  end
-
-  defp read_event(stream_uuid) do
-    stream_uuid |> EventStore.stream_forward() |> Enum.at(0) |> Map.get(:data)
-  end
-end
diff --git a/test/example_domain/bank_account/bank_account.ex b/test/example_domain/bank_account/bank_account.ex
index b683c9e..bc20dd9 100644
--- a/test/example_domain/bank_account/bank_account.ex
+++ b/test/example_domain/bank_account/bank_account.ex
@@ -1,87 +1,89 @@
 defmodule Commanded.ExampleDomain.BankAccount do
   @moduledoc false
   @derive Jason.Encoder
-  defstruct [:account_number, :state, balance: 0]
+  defstruct account_number: nil,
+            balance: 0,
+            state: nil
 
   alias Commanded.ExampleDomain.BankAccount
 
   defmodule Commands do
     defmodule OpenAccount do
-      defstruct [:account_number, :initial_balance]
+      @derive Jason.Encoder
+      defstruct([:account_number, :initial_balance])
     end
 
     defmodule DepositMoney do
-      defstruct [:account_number, :transfer_uuid, :amount]
+      @derive Jason.Encoder
+      defstruct([:account_number, :transfer_uuid, :amount])
     end
 
     defmodule WithdrawMoney do
-      defstruct [:account_number, :transfer_uuid, :amount]
+      @derive Jason.Encoder
+      defstruct([:account_number, :transfer_uuid, :amount])
     end
 
     defmodule CloseAccount do
-      defstruct [:account_number]
+      @derive Jason.Encoder
+      defstruct([:account_number])
     end
   end
 
   defmodule Events do
     defmodule BankAccountOpened do
       @derive Jason.Encoder
-      defstruct [:account_number, :initial_balance]
+      defstruct([:account_number, :initial_balance])
     end
 
     defmodule MoneyDeposited do
       @derive Jason.Encoder
-      defstruct [:account_number, :transfer_uuid, :amount, :balance]
+      defstruct([:account_number, :transfer_uuid, :amount, :balance])
     end
 
     defmodule MoneyWithdrawn do
       @derive Jason.Encoder
-      defstruct [:account_number, :transfer_uuid, :amount, :balance]
+      defstruct([:account_number, :transfer_uuid, :amount, :balance])
     end
 
     defmodule AccountOverdrawn do
       @derive Jason.Encoder
-      defstruct [:account_number, :balance]
+      defstruct([:account_number, :balance])
     end
 
     defmodule BankAccountClosed do
       @derive Jason.Encoder
-      defstruct [:account_number]
+      defstruct([:account_number])
     end
   end
 
-  alias Commands.OpenAccount
-  alias Commands.DepositMoney
-  alias Commands.WithdrawMoney
-  alias Commands.CloseAccount
-
-  alias Events.BankAccountOpened
-  alias Events.MoneyDeposited
-  alias Events.MoneyWithdrawn
-  alias Events.AccountOverdrawn
-  alias Events.BankAccountClosed
-
-  def open_account(
-        %BankAccount{state: nil},
-        %OpenAccount{initial_balance: initial_balance} = command
-      )
-      when is_number(initial_balance) and initial_balance > 0 do
-    %OpenAccount{account_number: account_number} = command
+  alias Commands.{OpenAccount, DepositMoney, WithdrawMoney, CloseAccount}
 
-    %BankAccountOpened{account_number: account_number, initial_balance: initial_balance}
+  alias Events.{
+    BankAccountOpened,
+    MoneyDeposited,
+    MoneyWithdrawn,
+    AccountOverdrawn,
+    BankAccountClosed
+  }
+
+  def open_account(%BankAccount{state: nil}, %OpenAccount{initial_balance: "clearly invalid"}) do
+    {:error, :invalid_initial_balance}
   end
 
-  def open_account(%BankAccount{state: nil}, %OpenAccount{}),
-    do: {:error, :invalid_initial_balance}
+  def open_account(%BankAccount{state: nil}, %OpenAccount{
+        account_number: account_number,
+        initial_balance: initial_balance
+      })
+      when is_number(initial_balance) and initial_balance > 0 do
+    %BankAccountOpened{account_number: account_number, initial_balance: initial_balance}
+  end
 
-  def deposit(
-        %BankAccount{state: :active} = account,
-        %DepositMoney{amount: amount} = command
-      )
+  def deposit(%BankAccount{state: :active, balance: balance}, %DepositMoney{
+        account_number: account_number,
+        transfer_uuid: transfer_uuid,
+        amount: amount
+      })
       when is_number(amount) and amount > 0 do
-    %BankAccount{balance: balance} = account
-    %DepositMoney{account_number: account_number, transfer_uuid: transfer_uuid} = command
-
     balance = balance + amount
 
     %MoneyDeposited{
@@ -92,14 +94,12 @@ defmodule Commanded.ExampleDomain.BankAccount do
     }
   end
 
-  def withdraw(
-        %BankAccount{state: :active} = account,
-        %WithdrawMoney{amount: amount} = command
-      )
+  def withdraw(%BankAccount{state: :active, balance: balance}, %WithdrawMoney{
+        account_number: account_number,
+        transfer_uuid: transfer_uuid,
+        amount: amount
+      })
       when is_number(amount) and amount > 0 do
-    %BankAccount{balance: balance} = account
-    %WithdrawMoney{account_number: account_number, transfer_uuid: transfer_uuid} = command
-
     case balance - amount do
       balance when balance < 0 ->
         [
@@ -132,9 +132,10 @@ defmodule Commanded.ExampleDomain.BankAccount do
 
   # State mutators
 
-  def apply(%BankAccount{} = state, %BankAccountOpened{} = event) do
-    %BankAccountOpened{account_number: account_number, initial_balance: initial_balance} = event
-
+  def apply(%BankAccount{} = state, %BankAccountOpened{
+        account_number: account_number,
+        initial_balance: initial_balance
+      }) do
     %BankAccount{state | account_number: account_number, balance: initial_balance, state: :active}
   end