# `DoubleDown.Repo`
[🔗](https://github.com/mccraigmccraig/double_down/blob/main/lib/double_down/repo.ex#L21)

Repo contract for common Ecto Repo operations.

Provides `defcallback` declarations for the standard write and read operations
from `Ecto.Repo`, so that code using `DoubleDown` for database access doesn't
need to redeclare these with identical boilerplate.

## Usage

    # Define a facade in your app:
    defmodule MyApp.Repo do
      use DoubleDown.ContractFacade, contract: DoubleDown.Repo, otp_app: :my_app
    end

    changeset = User.changeset(%User{}, attrs)
    {:ok, user} = MyApp.Repo.insert(changeset)
    user = MyApp.Repo.get!(User, user_id)

## Write Operations

Write operations return `{:ok, struct()} | {:error, Ecto.Changeset.t()}`.

## Bulk Operations

`update_all/3` and `delete_all/2` follow Ecto's return convention of
`{count, nil | list}`.

## Read Operations

Read operations follow Ecto's conventions: `get/2`, `get_by/2`, `one/1`
return `nil` on not-found; `all/1` returns a list; `exists?/1` returns
a boolean; `aggregate/3` returns a term.

Raise-on-not-found variants (`get!/2`, `get_by!/2`, `one!/1`) mirror
Ecto's semantics.

## Relationship to Ecto.Repo

This module is a `DoubleDown.Contract`, not an `Ecto.Repo` behaviour
implementation. It mirrors most of the `Ecto.Repo` API but differs in
two ways:

- **`transact` is the preferred name for `transaction`** — both are
  supported. `transact` is recommended for new code; `transaction` is
  provided for compatibility with existing `Ecto.Repo` call sites so
  that production code works unchanged through both ContractFacade and
  DynamicFacade routes.
- **Some rarely-used callbacks omitted** — `checkout/1`, `checked_out?/0`,
  `put_dynamic_repo/1`, `get_dynamic_repo/0`, `to_sql/2` are not included.
  These can be added incrementally if needed.

# `aggregate`

```elixir
@callback aggregate(queryable :: Ecto.Queryable.t(), aggregate :: atom(), field :: atom()) ::
  term()
```

# `aggregate`

```elixir
@callback aggregate(
  queryable :: Ecto.Queryable.t(),
  aggregate :: atom(),
  field :: atom(),
  opts :: keyword()
) :: term()
```

# `all`

```elixir
@callback all(queryable :: Ecto.Queryable.t()) :: [struct()]
```

# `all`

```elixir
@callback all(queryable :: Ecto.Queryable.t(), opts :: keyword()) :: [struct()]
```

# `all_by`

```elixir
@callback all_by(queryable :: Ecto.Queryable.t(), clauses :: keyword() | map()) :: [
  struct()
]
```

# `all_by`

```elixir
@callback all_by(
  queryable :: Ecto.Queryable.t(),
  clauses :: keyword() | map(),
  opts :: keyword()
) :: [
  struct()
]
```

# `delete`

```elixir
@callback delete(struct_or_changeset :: struct() | Ecto.Changeset.t()) ::
  {:ok, struct()} | {:error, Ecto.Changeset.t()}
```

# `delete`

```elixir
@callback delete(struct_or_changeset :: struct() | Ecto.Changeset.t(), opts :: keyword()) ::
  {:ok, struct()} | {:error, Ecto.Changeset.t()}
```

# `delete!`

```elixir
@callback delete!(struct_or_changeset :: struct() | Ecto.Changeset.t()) :: struct()
```

# `delete!`

```elixir
@callback delete!(struct_or_changeset :: struct() | Ecto.Changeset.t(), opts :: keyword()) ::
  struct()
```

# `delete_all`

```elixir
@callback delete_all(queryable :: Ecto.Queryable.t(), opts :: keyword()) ::
  {non_neg_integer(), nil | list()}
```

# `exists?`

```elixir
@callback exists?(queryable :: Ecto.Queryable.t()) :: boolean()
```

# `exists?`

```elixir
@callback exists?(queryable :: Ecto.Queryable.t(), opts :: keyword()) :: boolean()
```

# `get`

```elixir
@callback get(queryable :: Ecto.Queryable.t(), id :: term()) :: struct() | nil
```

# `get`

```elixir
@callback get(queryable :: Ecto.Queryable.t(), id :: term(), opts :: keyword()) ::
  struct() | nil
```

# `get!`

```elixir
@callback get!(queryable :: Ecto.Queryable.t(), id :: term()) :: struct()
```

# `get!`

```elixir
@callback get!(queryable :: Ecto.Queryable.t(), id :: term(), opts :: keyword()) ::
  struct()
```

# `get_by`

```elixir
@callback get_by(queryable :: Ecto.Queryable.t(), clauses :: keyword() | map()) ::
  struct() | nil
```

# `get_by`

```elixir
@callback get_by(
  queryable :: Ecto.Queryable.t(),
  clauses :: keyword() | map(),
  opts :: keyword()
) ::
  struct() | nil
```

# `get_by!`

```elixir
@callback get_by!(queryable :: Ecto.Queryable.t(), clauses :: keyword() | map()) ::
  struct()
```

# `get_by!`

```elixir
@callback get_by!(
  queryable :: Ecto.Queryable.t(),
  clauses :: keyword() | map(),
  opts :: keyword()
) ::
  struct()
```

# `in_transaction?`

```elixir
@callback in_transaction?() :: boolean()
```

# `insert`

```elixir
@callback insert(struct_or_changeset :: Ecto.Changeset.t() | struct()) ::
  {:ok, struct()} | {:error, Ecto.Changeset.t()}
```

# `insert`

```elixir
@callback insert(struct_or_changeset :: Ecto.Changeset.t() | struct(), opts :: keyword()) ::
  {:ok, struct()} | {:error, Ecto.Changeset.t()}
```

# `insert!`

```elixir
@callback insert!(struct_or_changeset :: Ecto.Changeset.t() | struct()) :: struct()
```

# `insert!`

```elixir
@callback insert!(struct_or_changeset :: Ecto.Changeset.t() | struct(), opts :: keyword()) ::
  struct()
```

# `insert_all`

```elixir
@callback insert_all(
  source :: Ecto.Queryable.t() | binary(),
  entries :: [map() | keyword()],
  opts :: keyword()
) :: {non_neg_integer(), nil | list()}
```

# `insert_or_update`

```elixir
@callback insert_or_update(changeset :: Ecto.Changeset.t()) ::
  {:ok, struct()} | {:error, Ecto.Changeset.t()}
```

# `insert_or_update`

```elixir
@callback insert_or_update(changeset :: Ecto.Changeset.t(), opts :: keyword()) ::
  {:ok, struct()} | {:error, Ecto.Changeset.t()}
```

# `insert_or_update!`

```elixir
@callback insert_or_update!(changeset :: Ecto.Changeset.t()) :: struct()
```

# `insert_or_update!`

```elixir
@callback insert_or_update!(changeset :: Ecto.Changeset.t(), opts :: keyword()) ::
  struct()
```

# `load`

```elixir
@callback load(
  schema_or_map :: module() | map(),
  data :: map() | keyword() | {list(), list()}
) ::
  struct() | map()
```

# `one`

```elixir
@callback one(queryable :: Ecto.Queryable.t()) :: struct() | nil
```

# `one`

```elixir
@callback one(queryable :: Ecto.Queryable.t(), opts :: keyword()) :: struct() | nil
```

# `one!`

```elixir
@callback one!(queryable :: Ecto.Queryable.t()) :: struct()
```

# `one!`

```elixir
@callback one!(queryable :: Ecto.Queryable.t(), opts :: keyword()) :: struct()
```

# `preload`

```elixir
@callback preload(
  structs_or_struct_or_nil :: [struct()] | struct() | nil,
  preloads :: term()
) ::
  [struct()] | struct() | nil
```

# `preload`

```elixir
@callback preload(
  structs_or_struct_or_nil :: [struct()] | struct() | nil,
  preloads :: term(),
  opts :: keyword()
) :: [struct()] | struct() | nil
```

# `query`

```elixir
@callback query(sql :: String.t()) :: {:ok, term()} | {:error, term()}
```

# `query`

```elixir
@callback query(sql :: String.t(), params :: list()) :: {:ok, term()} | {:error, term()}
```

# `query`

```elixir
@callback query(sql :: String.t(), params :: list(), opts :: keyword()) ::
  {:ok, term()} | {:error, term()}
```

# `query!`

```elixir
@callback query!(sql :: String.t()) :: term()
```

# `query!`

```elixir
@callback query!(sql :: String.t(), params :: list()) :: term()
```

# `query!`

```elixir
@callback query!(sql :: String.t(), params :: list(), opts :: keyword()) :: term()
```

# `reload`

```elixir
@callback reload(struct_or_structs :: struct() | [struct()]) ::
  struct() | nil | [struct() | nil]
```

# `reload`

```elixir
@callback reload(struct_or_structs :: struct() | [struct()], opts :: keyword()) ::
  struct() | nil | [struct() | nil]
```

# `reload!`

```elixir
@callback reload!(struct_or_structs :: struct() | [struct()]) :: struct() | [struct()]
```

# `reload!`

```elixir
@callback reload!(struct_or_structs :: struct() | [struct()], opts :: keyword()) ::
  struct() | [struct()]
```

# `rollback`

```elixir
@callback rollback(value :: term()) :: no_return()
```

# `stream`

```elixir
@callback stream(queryable :: Ecto.Queryable.t()) :: Enum.t()
```

# `stream`

```elixir
@callback stream(queryable :: Ecto.Queryable.t(), opts :: keyword()) :: Enum.t()
```

# `transact`

```elixir
@callback transact(fun_or_multi :: term(), opts :: keyword()) ::
  {:ok, term()} | {:error, term()} | {:error, term(), term(), term()}
```

# `transaction`

```elixir
@callback transaction(fun_or_multi :: term(), opts :: keyword()) ::
  {:ok, term()} | {:error, term()} | {:error, term(), term(), term()}
```

# `update`

```elixir
@callback update(changeset :: Ecto.Changeset.t()) ::
  {:ok, struct()} | {:error, Ecto.Changeset.t()}
```

# `update`

```elixir
@callback update(changeset :: Ecto.Changeset.t(), opts :: keyword()) ::
  {:ok, struct()} | {:error, Ecto.Changeset.t()}
```

# `update!`

```elixir
@callback update!(changeset :: Ecto.Changeset.t()) :: struct()
```

# `update!`

```elixir
@callback update!(changeset :: Ecto.Changeset.t(), opts :: keyword()) :: struct()
```

# `update_all`

```elixir
@callback update_all(
  queryable :: Ecto.Queryable.t(),
  updates :: keyword(),
  opts :: keyword()
) ::
  {non_neg_integer(), nil | list()}
```

---

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