Skip to content

Commit

Permalink
feat: add identify_aggregate to command router (#133)
Browse files Browse the repository at this point in the history
  • Loading branch information
yordis committed May 5, 2024
1 parent a163f82 commit 58f43c2
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 5 deletions.
3 changes: 2 additions & 1 deletion apps/one_piece_commanded/.formatter.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
locals_without_parens = [
register_type: 2,
import_type_provider: 1,
dispatch_transaction_script: 2
dispatch_transaction_script: 2,
identify_aggregate: 1
]

[
Expand Down
24 changes: 22 additions & 2 deletions apps/one_piece_commanded/lib/one_piece/commanded/aggregate.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ defmodule OnePiece.Commanded.Aggregate do
It adds an `apply/2` callback to the module as a fallback, return the aggregate as it is.
### Options
- `:identifier` - The aggregate identifier key.
- `:stream_prefix` (optional) - The prefix to be used for the identity.
## Using
- `OnePiece.Commanded.Entity`
Expand All @@ -49,12 +54,27 @@ defmodule OnePiece.Commanded.Aggregate do
end
end
"""
@spec __using__(opts :: []) :: any()
@spec __using__(
opts ::
OnePiece.Commanded.Entity.using_opts()
| [stream_prefix: String.t() | nil]
) :: any()
defmacro __using__(opts \\ []) do
{opts, entity_opts} = Keyword.split(opts, [:stream_prefix])
stream_prefix = Keyword.get(opts, :stream_prefix)

quote do
use OnePiece.Commanded.Entity, unquote(opts)
use OnePiece.Commanded.Entity, unquote(entity_opts)
@behaviour OnePiece.Commanded.Aggregate
@before_compile OnePiece.Commanded.Aggregate

@doc """
Returns `#{inspect(unquote(stream_prefix))}` as the identity prefix.
"""
@spec stream_prefix :: String.t() | nil
def stream_prefix do
unquote(stream_prefix)
end
end
end

Expand Down
34 changes: 34 additions & 0 deletions apps/one_piece_commanded/lib/one_piece/commanded/command_router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,38 @@ defmodule OnePiece.Commanded.CommandRouter do
Commanded.Commands.Router.dispatch(unquote(command_module), unquote(opts))
end
end

@doc """
Identify a given aggregate.
## Example
defmodule BankAccount do
use OnePiece.Commanded.Aggregate
identifier: :uuid,
stream_prefix: "bank-account-"
embedded_schema do
# ...
end
end
defmodule Router do
use OnePiece.Commanded.CommandRouter
identify_aggregate OpenBankAccount
end
"""
defmacro identify_aggregate(aggregate_module) do
aggregate_module = Macro.expand(aggregate_module, __CALLER__)

Code.ensure_compiled!(aggregate_module)

opts = [
by: Kernel.apply(aggregate_module, :identifier, []),
prefix: Kernel.apply(aggregate_module, :stream_prefix, [])
]

quote do
Commanded.Commands.Router.identify(unquote(aggregate_module), unquote(opts))
end
end
end
4 changes: 3 additions & 1 deletion apps/one_piece_commanded/lib/one_piece/commanded/entity.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ defmodule OnePiece.Commanded.Entity do
"""
@type identifier_opt :: atom() | {key_name :: atom(), type :: atom()} | {key_name :: atom(), type :: module()}

@type using_opts :: [identifier: identifier_opt()]

@doc """
Converts the module into an `t:t/0`.
Expand Down Expand Up @@ -57,7 +59,7 @@ defmodule OnePiece.Commanded.Entity do
end
end
"""
@spec __using__(opts :: [identifier: identifier_opt()]) :: any()
@spec __using__(opts :: using_opts()) :: any()
defmacro __using__(opts \\ []) do
unless Keyword.has_key?(opts, :identifier) do
raise ArgumentError, "missing :identifier key"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ defmodule OnePiece.Commanded.CommandRouterTest do

alias TestSupport.CommandRouterExample.{
CommandRouter,
OpenBankAccount
OpenBankAccount,
CloseBankAccount,
BankAccount
}

@dispatch_opts [
Expand All @@ -16,6 +18,18 @@ defmodule OnePiece.Commanded.CommandRouterTest do
:ok
end

describe "identify_aggregate/1" do
test "identifies an aggregate" do
{:ok, result} =
%{uuid: "uuid-1"}
|> CloseBankAccount.new!()
|> CommandRouter.dispatch(@dispatch_opts)

assert result.aggregate_uuid == "bank-account2-uuid-1"
assert result.aggregate_state == %BankAccount{closed?: true, uuid: "uuid-1"}
end
end

describe "dispatch_transaction/2" do
test "dispatches a transaction" do
{:ok, result} =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
defmodule TestSupport.CommandRouterExample.BankAccount do
@moduledoc false
use OnePiece.Commanded.Aggregate,
identifier: :uuid,
stream_prefix: "bank-account2-"

alias TestSupport.CommandRouterExample.{BankAccountOpened, BankAccountClosed}

embedded_schema do
field :closed?, :boolean, default: false
end

@impl OnePiece.Commanded.Aggregate
def apply(aggregate, %BankAccountOpened{} = event) do
aggregate
|> Map.put(:uuid, event.uuid)
end

@impl OnePiece.Commanded.Aggregate
def apply(aggregate, %BankAccountClosed{} = event) do
aggregate
|> Map.put(:uuid, event.uuid)
|> Map.put(:closed?, true)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule TestSupport.CommandRouterExample.BankAccountClosed do
@moduledoc false
use OnePiece.Commanded.Event, aggregate_identifier: :uuid

embedded_schema do
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
defmodule TestSupport.CommandRouterExample.CloseBankAccount do
@moduledoc false
use OnePiece.Commanded.CommandHandler

use OnePiece.Commanded.Command,
aggregate_identifier: :uuid

alias TestSupport.CommandRouterExample.{
BankAccountClosed,
CloseBankAccount,
BankAccount
}

embedded_schema do
end

def handle(%BankAccount{} = _aggregate, %CloseBankAccount{} = command) do
%BankAccountClosed{uuid: command.uuid}
end
end
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
defmodule TestSupport.CommandRouterExample.CommandRouter do
@moduledoc false
use OnePiece.Commanded.CommandRouter

identify_aggregate(TestSupport.CommandRouterExample.BankAccount)

dispatch(TestSupport.CommandRouterExample.CloseBankAccount,
to: TestSupport.CommandRouterExample.CloseBankAccount,
aggregate: TestSupport.CommandRouterExample.BankAccount
)

register_transaction_script(TestSupport.CommandRouterExample.OpenBankAccount)
end

0 comments on commit 58f43c2

Please sign in to comment.