Repository: commanded/commanded
Pull request #166 (2018-04-07 20:22:55)

Allow aggregate uuid to be of any type that implements String.Chars
Hey guys, I'm using sometimes URIs as an aggregate ids in my project, so i was thinking it may be handy to allow pass structures as an id, since they can be easily represented by a string. 


Patch:
diff --git a/lib/commanded/aggregates/supervisor.ex b/lib/commanded/aggregates/supervisor.ex
index 0e57009..ff00163 100644
--- a/lib/commanded/aggregates/supervisor.ex
+++ b/lib/commanded/aggregates/supervisor.ex
@@ -46,6 +46,13 @@ defmodule Commanded.Aggregates.Supervisor do
     end
   end
 
+  def open_aggregate(aggregate_module, aggregate_uuid) do
+    case String.Chars.impl_for(aggregate_uuid) do
+      nil -> {:error, :unsupported_uuid_type}
+      _ -> open_aggregate(aggregate_module, to_string(aggregate_uuid))
+    end
+  end
+
   def init(_) do
     children = [
       worker(Commanded.Aggregates.Aggregate, [], restart: :temporary)
diff --git a/lib/commanded/commands/dispatcher.ex b/lib/commanded/commands/dispatcher.ex
index cbd1fb7..75c212a 100644
--- a/lib/commanded/commands/dispatcher.ex
+++ b/lib/commanded/commands/dispatcher.ex
@@ -63,7 +63,7 @@ defmodule Commanded.Commands.Dispatcher do
     %Pipeline{assigns: %{aggregate_uuid: aggregate_uuid}} = pipeline,
     %Payload{aggregate_module: aggregate_module, timeout: timeout} = payload)
   do
-    {:ok, ^aggregate_uuid} = Commanded.Aggregates.Supervisor.open_aggregate(aggregate_module, aggregate_uuid)
+    {:ok, aggregate_uuid} = Commanded.Aggregates.Supervisor.open_aggregate(aggregate_module, aggregate_uuid)
 
     context = to_execution_context(pipeline, payload)
     task = Task.Supervisor.async_nolink(Commanded.Commands.TaskDispatcher, Aggregates.Aggregate, :execute, [aggregate_module, aggregate_uuid, context, timeout])
@@ -140,7 +140,7 @@ defmodule Commanded.Commands.Dispatcher do
           }
         %{include_aggregate_version: true} ->
           {:ok, pipeline.assigns.aggregate_version}
-          
+
         _ ->
           :ok
       end


Test patch:
diff --git a/test/aggregates/supervisor_test.exs b/test/aggregates/supervisor_test.exs
index b934c68..171a67e 100644
--- a/test/aggregates/supervisor_test.exs
+++ b/test/aggregates/supervisor_test.exs
@@ -1,9 +1,24 @@
 defmodule Commanded.Aggregates.SupervisorTest do
   use Commanded.StorageCase
 
-  test "should allow an integer, atom, or string as an aggregate_uuid" do
-    Enum.each([1, :atom, "string"], fn aggregate_uuid ->
-      {:ok, ^aggregate_uuid} = Commanded.Aggregates.Supervisor.open_aggregate(Commanded.ExampleDomain.BankAccount, aggregate_uuid)
-    end)
+  defmodule CustomID do
+    defstruct [:name, :scope]
+  end
+
+  defimpl String.Chars, for: CustomID do
+    def to_string(%CustomID{name: name, scope: scope}), do: scope <> ":" <> name
+  end
+
+  describe "aggregate_uuid" do
+    test "should allow an integer, atom or string" do
+      Enum.each([1, :atom, "string"], fn aggregate_uuid ->
+        {:ok, ^aggregate_uuid} = Commanded.Aggregates.Supervisor.open_aggregate(Commanded.ExampleDomain.BankAccount, aggregate_uuid)
+      end)
+    end
+
+    test "should allow any object since it can be represented as a string" do
+      custom_id = %CustomID{name: "bar", scope: "foo"}
+      assert {:ok, "foo:bar"} == Commanded.Aggregates.Supervisor.open_aggregate(Commanded.ExampleDomain.BankAccount, custom_id)
+    end
   end
 end