Cqrs.Command behaviour (cqrs_tools v0.2.0) 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 - If true, all fields will be required. Defaults to true
  • dispatcher - A module that defines a dispatch/2.

Examples

defmodule CreateUser do
  use Cqrs.Command

  field :email, :string
  field :name, :string
  field :id, :binary_id, internal: true

  @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> CreateUser.new()
#CreateUser<errors: %{email: ["can't be blank"], name: ["can't be blank"]}>

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

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

Dispatching

iex> {:error, {:invalid_command, state}} =
...> CreateUser.new(name: "chris", email: "wrong")
...> |> CreateUser.dispatch()
...> state.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.

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.

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 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.

Link to this callback

handle_dispatch(command, keyword)

View Source

Specs

handle_dispatch(command(), keyword()) :: {:ok, any()} | {:error, 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.