dimita/laravel-business-orchestration-engine

A Laravel library for business orchestration including Saga Pattern, Workflow, Event Sourcing, Versioning, Rule Engine, and more.

Maintainers

Package info

github.com/DIMITA/laravel-business-orchestration-engine

pkg:composer/dimita/laravel-business-orchestration-engine

Statistics

Installs: 10

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

1.0.0 2025-12-29 13:44 UTC

This package is auto-updated.

Last update: 2026-03-01 00:48:48 UTC


README

Latest Stable Version Total Downloads Monthly Downloads

A comprehensive Laravel package for business orchestration, including Saga Pattern, Workflow Management, Event Sourcing, Versioning, Rule Engine, and Dependency Management.

Note: This package is based on battle-tested code patterns I've been using in production for years. I've packaged it to make these proven patterns easily reusable across projects.

Tests Coverage PHP Laravel

Requirements

  • PHP: ^8.1 or higher
  • Laravel: ^10.0 or ^11.0
  • Database: MySQL 5.7+, PostgreSQL 10+, or SQLite 3.8+
  • Extensions:
    • ext-json - JSON support for payload serialization
    • ext-pdo - Database connectivity

Optional Requirements

For enhanced saga orchestration capabilities:

  • Queue Driver: Redis, Database, or SQS for asynchronous step execution
  • Cache Driver: Redis or Memcached for performance optimization
  • Message Queue (optional): RabbitMQ for distributed saga coordination

Table of Contents

Installation

Install the package via Composer:

composer require dimita/laravel-business-orchestration-engine

Publish the configuration and migrations:

php artisan vendor:publish --provider="Dimita\\BusinessOrchestration\\BusinessOrchestrationServiceProvider"
php artisan migrate

Features

✨ 7 Powerful Engines

Engine Description Use Case
Saga Pattern Distributed transactions with automatic compensation Complex business processes (orders, payments)
Workflow Engine State machine with guards and transitions Document validation, approval processes
Event Sourcing Append-only event store Audit trail, historical state reconstruction
Versioning Immutable model snapshots Change history, rollback capability
Rule Engine Business rule evaluation via AST Dynamic discounts, business validation
Sync Engine Multi-device synchronization Offline-first apps, mobile sync
Dependency Engine Business constraint management Pre-delete validation, dependency graphs

Independent vs Combined Usage

This package offers maximum flexibility - use engines independently or together based on your needs.

Independent Usage

Perfect when you only need specific functionality:

use Dimita\BusinessOrchestration\Core\VersionEngine;
use Dimita\BusinessOrchestration\Core\RuleEngine;
use Dimita\BusinessOrchestration\Core\SyncEngine;

// Method 1: Direct class resolution
$versionEngine = app(VersionEngine::class);
$versionEngine->snapshot($model);

// Method 2: Using aliases
$ruleEngine = app('rule-engine');
$rule = $ruleEngine->rule('MyRule')
    ->when('amount', '>', 1000)
    ->then(fn($ctx) => ['discount' => 10]);

// Method 3: Dependency injection (recommended)
class OrderController
{
    public function __construct(
        private VersionEngine $version,
        private RuleEngine $rules
    ) {}

    public function process(Order $order)
    {
        $this->version->snapshot($order);
        $discountRule = $this->rules->getMatchingRules(['amount' => $order->total]);
    }
}

Combined Usage

Use the main facade when working with multiple engines:

$orchestration = app('business-orchestration');

// Access any engine
$saga = $orchestration->saga();
$workflow = $orchestration->workflow();
$version = $orchestration->version();
$rules = $orchestration->rule();
$sync = $orchestration->sync();
$eventSourcing = $orchestration->eventSourcing();
$dependency = $orchestration->dependency();

Available Aliases

// All engines are available via these aliases:
app('saga-engine')           // SagaEngine
app('workflow-engine')       // WorkflowEngine
app('sync-engine')           // SyncEngine
app('version-engine')        // VersionEngine
app('event-sourcing-engine') // EventSourcingEngine
app('rule-engine')           // RuleEngine
app('dependency-engine')     // DependencyEngine

When to Use Each Approach

Approach Best For
Independent (Class) Type safety, IDE autocompletion, single-engine projects
Independent (Alias) Quick prototyping, flexibility, simpler syntax
Dependency Injection Production code, testability, SOLID principles
Combined Facade Complex workflows using multiple engines

Configuration for Selective Loading

Improve performance by loading only the engines you need. Edit config/business-orchestration.php:

'engines' => [
    'saga' => true,              // Enable Saga Pattern
    'workflow' => true,          // Enable Workflow Engine
    'sync' => false,             // Disable Sync Engine (not needed)
    'version' => true,           // Enable Versioning
    'event_sourcing' => false,   // Disable Event Sourcing (not needed)
    'rule' => true,              // Enable Rule Engine
    'dependency' => false,       // Disable Dependency Engine (not needed)
],

Or use environment variables in your .env:

# Enable only the engines you need
ORCHESTRATION_SAGA_ENABLED=true
ORCHESTRATION_WORKFLOW_ENABLED=true
ORCHESTRATION_SYNC_ENABLED=false
ORCHESTRATION_VERSION_ENABLED=true
ORCHESTRATION_EVENT_SOURCING_ENABLED=false
ORCHESTRATION_RULE_ENABLED=true
ORCHESTRATION_DEPENDENCY_ENABLED=false

Benefits of selective loading:

  • Reduced memory footprint
  • Faster application bootstrap
  • Cleaner service container
  • Only load what you actually use

Note: If you try to use a disabled engine, you'll get a clear error message:

RuntimeException: SyncEngine is not enabled. Enable it in config/business-orchestration.php

Usage Guide

1. Saga Pattern

When to use? When you have a business transaction involving multiple services and need to rollback (compensate) on failure.

Simple Example - Order Processing

use Dimita\BusinessOrchestration\BusinessOrchestration;

// Define your saga steps
class ValidateOrderStep
{
    public function execute($payload)
    {
        $order = Order::find($payload['order_id']);

        if (!$order->isValid()) {
            throw new \Exception('Invalid order');
        }

        return true;
    }
}

class ChargePaymentStep
{
    public function execute($payload)
    {
        $order = Order::find($payload['order_id']);

        // Charge payment
        $payment = PaymentGateway::charge($order->total);

        if (!$payment->success) {
            throw new \Exception('Payment failed');
        }

        return true;
    }
}

class ShipOrderStep
{
    public function execute($payload)
    {
        $order = Order::find($payload['order_id']);

        // Ship the order
        ShippingService::ship($order);

        return true;
    }
}

