DBFlow Architecture

DBFlow is a DAG workflow runtime embedded inside your Laravel application. It is a schema-driven execution engine that advances object-bound state machines with database-backed concurrency guarantees.

High-level topology

┌──────────────────┐     ┌─────────────────────┐     ┌──────────────────────────┐
│  Laravel 13 App  │────▶│  DBFlow DAG Runtime │────▶│  dbflow_workflow_* tables│
│  (Eloquent Model)│     │  (DBFlow::start/…)  │     │  MySQL / PG / SQLite     │
└──────────────────┘     └─────────────────────┘     └──────────────────────────┘
         │                          │
         │                          ▼
         │                 ┌─────────────────────┐
         └────────────────▶│  WorkflowHooks      │
                           │  (host callbacks)   │
                           └─────────────────────┘

Pro licensing and dbflow.dev HQ services are separate from the open-core runtime. Your host application validates Pro entitlements; Core does not require a license check to execute workflows during local development.

DAG runtime mechanics

A workflow definition is a directed acyclic graph stored as JSON (schema_version: 1.0). Each node is one of five primitives:

Node type Responsibility
start Single entry point
approval Suspends until an authorized actor approves or rejects; creates WorkflowTask rows
condition Metadata/documentation node; routing uses outgoing transitions[].condition
action Invokes a registered ActionManager handler key
end Terminal node; sets instance completion status

Execution lifecycle

  1. Resolve definition — Load the active published version for the workflow key.
  2. Start instance — Create dbflow_workflow_instances with active_key set while running.
  3. Traverse — Walk transitions from start. Conditions evaluate immediately; approvals create tasks and return; actions execute inline.
  4. Persist logsWorkflowLogger writes dbflow_workflow_logs rows (WorkflowLogEvent types).
  5. Invoke hooksWorkflowHooks callbacks fire on started / approved / rejected / cancelled boundaries.

Approvals do not block the PHP process. The HTTP request returns after persisting the pending task. Resumption happens when an actor calls DBFlow::approve() or DBFlow::reject() on the WorkflowTask.

Concurrency: active_key

Parallel requests are a common source of corrupted hand-rolled workflows. DBFlow stores a unique active_key column on dbflow_workflow_instances while an instance is running.

Format:

{workflowKey}:{workflowableType}:{workflowableId}

There is no separate dbflow_active_keys table.

-- Simplified: active_key lives on the instance row
ALTER TABLE dbflow_workflow_instances
    ADD active_key VARCHAR(255) NULL,
    ADD UNIQUE KEY uq_dbflow_workflow_instances_active_key (active_key);

When the instance reaches a terminal status, active_key is cleared. Starting a second concurrent instance for the same workflowable slot throws WorkflowAlreadyRunningException. Acting on a non-pending task throws TaskNotPendingException or UserCannotApproveTaskException.

Properties:

  • Atomic — enforced inside database transactions with row-level checks
  • Database-native — no Redis requirement; works on MySQL, PostgreSQL, and SQLite
  • Fail-fast — duplicate starts and stale task actions surface explicit exceptions

Lifecycle hooks and Laravel events

DBFlow dispatches Laravel container events under DbflowLabs\Core\Events\* (WorkflowStarted, WorkflowCompleted, TaskApproved, TaskReassigned, ActionFailed, and others). Use these when you prefer listeners, queues, or broadcasting.

For synchronous host side effects on the business model, register callbacks through WorkflowHooks and TaskHooks:

use DbflowLabs\Core\DBFlow;
use DbflowLabs\Core\Services\WorkflowHooksRegistry;

DBFlow::registerWorkflowHooks(
    app(WorkflowHooksRegistry::class),
    'refund_dispute_approval',
    RefundDisputeWorkflowHooks::class,
);
interface WorkflowHooks
{
    public function onStarted(WorkflowInstance $instance): void;
    public function onApproved(WorkflowInstance $instance): void;
    public function onRejected(WorkflowInstance $instance): void;
    public function onCancelled(WorkflowInstance $instance): void;
}

WorkflowLogEvent is an enum used to classify rows in dbflow_workflow_logs. It is separate from both Laravel Event classes and WorkflowHooks callbacks.

User resolution

Actors passed to DBFlow::start(), approve(), reject(), and cancel() are resolved through DbflowLabs\Core\Contracts\UserResolver.

The default ConfigUserResolver resolves the Eloquent user model from:

  1. config('dbflow.auth.model')
  2. config('auth.providers.users.model')
  3. App\Models\User fallback

Override the resolver class via config('dbflow.auth.resolver') when your host uses UUID/ULID primary keys or custom authentication stacks.

Runtime entrypoints

All adapters should call the static DBFlow facade class — do not instantiate action classes manually in host code:

DBFlow::start(string $workflowKey, Model $workflowable, mixed $startedBy = null, array $metadata = []);
DBFlow::approve(WorkflowTask $task, mixed $actor = null, ?string $comment = null);
DBFlow::reject(WorkflowTask $task, mixed $actor = null, ?string $comment = null, RejectStrategy $strategy = RejectStrategy::Starter, ?string $targetNodeKey = null);
DBFlow::cancel(WorkflowInstance $instance, mixed $actor = null, ?string $comment = null);
DBFlow::reassign(WorkflowTask $task, mixed $fromActor, string $toUserId, ?string $comment = null);

Filament integration surface

Standard UI registers through:

DbflowLabs\Filament\Support\DBFlowFilamentPanel::register($panel);

Key pages:

  • MyWorkflowTasks — assignee task inbox
  • WorkflowInstances / ViewWorkflowInstance — operator visibility and timeline
  • WorkflowResource — definition draft/edit/publish

Timelines are rendered with WorkflowInstanceTimelinePresenter and the dbflow-filament::components.timeline Blade partial.

DBFlowFilamentPanel gates on dbflow-filament.enabled only — hosts should also gate registration on dbflow.enabled and product feature flags. Replace default AllowAllPermissionChecker before production.

Pro canvas (DBFlow Pro)

dbflowlabs/filament-pro adds:

  • ProCanvasField — Filament form field
  • ProCanvasWorkflowDefinitionEditorResolver — replaces Standard definition editor
  • ProGraphBlueprintCompiler / WorkflowGraphJsonParser — graph JSON → Core blueprint

Pro 1.0 ships draft save (ProCanvasDraftSaveAdapter), canvas validation, and Standard WorkflowResource integration. A dedicated standalone builder route remains deferred.

Deployment recommendations

The table below describes a typical Laravel host application stack. DBFlow Core does not require Redis, queues, or Horizon — workflow traversal and approvals run synchronously inside the request that calls DBFlow::start(), approve(), or reject().

Environment Database Queue (host app) Cache (host app)
Local SQLite sync file
Staging MySQL 8.0+ redis (optional) redis (optional)
Production PostgreSQL 16+ redis/horizon (optional) redis (optional)

Verify workflow tables after deploy with php artisan migrate --force, run php artisan dbflow:sync, and schedule php artisan dbflow:process-timeouts when definitions use approval deadlines. There is no php artisan dbflow:health command.

Related guides

Security notes

  • License keys should never be logged in plaintext.
  • Public documentation routes are English-only via locale middleware in DBFlow HQ.
  • Admin operations on dbflow.dev run through the private Filament panel.
Something wrong? Open an issue on GitHub