
# Kompensacja przepływu pracy

Kompensacja pozwala dołączyć procedurę wycofania do ukończonego kroku przepływu pracy. Queuety zapisuje migawkę stanu publicznego po zakończeniu tego kroku, a następnie uruchamia procedury kompensacyjne w odwrotnej kolejności, gdy przepływ pracy zostanie anulowany lub, opcjonalnie, gdy zakończy się niepowodzeniem.

## Metoda `compensate_with()`

Wywołaj `compensate_with()` bezpośrednio po kroku, który ma zostać cofnięty:

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

Jeśli przepływ pracy zostanie później anulowany, Queuety uruchamia:

1. `CancelSubscriptionCompensation`
2. `DeleteRemoteUserCompensation`

## Procedury obsługi kompensacji

Procedury obsługi kompensacji implementują `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'] );
    }
}
```

Procedura obsługi otrzymuje publiczny stan przepływu pracy w takiej postaci, w jakiej był bezpośrednio po ukończeniu kompensowanego kroku. Zastrzeżone klucze wewnętrzne nie są uwzględniane.

## Anulowanie a niepowodzenie

Po jawnym anulowaniu kompensacje ukończonych kroków są zawsze uruchamiane przed opcjonalną procedurą obsługi `on_cancel()`.

W razie niepowodzenia kompensacje są uruchamiane tylko po wyrażeniu zgody:

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

Bez `compensate_on_failure()` nieudany przepływ pracy zachowuje historię ukończonych kroków i można ponowić jego wykonanie od kroku, który zakończył się niepowodzeniem.

Z `compensate_on_failure()` kompensacje ukończonych kroków są uruchamiane po oznaczeniu przepływu pracy jako nieudanego. Następnie `retry_workflow()` zgłasza wyjątek, ponieważ przepływ pracy został już poddany kompensacji.

## Obsługiwane typy kroków

Kompensację można dołączyć do:

- sekwencyjnych kroków `then()`
- grup `parallel()`
- kroków `for_each()`
- kroków `run_workflow()`
- kroków opóźnienia `delay()`
- kroków sygnału `wait_for_signal()`
- kroków zależności między przepływami pracy `wait_for_workflow()`

W przypadku `run_workflow()` i `for_each()` migawka kompensacji jest zapisywana po pełnym zakończeniu zagnieżdżonej lub rozgałęzionej pracy.

## `on_cancel()` a kompensacja

Użyj `compensate_with()`, aby cofnąć skutki uboczne ukończonych kroków w odwrotnej kolejności.

Użyj `on_cancel()` jako pojedynczego mechanizmu porządkowego na poziomie przepływu pracy, który ma uruchamiać się przy każdym jawnym anulowaniu przepływu.