// Start the saga synchronously
$saga = BusinessOrchestration::saga()->startSaga('OrderProcessing', [
    'validate' => ValidateOrderStep::class,
    'charge' => ChargePaymentStep::class,
    'ship' => ShipOrderStep::class,
], ['order_id' => 123]);

// If a step fails, completed steps will be automatically compensated

Advanced Compensation

Define compensation logic for each step to properly rollback changes:

class ChargePaymentStep
{
    public function execute($payload)
    {
        $order = Order::find($payload['order_id']);
        $payment = PaymentGateway::charge($order->total);

        if (!$payment->success) {
            throw new \Exception('Payment failed');
        }

        return true;
    }

    // Define compensation logic
    public function compensate($payload)
    {
        $order = Order::find($payload['order_id']);

        // Refund the payment
        PaymentGateway::refund($order->total);

        // Update order status
        $order->update(['status' => 'payment_refunded']);
    }
}

Asynchronous Execution

Execute sagas asynchronously using Laravel queues:

// Start saga asynchronously (returns immediately)
$saga = BusinessOrchestration::saga()->startSagaAsync('OrderProcessing', [
    'validate' => ValidateOrderStep::class,
    'charge' => ChargePaymentStep::class,
    'ship' => ShipOrderStep::class,
], ['order_id' => 123]);

// Check saga status later
$status = BusinessOrchestration::saga()->getSagaStatus($saga->id);

echo $status['status']; // PENDING, RUNNING, COMPLETED, COMPENSATED
echo $status['completed_steps'] . '/' . $status['total_steps'];

Resume After Crash

// If your server crashes during execution, resume the saga
$sagaEngine = BusinessOrchestration::saga();
$sagaEngine->resumeSaga($sagaId);

Cancel Running Saga

// Cancel a saga that's pending or running
$sagaEngine = BusinessOrchestration::saga();
$sagaEngine->cancelSaga($sagaId);

Saga Status Flow

  • PENDING - Saga created, not started yet
  • RUNNING - Saga currently executing
  • COMPLETED - All steps completed successfully
  • FAILED - A step failed (before compensation)
  • COMPENSATED - Completed steps have been rolled back after failure
  • CANCELLED - Saga was manually cancelled
  • COMPENSATION_FAILED - Compensation encountered an error (step-level)

2. Workflow Engine

When to use? When you need a state machine to manage transitions between different business states.

Simple Example - Contract Validation

use Dimita\BusinessOrchestration\BusinessOrchestration;

$workflow = BusinessOrchestration::workflow();

// Define possible transitions
$workflow->defineTransition('submit', 'draft', 'submitted');
$workflow->defineTransition('review', 'submitted', 'in_review');
$workflow->defineTransition('approve', 'in_review', 'approved');
$workflow->defineTransition('reject', 'in_review', 'rejected');
$workflow->defineTransition('revise', 'rejected', 'draft');

// Use workflow on a model
$contract = Contract::find(1);
$builder = $workflow->for($contract);

// Check if transition is possible
if ($builder->can('approve')) {
    $builder->apply('approve');
}

// Get current state
echo $builder->getState(); // 'approved'

With Guards (Conditions)

// Define transition with condition
$workflow->defineTransition(
    'auto_approve',
    'submitted',
    'approved',
    'return $context["amount"] < 1000;' // Guard expression
);

// Transition only possible if amount < 1000

Complex Workflow - Ticket Management

// Draft -> Open -> In Progress -> (Resolved | Closed)
//                     ↓
//                  On Hold

$workflow->defineTransition('open', 'draft', 'open');
$workflow->defineTransition('start', 'open', 'in_progress');
$workflow->defineTransition('hold', 'in_progress', 'on_hold');
$workflow->defineTransition('resume', 'on_hold', 'in_progress');
$workflow->defineTransition('resolve', 'in_progress', 'resolved');
$workflow->defineTransition('close', 'resolved', 'closed');

$ticket = Ticket::find(1);
$builder = $workflow->for($ticket);

// Apply transitions through lifecycle
$builder->apply('open');
$builder->apply('start');
$builder->apply('hold');
$builder->apply('resume');
$builder->apply('resolve');
$builder->apply('close');

Advanced Features

Register Workflow Definition
// Define a complete workflow with configuration
$workflow->registerWorkflow('order_approval', [
    'transitions' => [
        ['name' => 'submit', 'from' => 'draft', 'to' => 'pending'],
        ['name' => 'approve', 'from' => 'pending', 'to' => 'approved'],
        ['name' => 'reject', 'from' => 'pending', 'to' => 'rejected'],
    ]
]);
Event Hooks
// Execute custom logic before transitions
$workflow->beforeTransition('approve', function($instance) {
    Log::info("Approving workflow for {$instance->model_type}");
    // Send notification, update related records, etc.
});

// Execute custom logic after transitions
$workflow->afterTransition('approve', function($instance) {
    Mail::to($user)->send(new ApprovalConfirmation());
});
Get Available Transitions
$builder = $workflow->for($document);

// Get all transitions available from current state
$availableTransitions = $builder->getEnabledTransitions(['amount' => 500]);

// Returns: ['approve', 'reject', 'request_changes']
Check Workflow State
// Check if model is in specific state
if ($workflow->isInState($order, 'approved')) {
    // Process approved order
}

// Get all possible states in the workflow
$allStates = $workflow->getAllStates();
// Returns: ['draft', 'pending', 'approved', 'rejected']
Force State Change
// Override guards and force a state change (use carefully)
$builder->forceTransition('cancelled', 'Manual cancellation by admin');

3. Event Sourcing

When to use? When you need to keep a complete history of all changes and be able to rebuild state at any point in time.

Simple Example - Shopping Cart

use Dimita\BusinessOrchestration\BusinessOrchestration;

$es = BusinessOrchestration::eventSourcing();

// Store events
$es->storeEvent('cart-123', 'CartCreated', [
    'user_id' => 456,
    'created_at' => now()
]);

$es->storeEvent('cart-123', 'ItemAdded', [
    'product_id' => 789,
    'quantity' => 2,
    'price' => 29.99
]);

$es->storeEvent('cart-123', 'ItemAdded', [
    'product_id' => 101,
    'quantity' => 1,
    'price' => 49.99
]);

$es->storeEvent('cart-123', 'CartCheckedOut', [
    'total' => 109.97,
    'payment_method' => 'credit_card'
]);

