PHP code example of dimita / laravel-business-orchestration-engine

1. Go to this page and download the library: Download dimita/laravel-business-orchestration-engine library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

dimita / laravel-business-orchestration-engine example snippets


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

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

// 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

'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)
],

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

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']);
    }
}

// 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'];

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

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

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'

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

// Transition only possible if amount < 1000

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

// 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'],
    ]
]);

// 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());
});

$builder = $workflow->for($document);

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

// Returns: ['approve', 'reject', 'request_changes']

// 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']

// Override guards and force a state change (use carefully)
$builder->forceTransition('cancelled', 'Manual cancellation by admin');

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'
// )

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

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

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

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' => '[email protected]']);

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

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

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

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'

$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";
}

// 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'
        ]
    ]
]
*/

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

// Only critical fields are versioned

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

// Include hidden fields in version
$version->

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

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

// Latest version is now the restored one

$version->snapshot($document, [
    'user_id' => auth()->id(),
    'reason' => 'Legal compliance update',
    'ip_address' => request()->ip()
]);

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

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
}

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

// 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']);

// 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

// 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']);

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

// 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];
    });

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

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

// 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

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

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

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

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

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

// 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']
]
*/

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

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

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

// Other fields are ignored

$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";
    }
}

// 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'
    ],
    ...
]
*/

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

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

// 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']);

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

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

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

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

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

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

// 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');
}

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

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

// 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']);

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'),
        ],
    ],
];

'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
],

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

'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
],
bash
php artisan vendor:publish --provider="Dimita\\BusinessOrchestration\\BusinessOrchestrationServiceProvider"
php artisan migrate

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

┌─────────────────────────────────────────────────────────────────┐
│                    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
bash
php artisan vendor:publish --provider="Dimita\\BusinessOrchestration\\BusinessOrchestrationServiceProvider"