Laravel Cloud is here! Zero-config managed infrastructure for Laravel apps. Deploy now.

Chevere Workflow: A Declarative PHP Workflow Engine with Async Job Execution

Published on by

Chevere Workflow: A Declarative PHP Workflow Engine with Async Job Execution image

When your application needs to run a sequence of steps — some dependent on each other, others that could run at the same time — it's tempting to stuff everything into a single class or chain method calls together. The Chevere Workflow package offers a cleaner approach: declare your jobs, wire up their dependencies, and let the engine automatically determine execution order.

Installation

composer require chevere/workflow

A Practical Example

Consider a user registration flow that needs to:

  • Create the user account
  • Send a welcome email
  • Set up a free trial subscription
  • Notify your team on Slack

Some of these steps depend on the account being created first, while others can run in parallel once it is. Managing this manually means tracking shared state, writing glue code, and handling partial failures yourself. Workflow handles all of that declaratively.

Building the Pipeline

Here is how you might model the onboarding flow above:

use function Chevere\Workflow\{workflow, sync, async, variable, response, run};
 
$workflow = workflow(
createAccount: sync(
CreateUserAccount::class,
name: variable('name'),
email: variable('email'),
password: variable('password'),
),
sendWelcomeEmail: async(
SendWelcomeEmail::class,
userId: response('createAccount', 'id'),
email: response('createAccount', 'email'),
),
createTrial: sync(
CreateTrialSubscription::class,
userId: response('createAccount', 'id'),
)->withRunIf(variable('enableTrial')),
notifyTeam: async(
NotifySlackChannel::class,
userEmail: response('createAccount', 'email'),
),
);

A few things worth noting here:

  • sync() blocks until the job completes; async() runs concurrently once its dependencies are met
  • variable('name') is a placeholder for a value you supply at runtime
  • response('createAccount', 'id') references the id field from createAccount's output
  • ->withRunIf(variable('enableTrial')) makes the createTrial job conditional — it is skipped entirely if enableTrial is falsy

The engine builds a dependency graph from these declarations. Because sendWelcomeEmail and notifyTeam both depend only on createAccount, they run concurrently once the account is ready — without you needing to orchestrate that yourself.

Defining a Job

Each job can be a closure, an invocable class, or a class extending Chevere's Action. Here's a straightforward invocable:

use Chevere\Action\Action;
 
class CreateUserAccount extends Action
{
public function __invoke(
string $name,
string $email,
string $password,
): array {
$user = User::create([
'name' => $name,
'email' => $email,
'password' => bcrypt($password),
]);
 
return [
'id' => $user->id,
'email' => $user->email,
];
}
}

The return value becomes the job's response. Downstream jobs reference individual fields via response('createAccount', 'id') or the whole response via response('createAccount').

Running the Workflow

Pass your variables directly to run(), and access any job's output from the result:

$result = run(
$workflow,
name: 'Jane Doe',
email: 'jane@example.com',
password: 'secret',
enableTrial: true,
);
 
$userId = $result->response('createAccount')->int('id');

Handling Failures

If any job throws, Workflow wraps it in a WorkflowException that tells you exactly which job failed and why:

use Chevere\Workflow\Exceptions\WorkflowException;
 
try {
$result = run($workflow, ...);
} catch (WorkflowException $e) {
logger()->error("Job '{$e->name}' failed", [
'error' => $e->throwable->getMessage(),
]);
}

Retrying Unreliable Jobs

For jobs that call external services prone to occasional hiccups, attach a retry policy with withRetry():

notifyTeam: async(
NotifySlackChannel::class,
userEmail: response('createAccount', 'email'),
)->withRetry(timeout: 60, maxAttempts: 3, delay: 5),

Laravel Integration

A dedicated package brings first-class Laravel support, adding Artisan commands, an AbstractWorkflow base class, and a facade for running workflows anywhere in your app.

composer require chevere/workflow-laravel

Generate a workflow class with Artisan:

php artisan make:workflow UserOnboarding

Then fill in the definition using the same jobs from earlier:

use Chevere\WorkflowLaravel\AbstractWorkflow;
use Chevere\Workflow\Interfaces\WorkflowInterface;
use function Chevere\Workflow\{workflow, sync, async, variable, response};
 
