To build a routing slip activity with reliable compensation, implement the IActivity<TArguments, TLog> interface. The activity should validate arguments before performing side effects in the Execute method and provide a deterministic Compensate method to reverse those side effects using data stored in the compensation log (TLog).
Key Guidelines:
- Use
record types for arguments and compensation logs. - Validate all arguments at the start of
Execute before any side effects occur. - Ensure the compensation log contains all necessary data to reverse the action.
- Model irreversible side effects explicitly rather than attempting to compensate them.
public record DownloadImageArguments(string ImageUri, string WorkPath);
public record DownloadImageLog(string ImageSavePath);
public class DownloadImageActivity : IActivity<DownloadImageArguments, DownloadImageLog>
{
public async Task<ExecutionResult> Execute(ExecuteContext<DownloadImageArguments> context)
{
// 1. Validate arguments before side effects
if (!Uri.TryCreate(context.Arguments.ImageUri, UriKind.Absolute, out _))
throw new ArgumentException("ImageUri must be an absolute URI", nameof(context.Arguments.ImageUri));
if (string.IsNullOrWhiteSpace(context.Arguments.WorkPath))
throw new ArgumentException("WorkPath is required", nameof(context.Arguments.WorkPath));
// 2. Perform side effect
Directory.CreateDirectory(context.Arguments.WorkPath);
var filePath = Path.Combine(context.Arguments.WorkPath, context.TrackingNumber.ToString());
await File.WriteAllTextAsync(filePath, "downloaded", context.CancellationToken);
// 3. Return completion with log data needed for compensation
return context.Completed(new DownloadImageLog(filePath));
}
public Task<CompensationResult> Compensate(CompensateContext<DownloadImageLog> context)
{
// 4. Use the log to reverse the side effect
File.Delete(context.Log.ImageSavePath);
return Task.FromResult(context.Compensated());
}
}