# Boundaries

<!-- nav:header:start -->
[< DoubleDown](../README.md) | [Up: Guides](../README.md) | [Index](../README.md) | [Dispatch >](dispatch.md)
<!-- nav:header:end -->

DoubleDown separates _what you call_ from _what handles the call_. A function
call passes through four layers:

```
     ┌──────────────────────────────────────────────────────┐
     │                 FUNCTION CALL                        │
     │           MyApp.Repo.insert(changeset)               │
     └──────────────────────────┬───────────────────────────┘
                                │
                                ▼
     ┌──────────────────────────────────────────────────────┐
     │                  1.  CONTRACT                        │
     │              (type-level interface)                  │
     │                                                      │
     │  ┌────────────────┐ ┌──────────────┐ ┌─────────────┐ │
     │  │ defcallback    │ │  @behaviour  │ │ any module  │ │
     │  │ (explicit,     │ │  (explicit,  │ │ via Dynamic │ │
     │  │  typed, rich)  │ │   existing   │ │ Facade      │ │
     │  │                │ │   module)    │ │ (implicit)  │ │
     │  └────────┬───────┘ └──────┬───────┘ └──────┬──────┘ │
     └───────────┼────────────────┼────────────────┼────────┘
                 │                │                │
                 ▼                ▼                ▼
     ┌──────────────────────────────────────────────────────┐
     │                  2.  FACADE                          │
     │           (generated dispatch functions)             │
     │                                                      │
     │ ┌────────────────┐ ┌───────────────┐ ┌─────────────┐ │
     │ │ContractFacade  │ │BehaviourFacade│ │DynamicFacade│ │
     │ │use             │ │use            │ │setup(Mod)   │ │
     │ │ ContractFacade │ │BehaviourFacade│ │shim         │ │
     │ │defcallback...  │ │               │ │             │ │
     │ └────────┬───────┘ └──────┬────────┘ └──────┬──────┘ │
     └──────────┼────────────────┼─────────────────┼────────┘
                └────────────────┼─────────────────┘
                                 │
                                 ▼
     ┌──────────────────────────────────────────────────────┐
     │                  3.  DISPATCH                        │
     │              (call resolution)                       │
     │                                                      │
     │  ┌────────────┐  ┌──────────────┐  ┌──────────────┐  │
     │  │  Static    │  │   Runtime    │  │    Test      │  │
     │  │            │  │   Config     │  │   Handler    │  │
     │  │ compile-   │  │              │  │              │  │
     │  │ time       │  │ call_config  │  │ call/4       │  │
     │  │ inlined    │  │ /4           │  │              │  │
     │  │ direct     │  │              │  │ NimbleOwner- │  │
     │  │ call       │  │ App.get_env  │  │ ship lookup  │  │
     │  │            │  │ → apply/3    │  │ → handler    │  │
     │  │ (zero      │  │              │  │              │  │
     │  │ overhead)  │  │              │  │              │  │
     │  └─────┬──────┘  └───────┬──────┘  └───────┬──────┘  │
     └────────┼─────────────────┼─────────────────┼─────────┘
              └─────────────────┼─────────────────┘
                                │
                                ▼
     ┌──────────────────────────────────────────────────────┐
     │              4.  IMPLEMENTATION                      │
     │            (actual execution)                        │
     │                                                      │
     │  ┌──────────────────────────┐  ┌───────────────────┐ │
     │  │   Production Module      │  │   Test Double     │ │
     │  │                          │  │                   │ │
     │  │   @behaviour Contract    │  │  Module handler   │ │
     │  │   def operation(...)     │  │  Stateless fn     │ │
     │  │                          │  │  Stateful fn      │ │
     │  │   e.g. MyApp.EctoRepo    │  │  Double.expect/   │ │
     │  │                          │  │  fallback/stub    │ │
     │  └──────────────────────────┘  └───────────────────┘ │
     └──────────────────────────────────────────────────────┘

  ── Static path (prod, compile-time config):  Facade → Static → Production
  ── Config path (prod, no compile config):    Facade → Config → Production
  ── Test path (test env):                     Facade → Test   → Test Double
  ── DynamicFacade test path:                  Facade → Test   → Test Double
  ── DynamicFacade no-handler (passthrough):   Facade → (handler not found)
                                                → original module
```

## Layer 1: Contract

The contract is the type-level interface — it defines _what_ operations exist and
their type signatures, but not _how_ they're implemented.

### defcallback (explicit contract)

