Cqrs.Command behaviour (cqrs_tools v0.4.7) View Source

The Command macro allows you to define a command that encapsulates a struct definition, data validation, dependency validation, and dispatching of the command.

Options

  • require_all_fields (:boolean) - If true, all fields will be required. Defaults to true
  • dispatcher (:atom) - A module that defines a dispatch/2.
  • default_event_values (:boolean) - If true, events created using derive_event/1 or derive_event/2, will inherit default values from their command fields. Defaults to true.

Examples

defmodule CreateUser do
  use Cqrs.Command

  field :email, :string
  field :name, :string

  internal_field :id, :binary_id

  @impl true
  def handle_validate(command, _opts) do
    Ecto.Changeset.validate_format(command, :email, ~r/@/)
  end

  @impl true
  def after_validate(%{email: email} = command) do
    Map.put(command, :id, UUID.uuid5(:oid, email))
  end

  @impl true
  def handle_dispatch(_command, _opts) do
    {:ok, :dispatched}
  end
end

Creation

iex> {:error, errors} = CreateUser.new()
...> errors
%{email: ["can't be blank"], name: ["can't be blank"]}

iex> {:ok, %CreateUser{email: email, name: name, id: id}} = CreateUser.new(email: "chris@example.com", name: "chris")
...> %{email: email, name: name, id: id}
%{email: "chris@example.com", id: "052c1984-74c9-522f-858f-f04f1d4cc786", name: "chris"}

iex> %CreateUser{id: "052c1984-74c9-522f-858f-f04f1d4cc786"} = CreateUser.new!(email: "chris@example.com", name: "chris")

Dispatching

iex> {:error, {:invalid_command, errors}} =
...> CreateUser.new(name: "chris", email: "wrong")
...> |> CreateUser.dispatch()
...> errors
%{email: ["has invalid format"]}

iex> CreateUser.new(name: "chris", email: "chris@example.com")
...> |> CreateUser.dispatch()
{:ok, :dispatched}

Event derivation

You can derive events directly from a command.

see derive_event/2

defmodule DeactivateUser do
  use Cqrs.Command

  field :id, :binary_id

  derive_event UserDeactivated
end

Usage with Commanded

defmodule Commanded.Application do
  use Commanded.Application,
    otp_app: :my_app,
    default_dispatch_opts: [
      consistency: :strong,
      returning: :execution_result
    ],
    event_store: [
      adapter: Commanded.EventStore.Adapters.EventStore,
      event_store: MyApp.EventStore
    ]
end

defmodule DeactivateUser do
  use Cqrs.Command, dispatcher: Commanded.Application

  field :id, :binary_id

  derive_event UserDeactivated
end

iex> {:ok, event} = DeactivateUser.new(id: "052c1984-74c9-522f-858f-f04f1d4cc786")
...> |> DeactivateUser.dispatch()
...>  %{id: event.id, version: event.version}
%{id: "052c1984-74c9-522f-858f-f04f1d4cc786", version: 1}

Link to this section Summary

Functions

Generates an event based on the fields defined in the command.

Defines a command field.

The same as field/3 but sets the option internal to true.

Describes a supported option for this command.

Callbacks

Allows one to modify the fully validated command. The changes to the command are validated again after this callback.

This callback is intended to be used as a last chance to do any validation that performs IO.

Allows one to modify the incoming attrs before they are validated.

This callback is intended to be used to run the fully validated command.

Allows one to define any custom data validation aside from casting and requiring fields.

Link to this section Types

Specs

command() :: struct()

Link to this section Functions

Link to this macro

derive_event(name, opts \\ [])

View Source (macro)

Generates an event based on the fields defined in the command.

Accepts all the options that DomainEvent accepts.

Link to this macro

field(name, type, opts \\ [])

View Source (macro)

Specs

field(name :: atom(), type :: atom(), keyword()) :: any()

Defines a command field.

  • :name - any atom

  • :type - any valid Ecto Schema type

  • :opts - any valid Ecto Schema field options. Plus:

    • :required - true | false. Defaults to the require_all_fields option.
    • :internal - true | false. If true, this field is meant to be used internally. If true, the required option will be set to false and the field will be hidden from documentation.
    • :description - Documentation for the field.
Link to this macro

internal_field(name, type, opts \\ [])

View Source (macro)

Specs

internal_field(name :: atom(), type :: atom(), keyword()) :: any()

The same as field/3 but sets the option internal to true.

This helps with readability of commands with a large number of fields.

Link to this macro

option(name, hint, opts)

View Source (macro)

Specs

option(name :: atom(), hint :: atom(), keyword()) :: any()

Describes a supported option for this command.

Options

  • :default - this default value if the option is not provided.
  • :description - The documentation for this option.

Link to this section Callbacks

Specs

after_validate(command()) :: command()

Allows one to modify the fully validated command. The changes to the command are validated again after this callback.

This callback is optional.

Invoked after the handle_validate/2 callback is called.

Link to this callback

before_dispatch(command, keyword)

View Source

Specs

before_dispatch(command(), keyword()) :: {:ok, command()} | {:error, any()}

This callback is intended to be used as a last chance to do any validation that performs IO.

This callback is optional.

Invoked before handle_dispatch/2.

Specs

before_validate(map()) :: map()

Allows one to modify the incoming attrs before they are validated.

This callback is optional.

Invoked before the handle_validate/2 callback is called.

Link to this callback

handle_dispatch(command, keyword)

View Source

Specs

handle_dispatch(command(), keyword()) :: any()

This callback is intended to be used to run the fully validated command.

This callback is required.

Link to this callback

handle_validate(arg1, keyword)

View Source

Specs

handle_validate(Ecto.Changeset.t(), keyword()) :: Ecto.Changeset.t()

Allows one to define any custom data validation aside from casting and requiring fields.

This callback is optional.

Invoked when the new() or new!() function is called.