# The Module Front Door

`use ExternalService` is the recommended, declarative way to define a gateway to
an external service. You configure the circuit breaker, rate limiting, and
default retry options once at the module level, start the module under a
supervisor, and call the service through a small set of generated functions.

This guide covers what `use ExternalService` generates and how to operate it.
For the mechanics of each subsystem, see the [Circuit breakers](circuit-breakers.md),
[Retries](retries.md), and [Rate limiting](rate-limiting.md) guides.

## Defining a service

```elixir
defmodule MyApp.Stripe do
  use ExternalService,
    circuit_breaker: [tolerate: 5, within: :timer.seconds(1), reset: :timer.seconds(5)],
    rate_limit: [limit: 100, per: :timer.seconds(1), wait: :timer.seconds(1)],
    retry: [max_attempts: 5, backoff: :exponential, jitter: true]

  def charge(params) do
    call fn ->
      case Stripe.charge(params) do
        {:ok, result} -> {:ok, result}
        {:error, %{status: status}} when status in 500..599 -> :retry
        other -> other
      end
    end
  end
end
```

The options are exactly those accepted by `ExternalService.start/2`
(`:circuit_breaker`, `:rate_limit`, `:retry`, `:sleep_function`), plus:

- `:name` — the term that identifies the service. Defaults to the module name.

You rarely need `:name`; the module name is a perfectly good service identifier
and keeps things unambiguous.

## Starting the service

A service must be started before it is called. Starting installs the circuit
breaker and rate limiter and records the default retry options. Because `use
ExternalService` generates `child_spec/1`, you can place the module directly in
a supervision tree:

```elixir
children = [
  MyApp.Stripe
]

Supervisor.start_link(children, strategy: :one_for_one)
```

Under the hood the generated `start_link/1` installs the service's circuit
breaker and rate limiter. This is the only "process" the front door adds, and it
exists purely to tie the service's lifecycle to your supervision tree.

## Per-environment overrides

The child spec accepts overrides that are **deep merged** with the options given
to `use ExternalService`. This is the idiomatic way to tune a service per
environment — most usefully, to make tests fast and deterministic:

```elixir
# config-driven children
children = [
  {MyApp.Stripe, circuit_breaker: [tolerate: 1], retry: [max_attempts: 1]}
]
```

Because the merge is deep, you only override the keys you care about; everything
else falls back to the module-level configuration. For example, the override
above keeps the `:within` and `:reset` from the module and only changes
`:tolerate`.

## Generated functions

`use ExternalService` generates the following functions on your module:

| Function                       | Delegates to                              | Purpose                             |
| ------------------------------ | ----------------------------------------- | ----------------------------------- |
| `call/1`, `call/2`             | `ExternalService.call/2,3`                | Synchronous guarded call.           |
| `call!/1`, `call!/2`           | `ExternalService.call!/2,3`               | Like `call`, but raises on failure. |
| `call_async/1`, `call_async/2` | `ExternalService.call_async/2,3`          | Returns a `Task`.                   |
| `call_async_stream/2,3,4`      | `ExternalService.call_async_stream/3,4,5` | Parallel, streaming calls.          |
| `available?/0`                 | `ExternalService.available?/1`            | Is the breaker closed?              |
| `blown?/0`                     | `ExternalService.blown?/1`                | Is the breaker open?                |
| `reset/0`                      | `ExternalService.reset/1`                 | Force the breaker closed.           |
| `reset_all/0`                  | `ExternalService.reset_all/1`             | Clear the breaker and the limiter.  |
| `child_spec/1`, `start_link/1` | —                                         | Supervision integration.            |

The one- and two-argument `call` forms differ only in whether you pass retry
options explicitly:

```elixir
# Uses the module's default :retry options
call fn -> do_work() end

# Overrides just :max_attempts for this call, merged onto the module's defaults
call [max_attempts: 2], fn -> do_work() end
```

A per-call keyword list is **merged** onto the module's `:retry` defaults — it
changes only the keys it lists and inherits the rest. (A `%RetryOptions{}` struct
replaces them entirely.) See the [Retries](retries.md) guide for the full list of
keys and the merge-vs-replace rule.

## Introspection