Use `defcallback` from `DoubleDown.Contract` when you control the interface.
It generates `@behaviour` + `@callback` + introspection metadata
(`__callbacks__/0`) from a single macro call.  `defcallback` requires named
parameters (`id :: String.t()`) — these appear in generated `@spec` and `@doc`
on the facade, giving LSP-friendly hover docs at every call site.

```elixir
defmodule MyApp.Todos do
  use DoubleDown.Contract

  defcallback get_todo(tenant_id :: String.t(), id :: String.t()) ::
    {:ok, Todo.t()} | {:error, term()}

  defcallback list_todos(tenant_id :: String.t()) :: [Todo.t()]
end
```

### @behaviour (existing module)

Any existing Elixir `@behaviour` module works as a contract — see
`DoubleDown.BehaviourFacade`.  Use this for behaviours you don't control:
third-party libraries, existing codebase behaviours, or any module with
`@callback` declarations.

### Implicit (DynamicFacade)

With `DoubleDown.DynamicFacade`, no contract module exists at all — the target
module's public API becomes the contract implicitly.  The module is shimmed at
test time via bytecode replacement, so any call can be intercepted.

## Layer 2: Facade

The facade is what callers actually invoke.  It generates wrapper functions for
each contract operation that delegate to the dispatch layer.

### ContractFacade

For `defcallback` contracts.  Supports combined contract + facade (one module)
or separate modules.  Options control dispatch behaviour at compile time.

```elixir
# Combined contract + facade
defmodule MyApp.Todos do
  use DoubleDown.ContractFacade, otp_app: :my_app

  defcallback get_todo(id :: String.t()) :: {:ok, Todo.t()} | {:error, term()}
end
```

### BehaviourFacade

For vanilla `@behaviour` modules.  The behaviour must be compiled before the
facade — they live in separate modules.

```elixir
defmodule MyApp.Todos.Facade do
  use DoubleDown.BehaviourFacade, behaviour: MyApp.Todos.Behaviour, otp_app: :my_app
end
```

### DynamicFacade

Mimic-style bytecode interception.  Call `setup/1` in `test_helper.exs` before
`ExUnit.start()`.  The module is backed up and replaced with a dispatch shim.
Tests that don't install a handler get the original module's behaviour.

```elixir
# test/test_helper.exs
DoubleDown.DynamicFacade.setup(MyApp.EctoRepo)
ExUnit.start()
```

## Layer 3: Dispatch

Dispatch resolves which implementation handles a given call.  The resolution
strategy is selected at compile time per-facade via options.

### Static dispatch

When `static_dispatch?: true` and the implementation is available in config at
compile time, the facade function calls the implementation module directly.
No `Application.get_env`, no `NimbleOwnership` — the call inlines to identical
bytecode as calling the impl directly.  Default in `:prod`.

### Runtime config dispatch

`DoubleDown.Dispatch.call_config/4` reads `Application.get_env(otp_app, contract)[:impl]`
and calls `apply(impl, operation, args)`.  Used in production when static
dispatch isn't available, and in non-prod when `test_dispatch?: false`.

### Test handler dispatch

`DoubleDown.Dispatch.call/4` checks NimbleOwnership for a process-scoped test
handler before falling back to config.  This is the default in non-prod
environments — it's what makes `DoubleDown.Double.fallback/2`, `expect/3`, etc.
work in tests.

### DynamicFacade dispatch

`DynamicFacade.dispatch/3` checks NimbleOwnership for a test handler, falling
back to the original (backed-up) module.  This is always the path for
a DynamicFacade (which is only ever set up under test) — there's no config-based resolution because there's no contract
module to configure.

## Layer 4: Implementation

The implementation is the module or function that actually executes the
operation.

### Production

A module implementing the contract's `@behaviour`, wired via config:

```elixir
# config/config.exs
config :my_app, MyApp.Todos, impl: MyApp.Todos.Ecto
```

### Test doubles

Installed via `DoubleDown.Double` (recommended) or `DoubleDown.Testing`:

- **Module handler** — delegates to a module via `@behaviour`
- **Stateless handler** — `fn contract, operation, args -> result end`
- **Stateful handler** — `fn contract, operation, args, state -> {result, new_state} end`
  (4-arity) or `fn contract, operation, args, state, all_states -> {result, new_state} end`
  (5-arity, with cross-contract state access)

See `DoubleDown.Double` for the recommended API and
`DoubleDown.Dispatch.StatefulHandler` for the behaviour.

<!-- nav:footer:start -->

---

[< DoubleDown](../README.md) | [Up: Guides](../README.md) | [Index](../README.md) | [Dispatch >](dispatch.md)
<!-- nav:footer:end -->