// Rebuild cart state
$cart = $es->rebuildAggregate('cart-123', function($state, $event) {
    switch ($event['event_type']) {
        case 'CartCreated':
            return [
                'user_id' => $event['payload']['user_id'],
                'items' => [],
                'total' => 0,
                'status' => 'active'
            ];

        case 'ItemAdded':
            $state['items'][] = $event['payload'];
            $state['total'] += $event['payload']['price'] * $event['payload']['quantity'];
            return $state;

        case 'CartCheckedOut':
            $state['status'] => 'checked_out';
            return $state;

        default:
            return $state;
    }
});

print_r($cart);
// Array (
//     'user_id' => 456,
//     'items' => [...],
//     'total' => 109.97,
//     'status' => 'checked_out'
// )

Retrieve All Events

$events = $es->getEvents('cart-123');

foreach ($events as $event) {
    echo "{$event['event_type']} at version {$event['version']}\n";
}

Advanced Features

Projectors - Create Read Models

Projectors listen to events and create read models (projections) for querying:

class OrderTotalProjector
{
    // Called when MoneyAdded event is stored
    public function onMoneyAdded($event)
    {
        $account = Account::findOrFail($event->aggregate_id);
        $account->increment('balance', $event->payload['amount']);
    }

    // Called when MoneySubtracted event is stored
    public function onMoneySubtracted($event)
    {
        $account = Account::findOrFail($event->aggregate_id);
        $account->decrement('balance', $event->payload['amount']);
    }
}

// Register the projector
$es->addProjector(OrderTotalProjector::class);

// Now when you store events, projector will automatically update read models
$es->storeEvent('account-123', 'MoneyAdded', ['amount' => 100]);
Reactors - Handle Side Effects

Reactors respond to events with side effects (emails, notifications, etc.):

class SendEmailReactor
{
    public function onOrderPlaced($event)
    {
        // Send confirmation email
        Mail::to($event->payload['email'])->send(new OrderConfirmation($event));
    }
}

// Register the reactor
$es->addReactor(SendEmailReactor::class);

// Reactor will handle side effects asynchronously
$es->storeEvent('order-456', 'OrderPlaced', ['email' => 'customer@example.com']);
Event Replay

Rebuild projections by replaying all events:

// Replay all events through projectors
$count = $es->replay();
echo "Replayed {$count} events";

// Replay only specific aggregate
$count = $es->replay('account-123');

// Replay through specific projectors only
$count = $es->replay(null, [OrderTotalProjector::class]);
Snapshots for Performance

Create snapshots to avoid replaying thousands of events:

// Create a snapshot of current state
$cart = $es->rebuildAggregate('cart-123', $reducer);
$es->snapshot('cart-123', $cart);

// Retrieve latest snapshot instead of rebuilding from all events
$cart = $es->getLatestSnapshot('cart-123');

if (!$cart) {
    // No snapshot exists, rebuild from events
    $cart = $es->rebuildAggregate('cart-123', $reducer);
}
Metadata and Event Queries
// Store event with metadata
$es->storeEvent('order-789', 'OrderShipped',
    ['tracking_number' => 'ABC123'],
    ['user_id' => auth()->id(), 'ip_address' => request()->ip()]
);

// Get events by type
$shippedOrders = $es->getEventsByType('OrderShipped', 10);

// Get latest version number
$latestVersion = $es->getLatestVersion('order-789');

// Get events from specific version
$newEvents = $es->getEvents('order-789', $fromVersion = 5);

4. Versioning

When to use? When you need to keep snapshots of your models to rollback or view history.

Simple Example - Document Versioning

use Dimita\BusinessOrchestration\BusinessOrchestration;

$version = BusinessOrchestration::version();

$document = Document::find(1);

// Create snapshot before modification
$version->snapshot($document);

// Modify document
$document->content = 'New content';
$document->save();

// Create another snapshot
$version->snapshot($document);

// Modify again
$document->content = 'Even newer content';
$document->save();

// Create third snapshot
$version->snapshot($document);

// View all versions
$versions = $version->getVersions($document);
// 3 versions available

// Restore to version 2
$version->restore($document, 2);

echo $document->content; // 'New content'

Use Case - Contract Audit Trail

$contract = Contract::find(1);

// Create snapshot at each important change
$contract->status = 'draft';
$contract->save();
$version->snapshot($contract);

$contract->status = 'submitted';
$contract->save();
$version->snapshot($contract);

$contract->status = 'approved';
$contract->amount = 50000;
$contract->save();
$version->snapshot($contract);

// View complete history
$versions = $version->getVersions($contract);

foreach ($versions as $v) {
    echo "Version {$v['version']}: Status = {$v['snapshot']['status']}\n";
}

Advanced Versioning Features

Version Diffing

Compare differences between two versions:

// Create two versions
$document->content = 'First version';
$document->save();
$version->snapshot($document);

$document->content = 'Second version';
$document->price = 100;
$document->save();
$version->snapshot($document);

// Get differences between versions 1 and 2
$diff = $version->diff($document, 1, 2);

/*
Result:
[
    'added' => ['price' => 100],
    'removed' => [],
    'changed' => [
        'content' => [
            'old' => 'First version',
            'new' => 'Second version'
        ]
    ]
]
*/
Field Exclusion

Exclude sensitive or unnecessary fields from versioning:

// Exclude timestamps and sensitive data
$version->excludeFields(['password', 'remember_token', 'last_login_at'])
    ->snapshot($user);

// Only critical fields are versioned
Include Hidden Fields

Include normally hidden fields in version snapshots:

$user->makeHidden(['password']); // Hidden by default

// Include hidden fields in version
$version->includeHiddenFields(['password', 'api_token'])
    ->snapshot($user);
Revert to Previous Version

Quick revert to N versions back:

// Revert 1 version back
$version->revert($document);

// Revert 3 versions back
$version->revert($document, 3);

// Latest version is now the restored one
Version Metadata

Store contextual information with versions:

$version->snapshot($document, [
    'user_id' => auth()->id(),
    'reason' => 'Legal compliance update',
    'ip_address' => request()->ip()
]);
Version Utilities
// Get latest version number
$latestVersion = $version->getLatestVersion($document); // e.g., 15

// Check if specific version exists
if ($version->hasVersion($document, 5)) {
    // Version 5 exists
}

// Get total version count
$count = $version->getVersionCount($document); // e.g., 15

// Get version by hash
$versionModel = $version->getVersionByHash($document, $hash);

// Delete all versions
$deletedCount = $version->purge($document);

5. Rule Engine

