Workflows
Workflows provide a powerful way to orchestrate complex, long-running processes in your application. They are built on top of the Laravel Workflow package and provide additional features like progress tracking, resourceable relationships, retryable activities, and termination support.Overview
A workflow is a sequence of activities that execute in order. Workflows are:- Persistent: State is stored in the database, allowing workflows to resume after failures
- Trackable: Progress, status, and logs are automatically tracked
- Resumable: Can be paused and resumed
- Retryable: Activities can be retried on failure
- Terminable: Can be gracefully terminated by user request
Laravel Workflow Foundation
This library builds on top of the Laravel Workflow package. Understanding the core concepts helps when working with workflows.Basic Workflow Structure
In Laravel Workflow, workflows extend theWorkflow class and implement an execute() method. The yield keyword is used to pause execution and wait for activity completion:
Basic Activity Structure
Activities extend theActivity class and implement an execute() method. They perform specific tasks like API requests, data processing, or sending emails:
Starting Workflows with WorkflowStub
Workflows are started usingWorkflowStub::make() and the start() method. The start() method returns immediately and executes asynchronously:
Passing Data
Data can be passed to workflows via thestart() method and will be available as arguments to execute():
ModelIdentifier is serialized (id, class, relations, connection). The full model is retrieved from the database when the workflow or activity runs.
Workflow output can be retrieved after completion:
execute() methods and Laravel’s service container will inject them automatically.
Workflow Status
Monitor workflow status using therunning() method (returns true if still running) or status() method:
WorkflowCreatedStatusWorkflowCompletedStatusWorkflowContinuedStatusWorkflowFailedStatusWorkflowPendingStatusWorkflowRunningStatusWorkflowWaitingStatus
Workflow ID
Get the workflow ID when starting:Laravel Workflow Features
Laravel Workflow provides powerful features for building complex, reliable workflows. These features are available in workflows that extendInly\Core\Workflows\Base\Workflow.
Signals
Signals allow external processes to communicate with a running workflow. Define signal methods using the#[SignalMethod] attribute:
await() helper pauses workflow execution until a condition becomes true, commonly used with signals.
Queries
Queries allow you to inspect a workflow’s current state without altering its execution. Define query methods using the#[QueryMethod] attribute:
Timers
Durable timers allow workflows to pause for a specified duration and resume after system restarts or failures:Workflow\now() instead of Carbon::now() or now() inside workflows to maintain determinism during replay.
Signal + Timer
Combine waiting for a signal with a timeout usingawaitWithTimeout():
true if the condition becomes true before timeout, false if the timer expires.
Heartbeats
Heartbeats prevent activity timeouts during long-running operations. Use them inside activities:Side Effects
Side effects wrap non-deterministic operations that should execute exactly once. The result is stored and reused during replay:Child Workflows
Workflows can spawn child workflows for modularization and reuse:all():
Concurrency
Execute multiple activities or workflows in parallel or series:Sagas
Sagas implement distributed transactions by coordinating multiple activities with compensation logic:Events
Workflows can emit and listen to events. Events are typically implemented using signals or by dispatching Laravel events from activities:Webhooks
Workflows can be triggered via webhooks. Create webhook endpoints in your routes:Continue As New
For workflows that process large datasets over long periods, use “continue as new” to reset workflow history and prevent unbounded event log growth:- Prevents event log from growing indefinitely
- Improves performance for long-running workflows
- Allows workflow to process millions of items without memory issues
Versioning
When updating workflow code with breaking changes, use versioning to handle running workflows gracefully:- Increment version when changing workflow structure or logic
- Old workflow instances continue with their original version
- New instances use the latest version
- Consider backward compatibility when possible
- Keep old workflow code available until all instances complete
- Manually migrate or terminate old workflow instances
- Use version checks to handle different workflow versions in your code
Laravel Workflow Configuration
Publishing Config
Publish the workflow configuration file:config/workflow.php with customizable options.
Options
Key configuration options include:- Connection: Database connection for workflow storage
- Queue: Queue connection for workflow execution
- Timeout: Default activity timeout
- Retry: Default retry configuration
- Sticky: Whether to ensure same server execution
Ensuring Same Server
Configure workflows to run on the same server instance for stateful operations:Database Connection
Specify a custom database connection for workflow storage:Microservices
Workflows can span multiple services. Configure service-specific settings and ensure proper serialization of workflow data across service boundaries.Pruning Workflows
Automatically clean up old completed workflows:Laravel Workflow Constraints
To ensure workflows execute correctly and can be replayed deterministically, certain constraints must be followed.Overview
Workflows must be deterministic—they must produce the same result when replayed with the same inputs. Activities must be idempotent—they should safely handle retries without unintended side effects.Workflow Constraints
Workflows must not:- Use
Carbon::now(),now(), or system time functions (useWorkflow\now()instead) - Access
Auth::user()or other request context - Make direct network/HTTP requests
- Use random functions (unless wrapped in
sideEffect()) - Access mutable global state
- Perform database queries directly (use activities instead)
Activity Constraints
Activities should:- Be idempotent (safe to retry)
- Handle duplicate invocations gracefully
- Use transactions where appropriate
- Validate inputs before performing operations
- Make network requests
- Access the database
- Use system time functions
- Access request context (with caution)
Constraints Summary
| Constraint | Workflows | Activities |
|---|---|---|
| Deterministic | ✅ Required | ❌ Not required |
| Idempotent | ✅ Recommended | ✅ Strongly required |
| External IO | ❌ Forbidden | ✅ Allowed |
| Network calls | ❌ Forbidden | ✅ Allowed |
| System time | ❌ Use Workflow\now() | ✅ Allowed |
| Random functions | ❌ Use sideEffect() | ✅ Allowed |
| Database queries | ❌ Use activities | ✅ Allowed |
Testing Workflows
Test workflows usingWorkflowStub::fake() to run them synchronously:
Failures and Recovery
Activity Failures
Activities can throw exceptions. By default, they retry indefinitely with exponential backoff:Non-Retryable Exceptions
Mark exceptions as non-retryable to fail immediately:Workflow Status During Failures
When activities fail:- Workflow status remains RUNNING while retries are attempted
- Workflow transitions to FAILED only after all retries are exhausted
- Workflow can be recovered by fixing the underlying issue and redeploying
Recovery Process
- Identify the failure: Check workflow logs and activity exceptions
- Fix the root cause: Update code, fix data, resolve external issues
- Redeploy: Deploy the fix
- Automatic retry: Activities continue retrying automatically
- Monitor: Watch workflow progress until completion or permanent failure
How It Works
Laravel Workflow uses an event-sourcing approach:- Event Log: All workflow actions (activities, signals, timers) are stored as events in the database
- Deterministic Replay: Workflows are replayed from events, ensuring consistent state
- Persistence: Workflow state is derived from events, not stored directly
- Resumability: After failures, workflows resume from the last event
- Queue-Based: Workflows execute asynchronously via Laravel queues
- Reliability: Workflows survive server crashes and restarts
- Consistency: Deterministic execution ensures correct results
- Debuggability: Event log provides complete audit trail
- Scalability: Workflows can be distributed across multiple workers
Creating a Workflow
Workflows extend theInly\Core\Workflows\Base\Workflow class and implement an execute() method:
Workflow Categories
Set a category to group related workflows together:FORTNOX, SHOPIFY, HUBSPOT, SNOWFLAKE, BUSINESS_CENTRAL, FILE_IMPORT, HARVEST, NOTION.
Dispatchable Workflows
To allow workflows to be started from the frontend, add the#[Dispatchable] attribute:
Creating an Activity
Activities are the building blocks of workflows. They extendInly\Core\Workflows\Base\Activity and implement an execute() method:
Activity Execution Methods
Activities can be executed in three ways:- As part of a workflow (most common):
- As a standalone workflow:
- As a synchronous action (for testing or simple operations):
Starting Workflows
Using the start() Method
The SelfStartingWorkflow trait provides a start() method that automatically sets the category and causer:
CauserResolver.
Workflow Arguments
Workflows can accept any number of arguments:Progress Tracking
Workflows support progress tracking to show completion status in the UI.Setting Total Count
Set the total number of items to process:Tracking in Activities
Activities automatically track processed items:incrementProcessedCount() method automatically updates the parent workflow’s progress. Progress is saved in batches of 100 for performance.
Failed Count Tracking
Track failed items separately:Resourceable Relationships
Workflows can be associated with Eloquent models using the resourceable morph relationship. This allows you to track which workflows are related to specific resources.Attaching Resources
Attach a resource from within an activity:Querying Workflows by Resource
Find all workflows for a specific resource:Inverse Relationships
Add theHasWorkflows trait to your models:
Tracking Latest Workflow
If your model implementsTracksLatestWorkflow, the latest workflow ID is automatically updated when a resource is attached:
Retryable Activities
Theretryable() method allows activities to be retried when they fail. This is useful for handling transient errors or network issues.
Using retryable
How Retryable Works
- The activity is executed using
ActivityStub::make() - If an exception occurs, the workflow pauses and waits for a retry signal
- The
retry()signal method can be called to trigger a retry - The activity is re-executed until it succeeds
Manual Retry Control
Trigger a retry manually:Termination
Workflows can be gracefully terminated by setting thetermination_requested_at timestamp. The workflow will check for termination at key points and throw a RuntimeException if termination is requested.
Requesting Termination
Automatic Termination Checks
Workflows automatically check for termination:- At the start of
handle() - Every 100 processed items in activities
- Before saving progress updates
Environment
Set the environment to track which instance of a category the workflow is running on:Workflow Status
Workflows have several statuses:- PENDING: Workflow is queued but not started
- RUNNING: Workflow is currently executing
- WAITING: Workflow is paused (e.g., waiting for retry signal)
- COMPLETED: Workflow finished successfully
- FAILED: Workflow encountered an error
- TERMINATED: Workflow was terminated by user request
- TERMINATION_REQUESTED: Termination has been requested but not yet processed
- RETRYABLE: Workflow is waiting for a retry signal
Best Practices
- Use descriptive labels: Implement
label()methods for better UI display - Set categories: Always set a category for better organization
- Track progress: Use progress tracking for long-running workflows
- Handle exceptions: Use
incrementFailedProcessedCount()andhandleException()in activities - Attach resources: Attach resources early in the workflow for better tracking
- Use retryable for transient errors: Wrap activities that might fail due to network issues
- Keep activities focused: Each activity should do one thing well