The generated `available?/0`, `blown?/0`, and `reset/0` let you inspect and
control the circuit breaker without referring to the service name:

```elixir
if MyApp.Stripe.available?() do
  MyApp.Stripe.charge(params)
else
  {:error, :payments_unavailable}
end
```

`available?/0` returns `true` when the breaker is closed (calls will be
attempted) and `false` when it is open or the service has not been started.
`blown?/0` is the direct "is the breaker open?" question. See
[Circuit breakers](circuit-breakers.md) for the semantics and caveats.

## Relationship to the functional API

Everything the front door generates is a thin wrapper over the functional
`ExternalService` API (`start/2`, `call/3`, `call!/3`, `call_async/3`,
`call_async_stream/5`, `available?/1`, `blown?/1`, `reset/1`). Reach for the
functional API directly when a service identifier isn't naturally a module — for
example, when you start services dynamically or key them on runtime values:

```elixir
ExternalService.start({:tenant, tenant_id},
  circuit_breaker: [tolerate: 5, within: 1_000],
  retry: [max_attempts: 3]
)

ExternalService.call({:tenant, tenant_id}, fn -> fetch(tenant_id) end)
```

The two styles interoperate freely; the front door is just the ergonomic
default.

## Decorator annotations

If you'd rather not wrap each body in `call fn -> ... end`, `ExternalService.Decorator`
lets you mark a function as an external call with a `@decorate` annotation and write
the body directly. It's a thin convenience layer over `call` — nothing more.

> #### Optional dependency {: .info}
>
> Like `ExternalService.Flow`, this module exists only when its dependency is
> present. Add it to your application's deps to use the annotations:
>
> ```elixir
> {:decorator, "~> 1.4"}
> ```

```elixir
defmodule MyApp.Payments do
  use ExternalService.Decorator

  @decorate external_call(MyApp.Stripe)
  def charge(params) do
    case Stripe.charge(params) do
      {:error, %{status: s}} when s in 500..599 -> :retry
      other -> other
    end
  end

  @decorate external_call!(MyApp.Stripe)
  def capture(id), do: Stripe.capture(id)
end
```

The decorated body *is* the retriable function, so the same retry protocol applies:
it drives retries by returning `:retry` / `{:retry, reason}`, and the call returns the
body's value on success or a structured error on failure. `external_call!` raises
instead of returning, mirroring `call!`.

The service argument is any started service term — it need not be the module the
function lives in, so one module can front several services.

To retry based on the **return value** of an unmodified function, pass per-call retry
options (the same ones `call/3` accepts) as a second argument — most usefully a
`:retry_on` predicate:

```elixir
@decorate external_call(MyApp.Stripe, retry_on: &match?({:error, %{status: 500}}, &1))
def charge(params), do: Stripe.charge(params)
```

> #### Multi-clause functions, default args, and guards {: .info}
>
> Annotate an empty function head and let it cover every clause:
>
> ```elixir
> @decorate external_call(MyApp.Stripe)
> def fetch(id, opts \\ [])
> def fetch(id, opts) when is_integer(id), do: Client.get(id, opts)
> ```

See `ExternalService.Decorator` for the full reference.

## Flow-based pipelines

For parallel work that is part of a larger pipeline — partitioned, back-pressured
processing with downstream `map`/`filter`/`reduce` stages — the optional
`ExternalService.Flow` integration runs each element through a guarded `call` as a
[`Flow`](https://hexdocs.pm/flow) stage:

```elixir
orders
|> ExternalService.Flow.map(MyApp.Stripe, fn order -> charge(order) end)
|> Flow.filter(&match?({:ok, _}, &1))
|> Enum.to_list()
```

It requires adding `:flow` to your deps. For a simple ordered parallel map,
`call_async_stream/2` is still the right tool. See the [Flow pipelines](flow.md)
guide and `ExternalService.Flow`.

## Migrating from `ExternalService.Gateway`

`use ExternalService.Gateway` (the 1.x module-based gateway) still works but is
**deprecated** and emits a warning at compile time. It delegates to `use
ExternalService` and keeps the old `external_call/*` and `reset_fuse/0` names as
aliases — but it uses the new option shape, so the old `fuse: [...]` options are
gone. See [Migrating to 2.0](migrating-to-2.0.md) for the mapping.
