Durable.Orchestration (Durable v0.1.0-rc)

View Source

Workflow composition: call child workflows from parent steps.

Provides two primitives for composing workflows:

  • call_workflow/3 — Start a child workflow and wait for its result (synchronous)
  • start_workflow/3 — Start a child workflow without waiting (fire-and-forget)

Usage

defmodule MyApp.OrderWorkflow do
  use Durable
  use Durable.Context
  use Durable.Orchestration

  workflow "process_order" do
    step :charge_payment, fn data ->
      case call_workflow(MyApp.PaymentWorkflow, %{"amount" => data.total},
             timeout: hours(1)) do
        {:ok, result} ->
          {:ok, assign(data, :payment_id, result["payment_id"])}
        {:error, reason} ->
          {:error, reason}
      end
    end

    step :send_email, fn data ->
      {:ok, email_wf_id} = start_workflow(MyApp.EmailWorkflow,
        %{"to" => data.email}, ref: :confirmation)
      {:ok, assign(data, :email_workflow_id, email_wf_id)}
    end
  end
end

Summary

Functions

Injects orchestration functions into the calling module.

Starts a child workflow and waits for its result.

Starts a child workflow without waiting for its result (fire-and-forget).

Functions

__using__(opts)

(macro)

Injects orchestration functions into the calling module.

call_workflow(module, input, opts \\ [])

@spec call_workflow(module(), map(), keyword()) :: {:ok, map()} | {:error, term()}

Starts a child workflow and waits for its result.

The parent workflow will be suspended until the child completes or fails. On resume, returns {:ok, result} or {:error, reason}.

Options

  • :ref - Reference name for idempotency (default: module name)
  • :timeout - Timeout in milliseconds
  • :timeout_value - Value returned on timeout (default: :child_timeout)
  • :queue - Queue for the child workflow (default: "default")
  • :durable - Durable instance name (default: Durable)

What the parent sees on success

The result returned on success is the child workflow's entire final context, not just its explicit outputs. This includes every put_context/2 key the child set, plus the final step's return value.

If you need to expose only specific outputs, return a clean shape from the child's final step or pick only the keys you need in the parent:

case call_workflow(MyApp.PaymentWorkflow, %{"amount" => 100}) do
  {:ok, %{"payment_id" => id}} -> {:ok, assign(data, :payment_id, id)}
  {:error, reason} -> {:error, reason}
end

Examples

case call_workflow(MyApp.PaymentWorkflow, %{"amount" => 100}, timeout: hours(1)) do
  {:ok, result} -> {:ok, assign(data, :payment, result)}
  {:error, reason} -> {:error, reason}
end

start_workflow(module, input, opts \\ [])

@spec start_workflow(module(), map(), keyword()) :: {:ok, String.t()}

Starts a child workflow without waiting for its result (fire-and-forget).

Returns {:ok, child_id} immediately. The child runs independently. Idempotent: if resumed, returns the same child_id without creating a duplicate.

Options

  • :ref - Reference name for idempotency (default: module name)
  • :queue - Queue for the child workflow (default: "default")
  • :durable - Durable instance name (default: Durable)

Examples

{:ok, child_id} = start_workflow(MyApp.EmailWorkflow,
  %{"to" => email}, ref: :welcome_email)