
# Compensação do workflow

A compensação permite associar um handler de reversão a uma etapa concluída do workflow. Queuety armazena um snapshot do estado público quando a etapa termina e, em seguida, executa os handlers de compensação em ordem inversa quando o workflow é cancelado ou, opcionalmente, quando falha.

## O método `compensate_with()`

Chame `compensate_with()` imediatamente após a etapa que ele deve desfazer:

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

Se o workflow for cancelado posteriormente, Queuety será executado:

1. `CancelSubscriptionCompensation`
2. `DeleteRemoteUserCompensation`

## Handlers de compensação

Os handlers de compensação implementam `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'] );
    }
}
```

O handler recebe o estado público do workflow tal como estava imediatamente após a conclusão da etapa compensada. As chaves internas reservadas não são incluídas.

## Cancelamento vs. falha

Em caso de cancelamento explícito, as compensações das etapas concluídas são sempre executadas antes do handler opcional `on_cancel()`.

Em caso de falha, as compensações só são executadas se você optar por isso:

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

Sem `compensate_on_failure()`, um workflow com falha mantém intacto o histórico das etapas concluídas e pode ser repetido a partir da etapa com falha.

Com `compensate_on_failure()`, as compensações das etapas concluídas são executadas depois que o workflow é marcado como com falha. Depois disso, `retry_workflow()` lança uma exceção porque o workflow já foi compensado.

## Tipos de etapa compatíveis

É possível associar uma compensação a:

- etapas sequenciais `then()`
- grupos `parallel()`
- etapas `for_each()`
- etapas `run_workflow()`
- etapas de atraso `delay()`
- etapas de sinal `wait_for_signal()`
- etapas de dependência de workflow `wait_for_workflow()`

Para `run_workflow()` e `for_each()`, o snapshot da compensação é registrado depois que o trabalho aninhado ou ramificado é totalmente concluído.

## `on_cancel()` vs. compensação

Use `compensate_with()` para desfazer efeitos colaterais de etapas concluídas em ordem inversa.

Use `on_cancel()` para um único hook de limpeza no nível do workflow que deve ser executado sempre que o workflow for cancelado explicitamente.
