
# Компенсація робочого процесу

Компенсація дає змогу додати обробник відкату до завершеного кроку робочого процесу. Queuety зберігає знімок публічного стану після завершення цього кроку, а потім запускає обробники компенсації у зворотному порядку, коли робочий процес скасовано або, за бажанням, коли в ньому сталася помилка.

## Метод `compensate_with()`

Викличте `compensate_with()` одразу після кроку, який потрібно скасувати:

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

Якщо згодом робочий процес буде скасовано, виконається Queuety:

1. `CancelSubscriptionCompensation`
2. `DeleteRemoteUserCompensation`

## Обробники компенсації

Обробники компенсації реалізують `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'] );
    }
}
```

Обробник отримує публічний стан робочого процесу таким, яким він був одразу після завершення компенсованого кроку. Зарезервовані внутрішні ключі не включаються.

## Скасування та помилка

У разі явного скасування компенсації завершених кроків завжди виконуються перед необов’язковим обробником `on_cancel()`.

У разі помилки компенсації запускаються лише за вашою згодою:

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

Без `compensate_on_failure()` робочий процес із помилкою зберігає історію завершених кроків без змін, і його можна повторити з кроку, що завершився помилкою.

З `compensate_on_failure()` компенсації завершених кроків запускаються після того, як робочому процесу призначено статус помилки. Після цього `retry_workflow()` викидає виняток, оскільки компенсацію робочого процесу вже виконано.

## Підтримувані типи кроків

Компенсацію можна додати до таких кроків:

- послідовних кроків `then()`
- груп `parallel()`
- кроків `for_each()`
- кроків `run_workflow()`
- кроків затримки `delay()`
- кроків сигналу `wait_for_signal()`
- кроків залежності робочого процесу `wait_for_workflow()`

Для `run_workflow()` і `for_each()` знімок для компенсації записується після повного завершення вкладеної або розгалуженої роботи.

## `on_cancel()` і компенсація

Використовуйте `compensate_with()`, щоб скасувати побічні ефекти завершених кроків у зворотному порядку.

Використовуйте `on_cancel()` як єдиний хук очищення на рівні робочого процесу, який запускається щоразу під час його явного скасування.
