Router Generation

View Source

AshCommanded automatically generates Commanded Routers for dispatching commands to the appropriate handlers and aggregates based on your domain structure.

Domain-specific Routers

For each Ash.Domain containing resources with the AshCommanded.Commanded.Dsl extension, a domain-specific router is generated:

defmodule ECommerce.Store.Router do
  use Commanded.Commands.Router

  # For each resource in the domain
  identify ECommerce.ProductAggregate, by: :id
  dispatch [ECommerce.Commands.CreateProduct, ECommerce.Commands.UpdateProduct], to: ECommerce.ProductAggregate
  
  identify ECommerce.CustomerAggregate, by: :id
  dispatch [ECommerce.Commands.RegisterCustomer, ECommerce.Commands.UpdateStatus], to: ECommerce.CustomerAggregate
end

These domain routers handle command routing within a specific domain context.

Main Application Router

AshCommanded also generates a main application router at AshCommanded.Router. The behavior of this router depends on the domain structure:

Single Domain Setup

For applications with a single domain, the main router directly routes commands to aggregates:

defmodule AshCommanded.Router do
  use Commanded.Commands.Router

  # Direct command routing for all resources
  identify ECommerce.ProductAggregate, by: :id
  dispatch [ECommerce.Commands.CreateProduct, ECommerce.Commands.UpdateProduct], to: ECommerce.ProductAggregate
  
  identify ECommerce.CustomerAggregate, by: :id
  dispatch [ECommerce.Commands.RegisterCustomer, ECommerce.Commands.UpdateStatus], to: ECommerce.CustomerAggregate
end

Multiple Domain Setup

For applications with multiple domains, the main router forwards commands to the appropriate domain router:

defmodule AshCommanded.Router do
  use Commanded.Commands.Router

  # Forward to domain routers
  forward ECommerce.Store.Router
  forward ECommerce.Marketing.Router
  forward ECommerce.Shipping.Router
end

Command Dispatching

With these routers in place, you can dispatch commands using the dispatch/1 or dispatch/2 functions:

# Basic command dispatch
command = %ECommerce.Commands.CreateProduct{id: "123", name: "Premium Widget", price: 29.99}
AshCommanded.Router.dispatch(command)

# With options
command = %ECommerce.Commands.CreateProduct{id: "123", name: "Premium Widget", price: 29.99}
AshCommanded.Router.dispatch(command, correlation_id: UUID.uuid4(), metadata: %{user_id: "user-123"})

The command will be routed to the appropriate aggregate and handler based on the command type and the resource's identity.

Identity Resolution

By default, the router uses the resource's primary identity key or the specified identity_field in the command definition.

commanded do
  commands do
    command :create_product do
      fields([:id, :name, :price])
      identity_field(:id)  # Explicitly specify the identity field
    end
  end
end

If no identity field is specified, the router will try to use:

  1. The first field from the resource's primary identity
  2. The :id field as a fallback