Documentation

Overview content is shown below. Use the menu to open other pages.

Mutations — Change Tracking & Delivery

This page explains how IEC detects entity changes, constructs mutation payloads, assigns priorities, enqueues jobs, and executes Drivers.

Actors & Classes

  • `App\Observers\EntityObserver`: Observes Eloquent events (created/updated/deleted), computes diffs, and creates `Mutation` + `MutationJob` records.
  • `App\Traits\TracksEavChanges`: Augments models with EAV-aware change detection (attributes in `product_attribute_values`).
  • `App\Models\Mutation`: Stores the entity change snapshot including `payload`, `previous`, and `changed_fields`.
  • `App\Models\MutationJob`: Represents a unit of work per Integration (status, priority, attempts, last_error).
  • `App\Jobs\ProcessMutationJob`: Queue job that resolves the Integration’s Driver and calls `handle(MutationData)`.
  • `App\Services\QueueResolver`: Maps numeric priority to queue names per `config/queue-map.php`.
  • `App\Services\IntegrationDiscovery` + `App\Services\DriverDiscovery`: Finds enabled Integrations and resolves Drivers.

From Entity Change to Mutation

1. A model is created/updated/deleted (e.g., `App\Models\Product`). The model is `#[ObservedBy(EntityObserver::class)]`.

2. The observer’s `fanOut()`:

- Gathers direct attribute changes with `$entity->getChanges()` for updates.

- If the entity `use`s `TracksEavChanges`, merges EAV attribute codes that changed.

- Builds a complete `payload` using the model’s `toIntegrationPayload()` method; also captures `previous`.

- Creates a `Mutation` row with: `entity_type`, `entity_uuid`, `operation`, `payload`, `previous`, and `changed_fields`.

3. For each enabled `Integration` that matches the entity type:

- Computes the effective priority via `Integration::getPriority($changed_fields)` using `priority_map` (with `*` default).

- Resolves queue name via `QueueResolver::resolve($priority)` using `config/queue-map.php`.

- Creates a `MutationJob` with status `pending` and integration reference; dispatches immediately to `integrations-immediate` or defers to scheduler otherwise.

Queuing & Retries

  • Jobs are enqueued on Redis-backed queues. Horizon supervises workers and exposes metrics.
  • `ProcessMutationJob::tries()` and `backoff()` read from the `Integration` record (`retry_attempts`, `retry_delay_seconds`).
  • On handle failure, the job updates `last_error`, logs to `stack`, and is retried according to Horizon strategy.

Priority Queues

  • IEC assigns a numeric priority per Integration based on the mutation `changed_fields` and the Integration’s `priority_map`.
  • Queue mapping (see `config/queue-map.php`):
  • - `integrations-immediate` (<= 50)

    - `integrations-normal` (<= 200)

    - `integrations-low` (<= 250)

    - Fallback → `integrations-low`

ProcessMutationJob

1. Loads the `MutationJob` with `integration` and `mutation` relations.

2. Resolves the integration driver via `Integration::getIntegrationDriver()` → `DriverDiscovery` (container tag `integration`).

3. Constructs `IEC\IntegrationContracts\Data\MutationData` DTO from the mutation.

4. Calls `$driver->handle($dto)`.

5. On success: marks `status=success` and clears `last_error`.

6. On exception: records `last_error` and rethrows so Horizon can process retries/failures.

Payload Shape

Example `Product::toIntegrationPayload()` includes:

  • `identifier`, `enabled`, `family`, `completeness`
  • `values`: flat list of `{attribute_code, channel, locale, value, unit}` from EAV
  • `categories`: list of `{code, labels, parent}`
  • `updated_at`: ISO string

Downstream mapping happens in Drivers (e.g., Magento 2), which may transform IEC payloads to target schemas.

External References

  • Laravel Eloquent Observers: https://laravel.com/docs/12.x/eloquent#observers
  • Laravel Queues: https://laravel.com/docs/12.x/queues
  • Laravel Horizon: https://laravel.com/docs/12.x/horizon

Docs Index

Current file: docs/iec/09-mutations.md