
# Compensation de workflow

La compensation permet d’associer un gestionnaire d’annulation à une étape de workflow terminée. Queuety enregistre un instantané de l’état public à la fin de cette étape, puis exécute les gestionnaires de compensation dans l’ordre inverse lorsque le workflow est annulé ou, éventuellement, lorsqu’il échoue.

## La méthode `compensate_with()`

Appelez `compensate_with()` immédiatement après l’étape qu’il doit annuler :

```php
use Queuety\Queuety;

Queuety::workflow( 'provision_account' )
    ->then( CreateRemoteUserStep::class )
    ->compensate_with( DeleteRemoteUserCompensation::class )
    ->then( CreateSubscriptionStep::class )
    ->compensate_with( CancelSubscriptionCompensation::class )
    ->then( SendWelcomeEmailStep::class )
    ->dispatch( [ 'user_id' => 42 ] );
```

Si le workflow est annulé ultérieurement, Queuety s’exécute :

1. `CancelSubscriptionCompensation`
2. `DeleteRemoteUserCompensation`

## Gestionnaires de compensation

Les gestionnaires de compensation implémentent `Contracts\Compensation` :

```php
use Queuety\Contracts\Compensation;

final class DeleteRemoteUserCompensation implements Compensation {
    public function handle( array $state ): void {
        remote_api_delete_user( $state['remote_user_id'] );
    }
}
```

Le gestionnaire reçoit l’état public du workflow tel qu’il était immédiatement après l’achèvement de l’étape compensée. Les clés internes réservées ne sont pas incluses.

## Annulation ou échec

En cas d’annulation explicite, les compensations des étapes terminées s’exécutent toujours avant le gestionnaire facultatif `on_cancel()`.

En cas d’échec, les compensations ne s’exécutent que si vous les activez :

```php
Queuety::workflow( 'provision_account' )
    ->then( CreateRemoteUserStep::class )
    ->compensate_with( DeleteRemoteUserCompensation::class )
    ->then( CreateSubscriptionStep::class )
    ->compensate_with( CancelSubscriptionCompensation::class )
    ->then( ChargeCardStep::class )
    ->compensate_on_failure()
    ->dispatch( $payload );
```

Sans `compensate_on_failure()`, un workflow en échec conserve l’historique de ses étapes terminées et peut être relancé depuis l’étape en échec.

Avec `compensate_on_failure()`, les compensations des étapes terminées s’exécutent après le passage du workflow à l’état d’échec. Ensuite, `retry_workflow()` lève une exception, car le workflow a déjà été compensé.

## Types d’étapes pris en charge

La compensation peut être associée aux éléments suivants :

- étapes `then()` séquentielles
- groupes `parallel()`
- étapes `for_each()`
- étapes `run_workflow()`
- étapes `delay()` de délai
- étapes `wait_for_signal()` de signal
- étapes `wait_for_workflow()` de dépendance de workflow

Pour `run_workflow()` et `for_each()`, l’instantané de compensation est enregistré une fois le travail imbriqué ou ramifié entièrement terminé.

## `on_cancel()` ou compensation

Utilisez `compensate_with()` pour annuler les effets secondaires des étapes terminées dans l’ordre inverse.

Utilisez `on_cancel()` pour un seul mécanisme de nettoyage au niveau du workflow, exécuté à chaque annulation explicite.
