README.md

# Commanded Messaging

**Common macros for messaging in a Commanded application**

## Installation

This package can be installed
by adding `commanded_messaging` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:commanded_messaging, "~> 0.2.0"}
  ]
end
```

[Documentation](https://hexdocs.pm/commanded_messaging)

## Usage

### Commands

The `Commanded.Command` macro creates an Ecto `embedded_schema` so you can take advantage of the well known `Ecto.Changeset` API.

#### Default

```elixir
defmodule CreateAccount do
  use Commanded.Command,
      username: :string,
      email: :string,
      age: :integer
end

iex> CreateAccount.new() 
#Ecto.Changeset<action: nil, changes: %{}, errors: [], data: #CreateAccount<>, valid?: true>
```

#### Validation

```elixir
defmodule CreateAccount do
  use Commanded.Command,
      username: :string,
      email: :string,
      age: :integer

  def handle_validate(changeset) do
    changeset
    |> validate_required([:username, :email, :age])
    |> validate_format(:email, ~r/@/)
    |> validate_number(:age, greater_than: 12)
  end
end

iex> CreateAccount.new() 
#Ecto.Changeset<
  action: nil,
  changes: %{},
  errors: [
    username: {"can't be blank", [validation: :required]},
    email: {"can't be blank", [validation: :required]},
    age: {"can't be blank", [validation: :required]}
  ],
  data: #CreateAccount<>,
  valid?: false
>

iex> changeset = CreateAccount.new(username: "chris", email: "chris@example.com", age: 5) 
#Ecto.Changeset<
  action: nil,
  changes: %{age: 5, email: "chris@example.com", username: "chris"},
  errors: [
    age: {"must be greater than %{number}",
     [validation: :number, kind: :greater_than, number: 12]}
  ],
  data: #CreateAccount<>,
  valid?: false
>
```

To create the actual command struct, use `Ecto.Changeset.apply_changes/1`

```elixir
iex> cmd = Ecto.Changeset.apply_changes(changeset)
%CreateAccount{age: 5, email: "chris@example.com", username: "chris"}
```

> Note that `apply_changes` will not validate values.

### Events

Most events mirror the commands that produce them. So we make it easy to reduce the boilerplate in creating them with the `Commanded.Event` macro.

```elixir
defmodule AccountCreated do
  use Commanded.Event,
    from: CreateAccount
end

iex> AccountCreated.new(cmd)
%AccountCreated{
  age: 5,
  email: "chris@example.com",
  username: "chris",
  version: 1
}
```

#### Extra Keys

There are times when we need keys defined on an event that aren't part of the originating command. We can add these very easily.

```elixir
defmodule AccountCreated do
  use Commanded.Event,
    from: CreateAccount,
    with: [:date]
end

iex> AccountCreated.new(cmd, date: NaiveDateTime.utc_now())
%AccountCreated{
  age: 5,
  date: ~N[2019-07-25 08:03:15.372212],
  email: "chris@example.com",
  username: "chris",
  version: 1
}
```

#### Excluding Keys

And you may also want to drop some keys from your command.

```elixir
defmodule AccountCreated do
  use Commanded.Event,
    from: CreateAccount,
    with: [:date],
    drop: [:email]
end

iex> event = AccountCreated.new(cmd)
%AccountCreated{age: 5, date: nil, username: "chris", version: 1}
```

#### Versioning

You may have noticed that we provide a default version of `1`.

You can change the version of an event at anytime. 

After doing so, you should define an upcast instance that knows how to transform older events into the latest version.

```elixir
defmodule AccountCreated do
  use Commanded.Event,
    version: 2,
    from: CreateAccount,
    with: [:date, :sex],
    drop: [:email]

  defimpl Commanded.Event.Upcaster do
    def upcast(%{version: 1} = event, _metadata) do
      AccountCreated.new(event, sex: "maybe", version: 2)
    end

    def upcast(event, _metadata), do: event
  end
end

iex> Commanded.Event.Upcaster.upcast(event, %{})
%AccountCreated{age: 5, date: nil, sex: "maybe", username: "chris", version: 2}
```

> Note that you won't normally call `upcast` manually. `Commanded` will take care of that for you.

### Command Dispatch Validation

The `Commanded.CommandDispatchValidation` macro will inject the `validate_and_dispatch` into your `Commanded.Commands.Router`.

```elixir
defmodule AccountsRouter do
  use Commanded.Commands.Router
  use Commanded.CommandDispatchValidation
end

iex> AccountsRouter.validate_and_dispatch(changeset)
{:error, {:validation_failure, %{age: ["must be greater than 12"]}}}
```

***

I hope you find this library as useful as my team and I do. -Chris