call method
Executes all registered saga steps in order.
Execution semantics:
- Iterates through all steps in registration order
- Executes each step and logs its status
- On success: registers its compensation for later rollback
- On failure: executes all registered compensations in reverse order, then rethrows the original error
Compensation order (LIFO):
If steps are S1, S2, S3 and S3 fails:
- Execute: S1 → S2 → S3 (fails)
- Compensate: C2 → C1 (reverse order)
Error handling:
- Each compensation is awaited individually
- If a compensation fails, the error is logged but doesn't prevent subsequent compensations from running
- After all compensation attempts, the original error is rethrown
Logging:
- Uses developer.log for structured logging
- Start:
▶️ <step_name> - Success:
✓ <step_name> - Failure:
✗ <step_name> — compensating... - Compensation errors:
Compensation failed: <error>
Returns: A Future that completes when all steps execute successfully, or rejects with the error from the first failing step (after compensation).
Throws: Re-throws the original error from any step after attempting to compensate all prior steps.
Implementation
Future<void> call() async {
for (final step in _steps) {
try {
developer.log('▶️ ${step.name}');
final result = await step.execute();
// Register compensation in reverse order
_compensations.insert(0, () => step.compensate(result));
developer.log('✓ ${step.name}');
} catch (e) {
developer.log('✗ ${step.name} — compensating...');
for (final comp in _compensations) {
await comp().catchError(
(Object? e) => developer.log('Compensation failed: $e'),
);
}
rethrow;
}
}
}