Caravela.Flow (Caravela v0.8.0)

Copy Markdown View Source

Top-level entry point for the ephemeral async-workflow system.

use Caravela.Flow inside a module to declare flows via the DSL in Caravela.Flow.DSL:

defmodule MyApp.Flows.BookSyncFlow do
  use Caravela.Flow

  flow :sync_book, initial_state: %{dirty: false, book_id: nil} do
    repeat do
      wait_until fn state -> state.dirty end
      debounce 500
      run fn state ->
        MyApp.ExternalAPI.sync_book(state.book_id)
      end, retries: 3, backoff: :exponential, base_delay: 200
    end
  end
end

Start a flow runner with start/3, send external updates with signal/2, and read the current state with get_state/1.

{:ok, pid} =
  Caravela.Flow.start(MyApp.Flows.BookSyncFlow, :sync_book,
    initial_state: %{dirty: true, book_id: "abc"},
    notify: self())

# Later, flip `dirty` to kick the sync
Caravela.Flow.signal(pid, fn state -> %{state | dirty: true} end)

While the flow runs, each state change is delivered to the :notify pid as {:flow_state, new_state}. A LiveView can forward these messages to assigns — LiveSvelte picks up the prop diff and the Svelte component re-renders reactively.

Scope

Flows are ephemeral async orchestration only: debouncing, retries, sagas, parallel tasks. There is no event sourcing and no CQRS. Applications needing append-only logs should use Commanded.

Summary

Functions

Read the flow's current state.

Apply a mutating function to a running flow's state. Unblocks wait_until and resets debounce.

Stop a running flow.

Functions

get_state(pid)

Read the flow's current state.

signal(pid, fun)

Apply a mutating function to a running flow's state. Unblocks wait_until and resets debounce.

Caravela.Flow.signal(pid, fn state -> %{state | dirty: true} end)

start(flow_module, flow_name, opts \\ [])

Start a flow runner.

Caravela.Flow.start(MyApp.Flows.BookSyncFlow, :sync_book,
  initial_state: %{dirty: false, book_id: "abc"},
  notify: self())
#=> {:ok, #PID<0.456.0>}

Options:

  • :initial_state — overrides the flow's declared default state
  • :notify — a pid to receive {:flow_state, ...}, {:flow_done, ...}, {:flow_error, ...} messages
  • :tag — when set, every notification arrives wrapped as {:caravela_flow, tag, original_msg}. Use this to demultiplex many flows sharing one listener without forwarder processes.

If Caravela.Flow.Supervisor is running, the runner starts as a supervised child; otherwise it starts unsupervised (useful in tests).

stop(pid, reason \\ :normal)

Stop a running flow.