# 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,
concurrency limit, 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), [Rate limiting](rate-limiting.md), and
[Concurrency](concurrency.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`, `:concurrency`, `: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, rate limiter, and concurrency limit (if configured), 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, rate limiter, and concurrency limit. 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?                |
| `saturated?/0`                 | `ExternalService.saturated?/1`            | Is no concurrency slot free?        |
| `reset/0`                      | `ExternalService.reset/1`                 | Force the breaker closed.           |
| `reset_all/0`                  | `ExternalService.reset_all/1`             | Clear the breaker, the rate limiter, and the concurrency limit. |
| `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`, `saturated?/1`, `reset/1`,
`reset_all/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.