class UserOnboarding extends AbstractWorkflow
{
protected function definition(): WorkflowInterface
{
return workflow(
createAccount: sync(
CreateUserAccount::class,
name: variable('name'),
email: variable('email'),
password: variable('password'),
),
sendWelcomeEmail: async(
SendWelcomeEmail::class,
userId: response('createAccount', 'id'),
email: response('createAccount', 'email'),
),
createTrial: sync(
CreateTrialSubscription::class,
userId: response('createAccount', 'id'),
)->withRunIf(variable('enableTrial')),
notifyTeam: async(
NotifySlackChannel::class,
userEmail: response('createAccount', 'email'),
),
);
}
}

Run it via the Workflow facade — from a controller, a job, or anywhere else:

use Chevere\WorkflowLaravel\Facades\Workflow;
 
$result = Workflow::run(UserOnboarding::class,
name: $request->name,
email: $request->email,
password: $request->password,
enableTrial: $request->boolean('enable_trial'),
);
 
$userId = $result->response('createAccount')->int('id');

Laravel's service container automatically handles constructor injection for class-based jobs, so any service your job needs can be type-hinted and resolved without extra configuration.

You can also list all registered workflows with php artisan workflow:list, or trigger one directly from the terminal with php artisan workflow:run.

Visualising the Graph

If you use VS Code, the package has an official extension that renders your workflow as a Mermaid flowchart. The diagram shows each job as a node, draws edges between dependent jobs, and annotates those edges with the data flowing between them — so you can see at a glance that sendWelcomeEmail waits on createAccount, or that createTrial only runs when enableTrial is truthy. Because the graph is derived directly from your code, it stays accurate as you add or rearrange jobs with nothing to maintain separately.

Wrapping Up

Chevere Workflow provides structure for multi-step processes that would otherwise scatter logic across multiple classes or make a single method unwieldy. The declarative style makes it easy to see what runs when, and the automatic dependency graph means parallel execution happens with no extra configuration.

The package also ships with testing utilities so you can verify each job in isolation and assert that your workflow's execution graph matches your expectations — something that is much harder when steps are tightly coupled.

Check out the Chevere Workflow repository on GitHub for the full feature set, including Action classes, Workflow Providers, and the VS Code extension.

Yannick Lyn Fatt photo

Staff Writer at Laravel News and Full stack web developer.

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.

image
Laravel Code Review

Get expert guidance in a few days with a Laravel code review

Visit Laravel Code Review
Laravel Cloud logo

Laravel Cloud

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Cloud
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Multi-tenant Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit
Acquaint Softtech logo

Acquaint Softtech

Acquaint Softtech offers AI-ready Laravel developers who onboard in 48 hours at $3000/Month with no lengthy sales process and a 100 percent money-back guarantee.

Acquaint Softtech
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
PhpStorm logo

PhpStorm

The go-to PHP IDE with extensive out-of-the-box support for Laravel and its ecosystem.

PhpStorm
Harpoon: Next generation time tracking and invoicing logo

Harpoon: Next generation time tracking and invoicing

The next generation time-tracking and billing software that helps your agency plan and forecast a profitable future.

Harpoon: Next generation time tracking and invoicing
Lucky Media logo

Lucky Media

Get Lucky Now - the ideal choice for Laravel Development, with over a decade of experience!

Lucky Media
Get expert guidance in a few days with a Laravel code review logo

Get expert guidance in a few days with a Laravel code review

Expert code review! Get clear, practical feedback from two Laravel devs with 10+ years of experience helping teams build better apps.

Get expert guidance in a few days with a Laravel code review
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum

The latest

View all →
Laravel Schema Sentinel: Detect and Fix Database Schema Drift image

Laravel Schema Sentinel: Detect and Fix Database Schema Drift

Read article
Laravel Web Push Notifications image

Laravel Web Push Notifications

Read article
RedBerry to Host Georgia's First Laravel Meetup in Tbilisi image

RedBerry to Host Georgia's First Laravel Meetup in Tbilisi

Read article
Interruptible Jobs in Laravel 13.7.0 image

Interruptible Jobs in Laravel 13.7.0

Read article
A Free Shift to Check If Your App is Ready for Laravel Cloud image

A Free Shift to Check If Your App is Ready for Laravel Cloud

Read article
Laravel Idempotency: HTTP Idempotency Middleware for Laravel image

Laravel Idempotency: HTTP Idempotency Middleware for Laravel

Read article