Skip to main content

guides/your-first-workflow.md

# Your First Workflow

Continuum workflows are ordinary Elixir modules that use
`Continuum.Workflow`. The workflow function is pure orchestration code:
it may call activities, wait for signals, set timers, and use Continuum's
deterministic primitives.

```elixir
defmodule MyApp.OrderFlow do
  use Continuum.Workflow, version: 1

  def run(%{order_id: order_id, items: items}) do
    {:ok, validated} = activity MyApp.Activities.ValidateOrder.run(%{items: items})
    {:ok, charge} = activity MyApp.Activities.ChargeCard.run(%{order_id: order_id})

    case await signal(:fraud_review) do
      :approved -> activity MyApp.Activities.ShipOrder.run(%{order_id: order_id})
      :rejected -> {:error, {:rejected, charge}}
    end

    {:ok, validated}
  end
end
```

Start the run from application code:

```elixir
{:ok, run_id} =
  Continuum.start(MyApp.OrderFlow, %{
    order_id: "order_123",
    items: [%{sku: "sku_1", qty: 1}]
  })
```

Send a signal when outside input arrives:

```elixir
:ok = Continuum.signal(run_id, :fraud_review, :approved)
```

Wait for completion in tests or synchronous scripts:

```elixir
{:ok, %{state: :completed, result: result}} = Continuum.await(run_id, 5_000)
```

For Postgres-backed execution, configure a repo and generate the schema:

```elixir
config :continuum,
  repo: MyApp.Repo,
  journal: Continuum.Runtime.Journal.Postgres
```

```bash
mix continuum.gen.migration --repo MyApp.Repo
mix ecto.migrate
```

Add Continuum's runtime children after your repo in your application's
supervision tree:

```elixir
children =
  [
    MyApp.Repo,
    {Phoenix.PubSub, name: MyApp.PubSub}
  ] ++ Continuum.children()
```

The engine persists every effect in `continuum_events`. If the BEAM process
dies, the dispatcher re-leases suspended work and replays the history through
the same workflow module.