# `DDD.EventHandler`
[🔗](https://github.com/blackuprise/vicander/blob/v0.1.1/lib/ddd/events/event_handler.ex#L1)

A handler for domain events delivered through the outbox.

    defmodule MyApp.OnOrderPlaced do
      use DDD.EventHandler, events: [MyApp.Events.OrderPlaced]

      @impl DDD.EventHandler
      def handle(%MyApp.Events.OrderPlaced{} = event) do
        case MyApp.Commands.ReserveStock.call(%{order_id: event.aggregate_id}) do
          {:ok, _} -> :ok
          {:error, error} -> {:error, error}
        end
      end
    end

    MyApp.OnOrderPlaced.subscribe()

## What `handle/1` returns

  * `:ok` (or `{:ok, _}`, or any other non-error value): the event was handled.
  * `{:error, reason}`: it depends on the kind of the error.
    * A **business** error (`%DDD.Error{kind: :business}`, or any plain reason such as
      `:not_found`) means the handler looked at the event and said no. That outcome is
      deterministic, so it is **recorded as rejected and never retried**. It is not a dead
      letter.
    * An **infrastructure** error (`%DDD.Error{kind: :infrastructure}`, for example a
      conflict or a failed transaction) is a temporary failure: the event is **retried**
      with a backoff and dead-lettered if it keeps failing.
  * An exception, throw or crash is unexpected: retried like an infrastructure error.

In both failure cases a transactional handler is rolled back, so it never leaves half of
its work behind.

## Transactions and delivery guarantees

By default each delivery runs in its own database transaction together with a record in
`event_deliveries` that this handler processed this event. If the handler fails, everything
rolls back, including the record, and the event is retried later. If it succeeds, the handler
is never run again for that event, even when another handler of the same event failed and the
event is retried. For handlers that only write to our database that is effectively
exactly-once. Events a handler produces by saving aggregates commit with its work.

    use DDD.EventHandler, events: [...]                      # transaction on the configured repo
    use DDD.EventHandler, events: [...], repo: MyApp.Repo     # another repo
    use DDD.EventHandler, events: [...], transaction: false   # no transaction

A handler with an effect a transaction can't roll back (sending an email, calling an API)
should use `transaction: false`. It is then delivered at least once: if the server stops
after the effect but before the delivery is recorded, it runs again, so it must tolerate that.

Handlers that don't `use DDD.EventHandler` (any module with `handle/1`) still work and are
delivered like `transaction: false` handlers.

# `result`

```elixir
@type result() :: :delivered | :skipped | {:rejected, term()} | {:error, term()}
```

  * `:delivered` - the handler ran and succeeded
  * `:skipped` - it had already processed (or rejected) this event
  * `{:rejected, reason}` - a business error: recorded, never retried
  * `{:error, reason}` - a failure to retry (infrastructure error, exception)

# `handle`

```elixir
@callback handle(event :: struct()) :: :ok | {:ok, any()} | {:error, any()}
```

# `deliver`

```elixir
@spec deliver(module(), struct(), binary(), module()) :: result()
```

Deliver `event` (stored under `event_id`, the 16-byte outbox id) to `handler`.

`fallback_repo` is used for the delivery record of non-transactional handlers.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
