# `DDD.Migration`
[🔗](https://github.com/blackuprise/vicander/blob/v0.1.1/lib/ddd/persistence/migration.ex#L1)

Snapshot-based migration generation for aggregates stored by `DDD.Repository.Ecto`.

`generate/2` compares an aggregate's current columns with the snapshot saved by the
previous run and writes a new timestamped migration containing only the difference
(`create table` the first time). Nothing already generated is overwritten.

Safeguards:

  * Removing a column or changing its type loses data, so it is refused unless
    `allow_destructive: true` (`--allow-destructive` on the Mix task).
  * A rename must be explicit: `renames: %{"old" => "new"}` (`--rename old:new`) or
    `field :name, :string, renamed_from: :old_name` on the field. Otherwise it is a
    remove plus an add.
  * Edits inside a nested `jsonb` entity do not change columns. Old rows keep the old
    shape; the serializer fills defaults for missing keys when loading.

`drift/3` compares an aggregate with the real table columns, for use in a test.

# `column`

```elixir
@type column() :: %{required(String.t()) =&gt; String.t() | boolean()}
```

# `columns`

```elixir
@spec columns(module()) :: [column()]
```

Columns the Ecto adapter maps an aggregate to (`created_at` / `updated_at` included).

# `custom_schema_adapters`

```elixir
@spec custom_schema_adapters(module(), [module()]) :: [map()]
```

The adapter(s) among `modules` that store `aggregate`, as `DDD.Repository.Ecto` records them.
`mix vic.gen.migration` skips an aggregate whose adapters all use a schema you own.

# `declared_renames`

```elixir
@spec declared_renames(module()) :: %{required(String.t()) =&gt; String.t()}
```

Renames declared on fields with `renamed_from:`, as `%{old => new}`.

# `default_table`

```elixir
@spec default_table(module()) :: String.t()
```

Default table name for an aggregate (matches `DDD.Repository.Ecto`).

# `destructive?`

```elixir
@spec destructive?(map()) :: boolean()
```

True when applying the diff can lose data.

# `diff`

```elixir
@spec diff([column()], [column()], %{required(String.t()) =&gt; String.t()}) :: map()
```

Diff two column lists.

Returns `%{added: [column], removed: [column], renamed: [{old, new}], changed: [{name, old_type, new_type}]}`.

# `drift`

```elixir
@spec drift(module(), String.t(), module()) :: :ok | {:error, map()}
```

Compare an aggregate's columns with the real table. Returns `:ok` or
`{:error, %{missing_in_db: [...], extra_in_db: [...]}}`.

# `empty?`

```elixir
@spec empty?(map()) :: boolean()
```

True when the diff changes nothing.

# `generate`

```elixir
@spec generate(module(), keyword()) :: {:ok, :unchanged | Path.t()} | {:error, term()}
```

Generate the migration for `aggregate`.

Options: `:table`, `:snapshot_dir` (default `priv/ddd_snapshots`), `:migrations_path`
(default `priv/repo/migrations`), `:repo` (default: the configured repo), `:renames`,
`:allow_destructive`, `:timestamp`.

Returns `{:ok, :unchanged}`, `{:ok, migration_path}` or `{:error, reason}`.

# `identity`

```elixir
@spec identity(module()) :: [String.t()]
```

The names of the identity fields (`["id"]`, or the parts of a composite identity).

# `schema_drift`

```elixir
@spec schema_drift(module(), %{required(String.t()) =&gt; String.t()}) ::
  :ok | {:error, map()}
```

Compare an aggregate with the Ecto schema you wired it to (`schema:` on `DDD.Repository.Ecto`).
Report only: nothing is written. Returns `:ok` or
`{:error, %{missing_in_schema: [...], extra_in_schema: [...]}}`.

`renames` is `%{"aggregate_field" => "schema_field"}` for the fields your `to_domain/1` and
`from_domain/1` map by hand. An adapter with a generated schema is always `:ok`.

# `table_drift`

```elixir
@spec table_drift(module()) :: :ok | {:error, map()}
```

Compare the Ecto schema an adapter was wired to with the real table (`schema:` on
`DDD.Repository.Ecto`; the table is the schema's). Report only. Returns `:ok` or
`{:error, %{missing_in_db: [...], extra_in_db: [...]}}`. Needs the database; for a test.

---

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