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 (not Laravel events)

DBFlow alpha does not dispatch Laravel container events for workflow lifecycle boundaries. Instead, register host callbacks through WorkflowHooks:

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 not a Laravel Event facade target.

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);

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 (Early Access)

dbflowlabs/filament-pro adds:

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

Publication and full builder routing continue to evolve during alpha.

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 and run your definition sync step. There is no php artisan dbflow:health command in the current alpha packages.

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