When to use? When you have business rules that change frequently and you want to manage them without modifying code.

Simple Example - Discount Rules

use Dimita\BusinessOrchestration\BusinessOrchestration;

$ruleEngine = BusinessOrchestration::rule();

// Create rule: If amount > 500, apply discount
$discountRule = $ruleEngine->createRule('BigOrderDiscount', [
    'type' => 'comparison',
    'left' => 'amount',
    'op' => '>',
    'right' => 500
], ['type' => 'discount', 'value' => 10]);

// Evaluate rule
$order = ['amount' => 750, 'customer_id' => 123];

if ($ruleEngine->evaluate($discountRule, $order)) {
    echo "Discount applicable!";
    // Apply discount
}

Rules with Custom Actions

// Create rule with callable action
$rule = $ruleEngine->createRule('VIPCustomerRule', [
    'type' => 'comparison',
    'left' => 'customer_tier',
    'op' => '==',
    'right' => 'VIP'
], ['type' => 'action', 'name' => 'apply_vip_benefits']);

// Execute dynamic action
$ruleObject = new stdClass();
$ruleObject->action = function($context) {
    // Send VIP email
    Mail::to($context['email'])->send(new VIPWelcome());

    // Apply discount
    return ['discount' => 20];
};

if ($ruleEngine->evaluate($rule, $customer)) {
    $result = $ruleEngine->executeAction($ruleObject, $customer);
}

Supported Operators

// Numeric comparisons
'>' // Greater than
'<' // Less than
'==' // Equal to

// Examples
$rule1 = $ruleEngine->createRule('AgeCheck', [
    'type' => 'comparison',
    'left' => 'age',
    'op' => '>',
    'right' => 18
], ['type' => 'allow']);

$rule2 = $ruleEngine->createRule('StockCheck', [
    'type' => 'comparison',
    'left' => 'stock',
    'op' => '<',
    'right' => 10
], ['type' => 'reorder']);

Advanced Rule Engine Features

Fluent Rule Builder

Create rules using a fluent, readable syntax:

// Fluent API for rule creation
$rule = $ruleEngine->rule('HighValueOrder')
    ->when('total', '>', 1000)
    ->and('customer_type', '==', 'premium')
    ->priority(10)
    ->then(function($context) {
        // Apply free shipping
        return ['free_shipping' => true];
    });

// Rule is automatically saved and can be evaluated
$context = ['total' => 1500, 'customer_type' => 'premium'];
$result = $ruleEngine->evaluate($rule, $context); // true
Extended Operator Support
// All supported operators
'==' '===' '!=' '!==' // Equality
'>' '>=' '<' '<=' // Comparison
'in' 'contains' // Array operations
'starts_with' 'ends_with' // String operations

// Examples
$rule = $ruleEngine->createRule('EmailCheck', [
    'type' => 'comparison',
    'left' => ['type' => 'variable', 'name' => 'email'],
    'op' => 'ends_with',
    'right' => ['type' => 'literal', 'value' => '@company.com']
], ['action' => 'approve']);

$rule = $ruleEngine->createRule('RoleCheck', [
    'type' => 'comparison',
    'left' => ['type' => 'variable', 'name' => 'role'],
    'op' => 'in',
    'right' => ['type' => 'literal', 'value' => ['admin', 'moderator']]
], ['action' => 'grant_access']);
Logical Operations

Combine multiple conditions with AND/OR/NOT:

// Complex rule with logical operations
$rule = $ruleEngine->createRule('ComplexDiscount', [
    'type' => 'logical',
    'operator' => 'AND',
    'left' => [
        'type' => 'comparison',
        'left' => ['type' => 'variable', 'name' => 'amount'],
        'op' => '>',
        'right' => ['type' => 'literal', 'value' => 500]
    ],
    'right' => [
        'type' => 'logical',
        'operator' => 'OR',
        'left' => [
            'type' => 'comparison',
            'left' => ['type' => 'variable', 'name' => 'is_member'],
            'op' => '==',
            'right' => ['type' => 'literal', 'value' => true]
        ],
        'right' => [
            'type' => 'comparison',
            'left' => ['type' => 'variable', 'name' => 'coupon_code'],
            'op' => '!=',
            'right' => ['type' => 'literal', 'value' => null]
        ]
    ]
], ['discount' => 15], 5);
Rule Macros

Define reusable rule patterns:

// Register a macro for common rule pattern
$ruleEngine->macro('premium_customer', function($ruleEngine) {
    return $ruleEngine->rule('PremiumCustomer')
        ->when('tier', '==', 'premium')
        ->and('active', '==', true);
});

// Use the macro
$premiumRule = $ruleEngine->executeMacro('premium_customer', [$ruleEngine])
    ->then(function($context) {
        return ['special_offer' => true];
    });
Custom Operators

Register your own operators:

// Register custom operator
$ruleEngine->registerOperator('divisible_by', function($left, $right, $context) {
    return $left % $right === 0;
});

// Use custom operator
$rule = $ruleEngine->createRule('BulkOrder', [
    'type' => 'comparison',
    'left' => ['type' => 'variable', 'name' => 'quantity'],
    'op' => 'divisible_by',
    'right' => ['type' => 'literal', 'value' => 12]
], ['bulk_discount' => 10]);
Rule Groups

Organize and evaluate rules in groups:

// Create multiple rules
$rule1 = $ruleEngine->rule('FreeShipping')
    ->when('total', '>', 100)
    ->then(fn($ctx) => ['free_shipping' => true]);

$rule2 = $ruleEngine->rule('TenPercentOff')
    ->when('total', '>', 500)
    ->then(fn($ctx) => ['discount' => 10]);

// Add to group
$ruleEngine->addToGroup('checkout_rules', $rule1->id);
$ruleEngine->addToGroup('checkout_rules', $rule2->id);

// Evaluate entire group (AND logic)
$passed = $ruleEngine->evaluateGroup('checkout_rules', $context, 'AND');

// Evaluate with OR logic
$passed = $ruleEngine->evaluateGroup('checkout_rules', $context, 'OR');
Priority-Based Evaluation

Rules with higher priority execute first:

// Create rules with priorities
$rule1 = $ruleEngine->rule('CriticalRule')
    ->when('status', '==', 'urgent')
    ->priority(100)
    ->then(fn($ctx) => ['priority' => 'high']);

$rule2 = $ruleEngine->rule('NormalRule')
    ->when('status', '==', 'normal')
    ->priority(10)
    ->then(fn($ctx) => ['priority' => 'normal']);

