Packages

Essential utilities and helpers for Commanded CQRS/ES applications. Provides type-safe commands, events, enrichment pipeline, error handling, and more.

Current section

Files

Jump to
commanded_utils lib commanded_utils command.ex
Raw

lib/commanded_utils/command.ex

defmodule CommandedUtils.Command do
@moduledoc """
Adds the `defcommand` macro for defining CQRS commands.
Commands represent intentions to change state in your system.
They are validated, enriched, and then dispatched to aggregates.
## Basic Usage
defmodule CreateUser do
use CommandedUtils.Command
@required_fields ~w(user_id email)a
defcommand do
field :user_id, Ecto.UUID
field :email, :string
field :name, :string
field :age, :integer
end
# Optional: custom validation
def changeset(struct, attrs) do
struct
|> super(attrs)
|> validate_format(:email, ~r/@/)
|> validate_number(:age, greater_than: 0)
end
# Optional: enrichment (called by middleware)
def enrich(cmd) do
%{cmd | user_id: cmd.user_id || Ecto.UUID.generate()}
end
end
## Enrichment
If you define an `enrich/1` function, it will be automatically called
by `CommandedUtils.Middleware.Enrich` before the command is dispatched.
def enrich(cmd) do
cmd
|> generate_id()
|> validate_business_rules()
end
The enrich function can return:
- `{:ok, command}` - Success
- `{:error, reason}` - Validation failed
- `command` - Success (will be wrapped in {:ok, command})
## Including Optional Fields
use CommandedUtils.Command, include: [:tenant_id, :creator]
defcommand do
# Now tenant_id and creator are automatically included
field :name, :string
end
"""
import CommandedUtils.Type, only: [deftype: 1]
@doc """
Defines a command struct with automatic validation.
"""
defmacro defcommand(do: block) do
quote do
deftype do
unquote(block)
end
end
end
@doc false
defmacro __using__(opts) do
quote do
use CommandedUtils.Type, unquote(opts)
import CommandedUtils.Command, only: [defcommand: 1]
# Override new function to call enrich if defined
def new(params) do
case super(params) do
{:ok, struct} ->
if function_exported?(__MODULE__, :enrich, 1) do
case apply(__MODULE__, :enrich, [struct]) do
{:ok, enriched} -> {:ok, enriched}
{:error, reason} -> {:error, reason}
enriched -> {:ok, enriched}
end
else
{:ok, struct}
end
error ->
error
end
end
defoverridable new: 1
end
end
end