View Source Commanded.Application behaviour (Commanded v1.4.1)

Defines a Commanded application.

The application expects at least an :otp_app option to be specified. It should point to an OTP application that has the application configuration.

For example, the application:

defmodule MyApp.Application do
  use Commanded.Application, otp_app: :my_app

  router(MyApp.Router)
end

Could be configured with:

# config/config.exs
config :my_app, MyApp.Application
  event_store: [
    adapter: Commanded.EventStore.Adapters.EventStore,
    event_store: MyApp.EventStore
  ],
  pubsub: :local,
  registry: :local

Alternatively, you can include the configuration when defining the application:

defmodule MyApp.Application do
  use Commanded.Application,
    otp_app: :my_app,
    event_store: [
      adapter: Commanded.EventStore.Adapters.EventStore,
      event_store: MyApp.EventStore
    ],
    pubsub: :local,
    registry: :local

  router(MyApp.Router)
end

A Commanded application must be started before it can be used:

{:ok, _pid} = MyApp.Application.start_link()

Instead of starting the application manually, you should use a Supervisor.

supervision

Supervision

Use a supervisor to start your Commanded application:

Supervisor.start_link([
  MyApp.Application
], strategy: :one_for_one)

command-routing

Command routing

Commanded applications are also composite routers allowing you to include one or more routers within an application.

example

Example

defmodule MyApp.Application do
  use Commanded.Application, otp_app: :my_app

  router(MyApp.Accounts.Router)
  router(MyApp.Billing.Router)
  router(MyApp.Notifications.Router)
end

See Commanded.Commands.CompositeRouter for details.

command-dispatch

Command dispatch

Once a router has been configured you can dispatch a command via the application:

:ok = MyApp.dispatch(command, opts)

See dispatch/1 and dispatch/2 for details.

dynamic-named-applications

Dynamic named applications

An application can be provided with a name as an option to start_link/1. This can be used to start the same application multiple times, each using its own separately configured and isolated event store. Each application must be started with a unique name.

Multiple instances of the same event handler or process manager can be started by referring to a started application by its name. The event store operations can also be scoped to an application by referring to its name.

example-1

Example

Start an application process for each tenant in a multi-tenanted app, guaranteeing that the data and processing remains isolated between tenants.

for tenant <- [:tenant1, :tenant2, :tenant3] do
  {:ok, _app} = MyApp.Application.start_link(name: tenant)
end

Typically you would start the applications using a supervisor:

children =
  for tenant <- [:tenant1, :tenant2, :tenant3] do
    {MyApp.Application, name: tenant}
  end

Supervisor.start_link(children, strategy: :one_for_one)

To dispatch a command you must provide the application name:

:ok = MyApp.Application.dispatch(command, application: :tenant1)

default-dispatch-options

Default dispatch options

An application can be configured with default command dispatch options such as :consistency, :timeout, and :returning. Any defaults will be used unless overridden by options provided to the dispatch function.

defmodule MyApp.Application do
  use Commanded.Application,
    otp_app: :my_app,
    default_dispatch_opts: [
      consistency: :eventual,
      returning: :aggregate_version
    ]
end

See the Commanded.Commands.Router module for more details about the supported options.

telemetry

Telemetry

  • [:commanded, :application, :dispatch, :start]

    • Description: Emitted when an application starts dispatching a command
    • Measurements: %{system_time: integer()}
    • Metadata: %{application: Commanded.Application.t(), execution_context: Commanded.Aggregates.ExecutionContext.t()}
  • [:commanded, :application, :dispatch, :stop]

    • Description: Emitted when an application stops dispatching a command
    • Measurements: %{duration: non_neg_integer()}
    • Metadata: %{application: Commanded.Application.t(), execution_context: Commanded.Aggregates.ExecutionContext.t(), error: nil | any()}

Link to this section Summary

Callbacks

Returns the application configuration stored in the :otp_app environment.

Dispatch a registered command.

Dispatch a registered command.

A callback executed when the application starts.

Starts the application supervisor.

Shuts down the application.

Link to this section Types

@type options() :: [{:name, nil | atom()}]
@type t() :: module()

Link to this section Callbacks

@callback config() :: Keyword.t()

Returns the application configuration stored in the :otp_app environment.

@callback dispatch(command :: struct()) :: Commanded.Commands.Router.dispatch_resp()

Dispatch a registered command.

Link to this callback

dispatch(command, timeout_or_opts)

View Source
@callback dispatch(
  command :: struct(),
  timeout_or_opts :: non_neg_integer() | :infinity | Keyword.t()
) :: Commanded.Commands.Router.dispatch_resp()

Dispatch a registered command.

  • command is a command struct which must be registered with a Commanded.Commands.Router and included in the application.

  • timeout_or_opts is either an integer timeout or a keyword list of options.

    The timeout must be an integer greater than zero which specifies how many milliseconds to allow the command to be handled, or the atom :infinity to wait indefinitely. The default timeout value is five seconds.

    Alternatively, an options keyword list can be provided, it supports the following options.

    Options:

    • causation_id - an optional UUID used to identify the cause of the command being dispatched.

    • correlation_id - an optional UUID used to correlate related commands/events together.

    • consistency - one of :eventual (default) or :strong. By setting the consistency to :strong a successful command dispatch will block until all strongly consistent event handlers and process managers have handled all events created by the command.

    • metadata - an optional map containing key/value pairs comprising the metadata to be associated with all events created by the command.

    • returning - to choose what response is returned from a successful command dispatch. The default is to return an :ok.

      The available options are:

      • :aggregate_state - to return the update aggregate state in the successful response: {:ok, aggregate_state}.

      • :aggregate_version - to include the aggregate stream version in the successful response: {:ok, aggregate_version}.

      • :execution_result - to return a Commanded.Commands.ExecutionResult struct containing the aggregate's identity, version, and any events produced from the command along with their associated metadata.

      • false - don't return anything except an :ok.

    • timeout - as described above.

Returns :ok on success unless the :returning option is specified where it returns one of {:ok, aggregate_state}, {:ok, aggregate_version}, or {:ok, %Commanded.Commands.ExecutionResult{}}.

Returns {:error, reason} on failure.

example

Example

command = %OpenAccount{account_number: "ACC123", initial_balance: 1_000}

:ok = BankApp.dispatch(command, timeout: 30_000)
@callback init(config :: Keyword.t()) :: {:ok, Keyword.t()}

A callback executed when the application starts.

It must return {:ok, keyword} with the updated list of configuration.

@callback start_link(opts :: options()) ::
  {:ok, pid()} | {:error, {:already_started, pid()}} | {:error, term()}

Starts the application supervisor.

Returns {:ok, pid} on success, {:error, {:already_started, pid}} if the application is already started, or {:error, term} in case anything else goes wrong.

@callback stop(pid(), timeout()) :: :ok

Shuts down the application.