// Batch evaluate with priority ordering
$results = $ruleEngine->evaluateBatch([1, 2, 3], $context);
// Returns results sorted by priority descending
Built-in Functions

Use built-in functions in rule conditions:

// Function-based rules
$rule = $ruleEngine->createRule('EmptyCartCheck', [
    'type' => 'function',
    'name' => 'empty',
    'args' => [['type' => 'variable', 'name' => 'cart_items']]
], ['action' => 'show_empty_message']);

// Other built-in functions:
// - empty, isset, is_null
// - count (with comparison)
Get Matching Rules

Find all rules that pass for a context:

$context = ['amount' => 750, 'customer_type' => 'premium'];

// Get all matching rules
$matchingRules = $ruleEngine->getMatchingRules($context);

foreach ($matchingRules as $rule) {
    echo "Matched rule: {$rule->name}\n";
    $ruleEngine->executeAction($rule, $context);
}

6. Sync Engine

When to use? To synchronize data between multiple devices (mobile app, web, etc.) with offline support.

Simple Example - Mobile Sync

use Dimita\BusinessOrchestration\BusinessOrchestration;

$sync = BusinessOrchestration::sync();

// On server, log each change
$task = Task::find(1);

$sync->logChange($task, 'INSERT', [
    'title' => 'New task',
    'status' => 'pending'
]);

// Later, modification
$task->status = 'in_progress';
$task->save();

$sync->logChange($task, 'UPDATE', [
    'status' => 'in_progress'
]);

// Another modification
$task->status = 'completed';
$task->save();

$sync->logChange($task, 'UPDATE', [
    'status' => 'completed'
]);

Retrieving Changes (Mobile Client)

// Mobile client requests changes since last sync
$lastSyncVersion = 5; // Version from last sync

$deltas = $sync->getDeltas(
    'App\\Models\\Task',
    $taskId,
    $lastSyncVersion
);

// Client receives only changes after version 5
foreach ($deltas as $delta) {
    echo "Version {$delta['version']}: {$delta['operation']}\n";
    // Apply changes locally
    applyChange($delta);
}

Offline Scenario

// 1. Mobile app syncs
$clientVersion = 0;
$deltas = $sync->getDeltas('App\\Models\\Task', 1, $clientVersion);
// Receives all modifications

// 2. Client goes offline and makes local modifications
// Modifications stored locally

// 3. Client comes back online
// Send local modifications to server
foreach ($localChanges as $change) {
    $sync->logChange($model, $change['operation'], $change['fields']);
}

// 4. Get new changes from server
$newClientVersion = 15; // Version after upload
$newDeltas = $sync->getDeltas('App\\Models\\Task', 1, $newClientVersion);

Advanced Sync Engine Features

Conflict Resolution

Handle conflicts when data changes on both client and server:

// Synchronize with conflict detection
$sourceModel = Task::find(1);
$targetModel = Task::find(1); // Simulating different state

$result = $sync->sync($sourceModel, $targetModel);

/*
Result:
[
    'conflicts' => [
        'status' => [
            'source' => 'completed',
            'target' => 'in_progress'
        ]
    ],
    'synced_fields' => ['title', 'status', 'description']
]
*/
Conflict Strategies

Choose how conflicts are resolved:

// Latest wins (default)
$sync->setConflictStrategy('latest_wins')
    ->sync($source, $target);

// Source always wins
$sync->setConflictStrategy('source_wins')
    ->sync($source, $target);

// Target always wins
$sync->setConflictStrategy('target_wins')
    ->sync($source, $target);

// Merge values (for arrays and strings)
$sync->setConflictStrategy('merge')
    ->sync($source, $target);
Custom Conflict Handlers

Register custom handlers for specific fields:

// Register custom handler for 'tags' field
$sync->registerConflictHandler('tags', function($values, $source, $target) {
    // Merge tags from both sources uniquely
    $sourceTags = $values['source'];
    $targetTags = $values['target'];

    return array_unique(array_merge($targetTags, $sourceTags));
});

// Now when syncing, 'tags' conflicts use custom handler
$sync->sync($source, $target);
Selective Field Sync

Sync only specific fields:

// Sync only title and description
$result = $sync->sync($source, $target, ['title', 'description']);

// Other fields are ignored
Batch Synchronization

Sync multiple model pairs at once:

$pairs = [
    ['source' => $task1, 'target' => $task1Remote],
    ['source' => $task2, 'target' => $task2Remote],
    ['source' => $task3, 'target' => $task3Remote],
];

$results = $sync->batchSync($pairs, ['title', 'status']);

foreach ($results as $index => $result) {
    if (isset($result['error'])) {
        echo "Pair {$index} failed: {$result['error']}\n";
    } else {
        echo "Pair {$index} synced: {$result['synced_fields']}\n";
    }
}
Sync Checkpoints

Create named checkpoints for restore points:

// Create checkpoint before major changes
$sync->checkpoint($task, 'before_bulk_update');

// Make changes
$task->status = 'archived';
$task->save();

// Restore from checkpoint if needed
$sync->restoreCheckpoint($task, 'before_bulk_update');

// List all checkpoints
$checkpoints = $sync->getCheckpoints($task);
/*
[
    [
        'version' => 15,
        'name' => 'before_bulk_update',
        'created_at' => '2025-01-15 10:30:00'
    ],
    ...
]
*/
Sync Status Monitoring

Track synchronization activity:

$status = $sync->getSyncStatus($task);

/*
Result:
[
    'total_syncs' => 42,
    'last_sync' => '2025-01-15 14:30:00',
    'current_version' => 42,
    'operations' => [
        'synced' => 35,
        'checkpoint' => 7
    ]
]
*/
Model Comparison

Compare two models without syncing:

// Detect differences
$differences = $sync->compare($modelA, $modelB);

/*
Result:
[
    'title' => [
        'source' => 'Task A',
        'target' => 'Task B'
    ],
    'status' => [
        'source' => 'completed',
        'target' => 'pending'
    ]
]
*/

// Compare specific fields only
$differences = $sync->compare($modelA, $modelB, ['title', 'status']);
Apply Deltas

Apply a set of changes to a model:

$deltas = [
    [
        'operation' => 'update',
        'changed_fields' => ['status' => 'completed', 'completed_at' => now()]
    ],
    [
        'operation' => 'update',
        'changed_fields' => ['priority' => 'high']
    ]
];

$updatedModel = $sync->applyDeltas($task, $deltas);
Metadata Tracking

Track sync context and metadata:

