Skip to content

Commit

Permalink
Merge pull request commanded#359 from jwilger/document-function-option
Browse files Browse the repository at this point in the history
Test and document dispatch `:function` option
  • Loading branch information
slashdotdash authored Mar 27, 2020
2 parents 2d9950f + 934db8f commit 47bae25
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 8 deletions.
14 changes: 13 additions & 1 deletion guides/Commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,23 @@ It is also possible to route a command directly to an aggregate, without requiri
defmodule BankRouter do
use Commanded.Commands.Router

# will route to `BankAccount.execute/2`
dispatch OpenAccount, to: BankAccount, identity: :account_number
end
```

The aggregate must implement an `execute/2` function that receives the aggregate's state and the command to execute.
By default, the aggregate module's `execute/2` function will be called with the aggregate's state and the command to execute. Using this approach, you will create an `execute/2` clause that pattern-matches on each command that the aggregate should handle.

Alternatively, you may specify the name of a function (also receiving both the aggregate state and the command) on your aggregate module to which the command will be dispatched:

```elixir
defmodule BankRouter do
use Commanded.Commands.Router

# Will route to `BankAccount.open_account/2`
dispatch OpenAccount, to: BankAccount, function: :open_account, identity: :account_number
end
```

### Dispatching commands

Expand Down
25 changes: 20 additions & 5 deletions lib/commanded/commands/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ defmodule Commanded.Commands.Router do
the aggregate's state and the command to execute. Usually the command handler
module will forward the command to the aggregate.
Once configured, you can either dispatch a command using the module by and
specify the application:
Once configured, you can either dispatch a command by using the module and
specifying the application:
command = %OpenAccount{account_number: "ACC123", initial_balance: 1_000}
:ok = BankRouter.dispatch(command, application: BankApp)
Or, more simply you should include the router module in your application:
Or, more simply, you should include the router module in your application:
defmodule BankApp do
use Commanded.Application, otp_app: :my_app
Expand All @@ -52,11 +52,26 @@ defmodule Commanded.Commands.Router do
defmodule BankRouter do
use Commanded.Commands.Router
# Will route to `BankAccount.open_account/2`
dispatch OpenAccount, to: BankAccount, identity: :account_number
end
The aggregate must implement an `execute/2` function that receives the
aggregate's state and the command being executed.
By default, you must define an `execute/2` function on the aggregate module, which will be
called with the aggregate's state and the command to execute. Using this approach, you must
create an `execute/2` clause that pattern-matches on each command that the aggregate should
handle.
Alternatively, you may specify the name of a function (also receiving both the aggregate state
and the command) on your aggregate module to which the command will be dispatched:
### Example
defmodule BankRouter do
use Commanded.Commands.Router
# Will route to `BankAccount.open_account/2`
dispatch OpenAccount, to: BankAccount, function: :open_account, identity: :account_number
end
## Define aggregate identity
Expand Down
6 changes: 5 additions & 1 deletion test/commands/routing_commands_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ defmodule Commanded.Commands.RoutingCommandsTest do

describe "routing to aggregate" do
alias Commanded.Commands.AggregateRouter
alias Commanded.Commands.AggregateRoot.Command
alias Commanded.Commands.AggregateRoot.{Command, Command2}

test "should dispatch command to registered handler" do
assert :ok = AggregateRouter.dispatch(%Command{uuid: UUID.uuid4()}, @dispatch_opts)
Expand All @@ -76,6 +76,10 @@ defmodule Commanded.Commands.RoutingCommandsTest do
assert {:error, :unregistered_command} =
AggregateRouter.dispatch(%UnregisteredCommand{}, @dispatch_opts)
end

test "should allow dispatching to a function other than execute/2" do
assert :ok = AggregateRouter.dispatch(%Command2{uuid: UUID.uuid4()}, @dispatch_opts)
end
end

describe "identify aggregate prefix by string" do
Expand Down
3 changes: 3 additions & 0 deletions test/commands/support/aggregate_root.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ defmodule Commanded.Commands.AggregateRoot do
alias Commanded.Commands.AggregateRoot

defmodule Command, do: defstruct(uuid: nil)
defmodule Command2, do: defstruct(uuid: nil)

defstruct uuid: nil

def execute(%AggregateRoot{}, %Command{}), do: []

def custom_function_name(%AggregateRoot{}, %Command2{}), do: []
end
3 changes: 2 additions & 1 deletion test/commands/support/aggregate_router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ defmodule Commanded.Commands.AggregateRouter do
use Commanded.Commands.Router

alias Commanded.Commands.AggregateRoot
alias Commanded.Commands.AggregateRoot.Command
alias Commanded.Commands.AggregateRoot.{Command, Command2}

dispatch Command, to: AggregateRoot, identity: :uuid
dispatch Command2, to: AggregateRoot, function: :custom_function_name, identity: :uuid
end

0 comments on commit 47bae25

Please sign in to comment.