# `Finitomata.Persistency`
[🔗](https://github.com/am-kantox/finitomata/blob/v0.41.0/lib/finitomata/persistency.ex#L1)

The behaviour to be implemented by a persistent storage to be used
  with `Finitomata` (pass the implementation as `persistency: Impl.Module.Name`
  to `use Finitomata`.)

Once declared, the FSM attempts to load its current state from the storage on start
  via `c:load/1`, and writes back on every successful transition via `c:store/3`
  (and `c:store_error/4` on a failed one).

## Built-in adapters

Two zero-dependency adapters ship with the library and only require their owner
  process to be added to the supervision tree:

- `Finitomata.Persistency.ETS` — in-memory snapshots that survive an individual
  FSM restart (good for development, tests, and single-node setups);
- `Finitomata.Persistency.DETS` — the same, persisted to disk so snapshots survive
  a node restart.

For database-backed persistence, implement this behaviour directly, or implement the
  `Finitomata.Persistency.Persistable` protocol for the carried struct and use the
  `Finitomata.Persistency.Protocol` adapter (see `examples/ecto_integration`).

## The `load/1` contract

`Finitomata.Engine` does not call `c:load/1` with a bare id; it builds a
  `{type, fields}` descriptor from the start payload (a module, a struct, or a
  `%{type: type, id: id}` map) and expects back a `{lifecycle, {state, payload}}`
  tuple. A `:loaded` lifecycle skips the entry transition and resumes from the
  persisted `state`; `:created`/`:unknown` proceed through the normal entry flow.

# `error`

```elixir
@type error() :: %{reason: any(), info: map(), payload: Finitomata.State.payload()}
```

A persisted transition failure

# `lifecycle`

```elixir
@type lifecycle() :: :loaded | :created | :unknown
```

The lifecycle of the loaded entity, driving whether the entry transition runs

# `load_descriptor`

```elixir
@type load_descriptor() :: {module(), keyword() | map()} | Finitomata.fsm_name()
```

The entity descriptor `Finitomata.Engine` passes to `c:load/1`

# `snapshot`

```elixir
@type snapshot() :: {Finitomata.Transition.state() | nil, Finitomata.State.payload()}
```

A persisted FSM snapshot: the current state and the carried payload

# `transition_info`

```elixir
@type transition_info() :: %{
  from: Finitomata.Transition.state(),
  to: Finitomata.Transition.state(),
  event: Finitomata.Transition.event(),
  event_payload: Finitomata.event_payload(),
  object: Finitomata.State.payload()
}
```

# `load`

```elixir
@callback load(descriptor :: load_descriptor()) ::
  {lifecycle(),
   {Finitomata.Transition.state() | nil, Finitomata.State.payload()}}
```

The function to be called from `init/1` callback upon FSM start to load the state and
  payload from the persistent storage.

It receives the `{type, fields}` descriptor built from the start payload and returns
  `{lifecycle, {state, payload}}`, where `state` is `nil` for a freshly created entity.

# `store`

```elixir
@callback store(
  id :: Finitomata.fsm_name(),
  object :: Finitomata.State.payload(),
  transition :: transition_info()
) :: :ok | {:ok, Finitomata.State.payload()} | {:error, any()}
```

The function to be called from `on_transition/4` handler to allow storing the state
  and payload to the persistent storage

# `store_error`
*optional* 

```elixir
@callback store_error(
  id :: Finitomata.fsm_name(),
  object :: Finitomata.State.payload(),
  reason :: any(),
  transition :: transition_info()
) :: :ok | {:error, any()}
```

The function to be called from `on_transition/4` handler on non successful
  transition to allow storing the failed attempt to transition to the persistent storage

---

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