$sync->logChange($task, 'synced', ['status'], [
    'device_id' => 'mobile-123',
    'user_id' => auth()->id(),
    'app_version' => '2.1.0'
]);
Cleanup Old Logs

Purge old synchronization logs to save space:

// Keep only last 100 logs per model type
$deletedCount = $sync->purgeOldLogs('App\\Models\\Task', 100);

echo "Deleted {$deletedCount} old sync logs";

7. Dependency Engine

When to use? To manage business dependencies and prevent deletions that would violate constraints.

Simple Example - Prevent Deletion

use Dimita\BusinessOrchestration\BusinessOrchestration;

$dep = BusinessOrchestration::dependency();

// Define that Category cannot be deleted if Products exist
$dep->addDependency(
    'App\\Models\\Product',
    'App\\Models\\Category',
    'prevent_delete'
);

// Before deleting a category
$categoryId = 5;

if (!$dep->checkDeletion('App\\Models\\Category', $categoryId)) {
    return response()->json([
        'error' => 'Cannot delete category with existing products'
    ], 422);
}

// Otherwise, delete
Category::destroy($categoryId);

Complex Dependency Graph

// Define dependency graph
$dep->addDependency('App\\Models\\OrderItem', 'App\\Models\\Order', 'cascade_delete');
$dep->addDependency('App\\Models\\Order', 'App\\Models\\Customer', 'prevent_delete');
$dep->addDependency('App\\Models\\Product', 'App\\Models\\Category', 'prevent_delete');
$dep->addDependency('App\\Models\\OrderItem', 'App\\Models\\Product', 'prevent_delete');

// Get all dependencies for a model
$dependencies = $dep->getDependencies('App\\Models\\Product');

// Before deletion, check all constraints
if (!$dep->checkDeletion('App\\Models\\Customer', $customerId)) {
    throw new \Exception('Customer has active orders');
}

Dependency Rule Types

// Prevent delete - Block deletion
$dep->addDependency('Child', 'Parent', 'prevent_delete');

// Cascade delete - Delete children
$dep->addDependency('Child', 'Parent', 'cascade_delete');

// Soft delete - Soft delete children
$dep->addDependency('Child', 'Parent', 'soft_delete');

// Custom rule - Custom business rule
$dep->addDependency('Child', 'Parent', 'custom_business_rule');

Real-World Use Cases

E-commerce - Complete Order Process

// 1. Saga for order processing
$saga = BusinessOrchestration::saga()->startSaga('OrderProcessing', [
    'validate_cart' => ValidateCartStep::class,
    'reserve_stock' => ReserveStockStep::class,
    'charge_payment' => ChargePaymentStep::class,
    'create_shipment' => CreateShipmentStep::class,
    'send_confirmation' => SendConfirmationStep::class,
], ['order_id' => $order->id]);

// 2. Workflow for order status
$workflow = BusinessOrchestration::workflow();
$workflow->defineTransition('pay', 'pending', 'paid');
$workflow->defineTransition('ship', 'paid', 'shipped');
$workflow->defineTransition('deliver', 'shipped', 'delivered');

$builder = $workflow->for($order);
$builder->apply('pay');

// 3. Event Sourcing for audit
$es = BusinessOrchestration::eventSourcing();
$es->storeEvent("order-{$order->id}", 'OrderCreated', $order->toArray());
$es->storeEvent("order-{$order->id}", 'PaymentReceived', $payment->toArray());
$es->storeEvent("order-{$order->id}", 'OrderShipped', $shipment->toArray());

// 4. Versioning for order history
$version = BusinessOrchestration::version();
$version->snapshot($order); // At each important step

// 5. Business rules for discounts
$rule = BusinessOrchestration::rule()->createRule('FirstOrderDiscount', [
    'type' => 'comparison',
    'left' => 'order_count',
    'op' => '==',
    'right' => 1
], ['discount' => 15]);

// 6. Sync for customer mobile app
$sync = BusinessOrchestration::sync();
$sync->logChange($order, 'UPDATE', ['status' => 'shipped']);

// 7. Dependencies to prevent invalid deletions
$dep = BusinessOrchestration::dependency();
$dep->addDependency('App\\Models\\OrderItem', 'App\\Models\\Order', 'cascade_delete');

SaaS - Subscription Management

// Subscription workflow
$workflow = BusinessOrchestration::workflow();
$workflow->defineTransition('activate', 'trial', 'active');
$workflow->defineTransition('cancel', 'active', 'cancelled');
$workflow->defineTransition('suspend', 'active', 'suspended');
$workflow->defineTransition('reactivate', 'suspended', 'active');

// Event sourcing for billing history
$es = BusinessOrchestration::eventSourcing();
$es->storeEvent("subscription-{$sub->id}", 'SubscriptionStarted', [...]);
$es->storeEvent("subscription-{$sub->id}", 'PaymentProcessed', [...]);
$es->storeEvent("subscription-{$sub->id}", 'UpgradedToPro', [...]);

// Rules for automatic upgrades
$upgradeRule = $ruleEngine->createRule('AutoUpgrade', [
    'type' => 'comparison',
    'left' => 'usage',
    'op' => '>',
    'right' => 80
], ['action' => 'suggest_upgrade']);

Architecture

Package Structure

src/
├── Core/                    # Main engines
│   ├── SagaEngine.php
│   ├── WorkflowEngine.php
│   ├── EventSourcingEngine.php
│   ├── VersionEngine.php
│   ├── RuleEngine.php
│   ├── SyncEngine.php
│   └── DependencyEngine.php
├── Models/                  # Eloquent models
│   ├── Saga.php
│   ├── SagaStep.php
│   ├── WorkflowInstance.php
│   ├── WorkflowTransition.php
│   ├── EventStore.php
│   ├── ModelVersion.php
│   ├── SyncLog.php
│   ├── Rule.php
│   └── Dependency.php
├── Drivers/                 # Multi-driver support
│   ├── DatabaseDriver.php
│   ├── RedisDriver.php
│   └── QueueDriver.php
└── BusinessOrchestration.php # Main facade

Design Principles

  • Human-friendly logic: Clear and intuitive API
  • Production-ready: Error handling, compensation, crash recovery
  • Persistent: Everything saved to database
  • Extensible: Multi-driver support (DB, Redis, Queue)
  • Testable: 122 tests, 100% coverage

Architecture Diagrams

1. Saga Pattern Flow

┌─────────────────────────────────────────────────────────────────┐
│                    SAGA ORCHESTRATION                            │
└─────────────────────────────────────────────────────────────────┘

