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

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

Use a supervisor to start your Commanded application:

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

Command routing

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

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

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

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

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

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

  • [: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()}

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.

Types

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

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 - to choose the consistency guarantee of the command dispatch.

      The available options are:

      • :eventual (default) - a successful command dispatch will return immediately.

      • :strong - a successful command dispatch will block until all strongly consistent event handlers and process managers have handled all events created by the command.

      • An explicit list of event handler and process manager modules (or their configured names), containing only those handlers you'd like to wait for. No other handlers will be awaited on, regardless of their own configured consistency setting. e.g. [ExampleHandler, AnotherHandler] or ["ExampleHandler", "AnotherHandler"]

    • 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

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.