Current section
Files
Jump to
Current section
Files
lib/ex_flow/storage.ex
defmodule ExFlow.Storage do
@moduledoc """
Behaviour for implementing graph storage adapters.
Storage adapters provide a pluggable backend for persisting and retrieving
flow graphs. This allows applications to choose between different storage
mechanisms (in-memory, database, file system, etc.) without changing the
core graph logic.
## Callbacks
All storage adapters must implement four callbacks:
- `c:load/1` - Retrieve a graph by ID
- `c:save/2` - Persist a graph with an ID
- `c:delete/1` - Remove a graph by ID
- `c:list/0` - List all stored graph IDs
## Built-in Adapters
- `ExFlow.Storage.InMemory` - Agent-based in-memory storage (included)
- `ExFlow.Storage.Ecto` - Database storage via Ecto (separate package)
## Example Implementation
defmodule MyApp.GraphStorage do
@behaviour ExFlow.Storage
@impl true
def load(id) do
case MyApp.Repo.get(Graph, id) do
nil -> {:error, :not_found}
graph -> {:ok, graph.data}
end
end
@impl true
def save(id, graph) do
%Graph{id: id, data: graph}
|> MyApp.Repo.insert_or_update()
|> case do
{:ok, _} -> :ok
{:error, reason} -> {:error, reason}
end
end
@impl true
def delete(id) do
case MyApp.Repo.get(Graph, id) do
nil -> {:error, :not_found}
graph -> MyApp.Repo.delete(graph)
end
end
@impl true
def list do
MyApp.Repo.all(from g in Graph, select: g.id)
end
end
## Usage in LiveView
def mount(_params, _session, socket) do
graph =
case MyStorage.load("my-graph-id") do
{:ok, graph} -> graph
{:error, :not_found} -> ExFlow.Core.Graph.new()
end
{:ok, assign(socket, :graph, graph)}
end
def handle_event("update_position", params, socket) do
graph = update_graph(socket.assigns.graph, params)
:ok = MyStorage.save("my-graph-id", graph)
{:noreply, assign(socket, :graph, graph)}
end
"""
@callback load(id :: String.t()) :: {:ok, any()} | {:error, term()}
@callback save(id :: String.t(), graph :: any()) :: :ok | {:error, term()}
@callback delete(id :: String.t()) :: :ok | {:error, term()}
@callback list() :: [String.t()]
end