Success Flow:
┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐
│ PENDING │ -> │ RUNNING │ -> │ RUNNING │ -> │COMPLETED│
│         │    │ Step 1  │    │ Step 2  │    │         │
└─────────┘    └─────────┘    └─────────┘    └─────────┘

Failure + Compensation Flow:
┌─────────┐    ┌─────────┐    ┌─────────┐    ┌──────────────┐
│ PENDING │ -> │ RUNNING │ -> │ FAILED  │ -> │ COMPENSATED  │
│         │    │ Step 1✓ │    │ Step 2✗ │    │ Rollback 1✓  │
└─────────┘    └─────────┘    └─────────┘    └──────────────┘

Database Schema:
sagas                           saga_steps
├── id                          ├── id
├── name                        ├── saga_id (FK)
├── status                      ├── step_name
├── payload (JSON)              ├── status
├── current_step                ├── executed_at
└── timestamps                  ├── compensated_at
                                ├── error
                                └── timestamps

2. Workflow State Machine

┌─────────────────────────────────────────────────────────────────┐
│                    WORKFLOW ENGINE                               │
└─────────────────────────────────────────────────────────────────┘

State Transition Graph:
                    submit
    ┌─────────┐ ─────────> ┌───────────┐
    │  draft  │            │ submitted │
    └─────────┘ <───────── └───────────┘
                   revise         │
                                  │ review
                                  v
                            ┌───────────┐
                     reject │ in_review │ approve
                    ┌───────┴───────────┴────────┐
                    │                             │
                    v                             v
              ┌──────────┐                  ┌──────────┐
              │ rejected │                  │ approved │
              └──────────┘                  └──────────┘

Database Schema:
workflow_instances              workflow_transitions
├── id                          ├── id
├── model_type                  ├── instance_id (FK)
├── model_id                    ├── from_state
├── state                       ├── to_state
└── timestamps                  ├── transition_name
                                ├── context (JSON)
                                └── timestamps

3. Event Sourcing Stream

┌─────────────────────────────────────────────────────────────────┐
│                    EVENT SOURCING                                │
└─────────────────────────────────────────────────────────────────┘

Event Stream (Append-Only):
Time ─────────────────────────────────────────────────────>

┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│OrderCreated  │─>│PaymentCharged│─>│ItemsShipped  │─>│OrderDelivered│
│ v1           │  │ v2           │  │ v3           │  │ v4           │
└──────────────┘  └──────────────┘  └──────────────┘  └──────────────┘

Aggregate Rebuild:
Initial State: {}
  + OrderCreated     -> {status: 'pending', total: 100}
  + PaymentCharged   -> {status: 'paid', total: 100, payment_id: 123}
  + ItemsShipped     -> {status: 'shipped', tracking: 'ABC123'}
  + OrderDelivered   -> {status: 'delivered', delivered_at: '2024-01-01'}

Database Schema:
event_store
├── id
├── aggregate_id
├── event_type
├── event_data (JSON)
├── version
├── metadata (JSON)
└── created_at

4. Version Control System

┌─────────────────────────────────────────────────────────────────┐
│                    VERSIONING ENGINE                             │
└─────────────────────────────────────────────────────────────────┘

Snapshot Timeline:
Model State: {name: "Doc1", status: "draft"}
     │
     v  snapshot()
Version 1: {name: "Doc1", status: "draft", hash: "abc123"}
     │
     │  model.update({status: "published"})
     v  snapshot()
Version 2: {name: "Doc1", status: "published", hash: "def456"}
     │
     │  model.update({name: "Doc1-Updated"})
     v  snapshot()
Version 3: {name: "Doc1-Updated", status: "published", hash: "ghi789"}
     │
     │  restore(version: 1)
     v
Restored: {name: "Doc1", status: "draft"}

Database Schema:
model_versions
├── id
├── model_type
├── model_id
├── version
├── snapshot_data (JSON)
├── hash
└── created_at

5. Rule Engine AST Evaluation

┌─────────────────────────────────────────────────────────────────┐
│                    RULE ENGINE                                   │
└─────────────────────────────────────────────────────────────────┘

Rule Definition (AST):
Business Rule: "If order total > 100 AND customer_type == 'VIP', apply 20% discount"

AST Structure:
{
  type: "logical",
  operator: "AND",
  left: {
    type: "comparison",
    left: "order_total",
    op: ">",
    right: 100
  },
  right: {
    type: "comparison",
    left: "customer_type",
    op: "==",
    right: "VIP"
  }
}

Evaluation Flow:
Context: {order_total: 150, customer_type: "VIP"}
  ├─> Evaluate left: 150 > 100 = true
  ├─> Evaluate right: "VIP" == "VIP" = true
  └─> AND(true, true) = true -> Execute action

Database Schema:
rules
├── id
├── name
├── condition_ast (JSON)
├── action (JSON)
└── timestamps

6. Sync Engine Delta Synchronization

┌─────────────────────────────────────────────────────────────────┐
│                    SYNC ENGINE                                   │
└─────────────────────────────────────────────────────────────────┘

Multi-Device Sync:
Server                          Client A                    Client B
  │                                │                           │
  │ v1: INSERT {name: "Item1"}     │                           │
  ├────────────────────────────────>│ Sync from v0             │
  │                                │ Receives: [v1]            │
  │                                │                           │
  │ v2: UPDATE {status: "active"}  │                           │
  ├────────────────────────────────>│ Sync from v1             │
  │                                │ Receives: [v2]            │
  │                                │                           │
  │                                │                           ├─> Sync from v0
  │                                │                           │   Receives: [v1, v2]
  │ v3: UPDATE {price: 99}         │                           │
  ├────────────────────────────────>│ Sync from v2             │
  │                                │ Receives: [v3]            │
  ├───────────────────────────────────────────────────────────>│ Sync from v2
  │                                │                           │ Receives: [v3]

Database Schema:
sync_log
├── id
├── model_type
├── model_id
├── operation (INSERT|UPDATE|DELETE)
├── version
├── changed_fields (JSON)
└── created_at

7. Dependency Graph System

┌─────────────────────────────────────────────────────────────────┐
│                    DEPENDENCY ENGINE                             │
└─────────────────────────────────────────────────────────────────┘

Dependency Graph:
┌──────────┐
│  User    │
└────┬─────┘
     │ has_many (dependency: prevent_delete)
     ├────────────┬────────────┬────────────┐
     v            v            v            v
┌─────────┐  ┌────────┐  ┌─────────┐  ┌─────────┐
│ Order   │  │Profile │  │Comments │  │Payments │
└─────────┘  └────────┘  └─────────┘  └─────────┘

Deletion Check Flow:
canDelete(User #123)?
  ├─> Check Orders: 5 orders exist -> BLOCKED
  ├─> Check Profile: 1 profile exists -> BLOCKED
  ├─> Check Comments: 12 comments exist -> BLOCKED
  └─> Check Payments: 3 payments exist -> BLOCKED

Result: Cannot delete User #123 (has dependencies)

Database Schema:
dependencies
├── id
├── source_model
├── target_model
├── dependency_type
├── created_at
└── updated_at

Overall System Architecture

┌─────────────────────────────────────────────────────────────────┐
│                     Application Layer                            │
│  (Controllers, Services, Commands)                               │
└──────────────────────┬───────────────────────────────────────────┘
                       │
                       v
┌─────────────────────────────────────────────────────────────────┐
│              BusinessOrchestration Facade                        │
│  ->saga() ->workflow() ->eventSourcing() ->version()             │
│  ->rule() ->sync() ->dependency()                                │
└──────────────────────┬───────────────────────────────────────────┘
                       │
       ┌───────────────┼───────────────┐
       v               v               v
┌────────────┐  ┌────────────┐  ┌────────────┐
│   Saga     │  │  Workflow  │  │   Event    │
│  Engine    │  │  Engine    │  │  Sourcing  │
└─────┬──────┘  └─────┬──────┘  └─────┬──────┘
      │               │               │
┌────────────┐  ┌────────────┐  ┌────────────┐
│  Version   │  │    Rule    │  │    Sync    │
│  Engine    │  │  Engine    │  │   Engine   │
└─────┬──────┘  └─────┬──────┘  └─────┬──────┘
      │               │               │
      └───────────────┼───────────────┘
                      v
┌─────────────────────────────────────────────────────────────────┐
│                    Driver Layer                                  │
│  DatabaseDriver │ RedisDriver │ QueueDriver                      │
└──────────────────────┬───────────────────────────────────────────┘
                       │
                       v
┌─────────────────────────────────────────────────────────────────┐
│                 Persistence Layer                                │
│  MySQL │ PostgreSQL │ SQLite │ Redis │ RabbitMQ                 │
└─────────────────────────────────────────────────────────────────┘

Testing

The package includes 122 tests covering all use cases:

# Run all tests
vendor/bin/phpunit

# With details
vendor/bin/phpunit --testdox

# Result
OK (122 tests, 252 assertions)

Test Coverage by Engine

  • SagaEngine: 16 tests (compensation, resume, states)
  • WorkflowEngine: 16 tests (transitions, guards, states)
  • RuleEngine: 18 tests (evaluation, AST, actions)
  • DependencyEngine: 17 tests (graphs, constraints)
  • EventSourcingEngine: 16 tests (events, rebuild, versioning)
  • VersionEngine: 19 tests (snapshots, restore, audit)
  • SyncEngine: 20 tests (deltas, offline, incremental)

Configuration

Publish the configuration file:

php artisan vendor:publish --provider="Dimita\\BusinessOrchestration\\BusinessOrchestrationServiceProvider"

Available in config/business-orchestration.php:

return [
    /*
    |--------------------------------------------------------------------------
    | Enabled Engines
    |--------------------------------------------------------------------------
    |
    | Configure which engines should be loaded and available in your application.
    | Set to false to disable an engine completely and improve performance.
    | By default, all engines are enabled.
    |
    */
    'engines' => [
        'saga' => env('ORCHESTRATION_SAGA_ENABLED', true),
        'workflow' => env('ORCHESTRATION_WORKFLOW_ENABLED', true),
        'sync' => env('ORCHESTRATION_SYNC_ENABLED', true),
        'version' => env('ORCHESTRATION_VERSION_ENABLED', true),
        'event_sourcing' => env('ORCHESTRATION_EVENT_SOURCING_ENABLED', true),
        'rule' => env('ORCHESTRATION_RULE_ENABLED', true),
        'dependency' => env('ORCHESTRATION_DEPENDENCY_ENABLED', true),
    ],

    /*
    |--------------------------------------------------------------------------
    | Storage Drivers
    |--------------------------------------------------------------------------
    |
    | Configure how orchestration data is stored and retrieved.
    | Supports: database, redis, queue
    |
    */
    'drivers' => [
        'default' => env('BUSINESS_ORCHESTRATION_DRIVER', 'database'),

        'database' => [
            'connection' => env('DB_CONNECTION', 'mysql'),
        ],

        'redis' => [
            'connection' => env('REDIS_CONNECTION', 'default'),
        ],

        'queue' => [
            'connection' => env('QUEUE_CONNECTION', 'sync'),
        ],
    ],
];

Configuration Examples

Scenario 1: E-commerce application (needs Saga, Workflow, Versioning)

'engines' => [
    'saga' => true,              // For order processing
    'workflow' => true,          // For order status transitions
    'sync' => false,             // No mobile sync needed
    'version' => true,           // For order audit trail
    'event_sourcing' => false,   // Not needed
    'rule' => true,              // For discount rules
    'dependency' => false,       // Not needed
],

Scenario 2: Mobile-first app with offline support

'engines' => [
    'saga' => false,
    'workflow' => false,
    'sync' => true,              // Critical for mobile sync
    'version' => true,           // Version tracking
    'event_sourcing' => true,    // Event history
    'rule' => false,
    'dependency' => false,
],

Scenario 3: Enterprise workflow system

'engines' => [
    'saga' => true,              // Multi-step processes
    'workflow' => true,          // State machines
    'sync' => false,
    'version' => true,           // Document versioning
    'event_sourcing' => true,    // Complete audit trail
    'rule' => true,              // Business rules
    'dependency' => true,        // Constraint management
],

Support

  • Documentation: This README
  • Issues: GitHub Issues
  • Tests: 100% coverage, 122 tests

License

MIT License - Free for commercial and open-source projects.

Changelog

v1.0.0 (2025)

  • ✅ Saga Pattern with automatic compensation
  • ✅ Workflow Engine with guards
  • ✅ Event Sourcing with rebuild
  • ✅ Versioning with snapshots
  • ✅ Rule Engine with AST
  • ✅ Sync Engine for multi-device
  • ✅ Dependency Engine for business constraints
  • ✅ 122 tests with 100% coverage
  • ✅ Production-ready with complete error handling

Made with ❤️ for the Laravel community

Based on production-tested patterns I've refined over years of building enterprise applications.