-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathOrderFlow.cs
More file actions
80 lines (68 loc) · 2.64 KB
/
Copy pathOrderFlow.cs
File metadata and controls
80 lines (68 loc) · 2.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
using Cleipnir.Flows.Sample.MicrosoftOpen.Clients;
using Cleipnir.Flows.Sample.MicrosoftOpen.Flows.MessageDriven;
using Cleipnir.ResilientFunctions.Domain;
using Polly;
using Polly.Retry;
namespace Cleipnir.Flows.Sample.MicrosoftOpen.Flows.Rpc;
public class OrderFlow(
IPaymentProviderClient paymentProviderClient,
IEmailClient emailClient,
ILogisticsClient logisticsClient
) : Flow<Order>
{
public override async Task Run(Order order)
{
var transactionId = await Capture(Guid.NewGuid);
await Capture(() => paymentProviderClient.Reserve(transactionId, order.CustomerId, order.TotalPrice));
var trackAndTrace = await Effect.Capture(
() => logisticsClient.ShipProducts(order.CustomerId, order.ProductIds),
ResiliencyLevel.AtMostOnce
);
await Capture(() => paymentProviderClient.Capture(transactionId));
await Capture(() => emailClient.SendOrderConfirmation(order.CustomerId, trackAndTrace, order.ProductIds));
}
#region Polly
private ResiliencePipeline Pipeline { get; } = new ResiliencePipelineBuilder()
.AddRetry(
new RetryStrategyOptions
{
ShouldHandle = new PredicateBuilder().Handle<Exception>(),
BackoffType = DelayBackoffType.Exponential,
UseJitter = true, // Adds a random factor to the delay
MaxRetryAttempts = 10,
Delay = TimeSpan.FromSeconds(3),
}
).Build();
#endregion
#region CleanUp
private async Task CleanUp(FailedAt failedAt, Guid transactionId, TrackAndTrace? trackAndTrace)
{
switch (failedAt)
{
case FailedAt.FundsReserved:
break;
case FailedAt.ProductsShipped:
await paymentProviderClient.CancelReservation(transactionId);
break;
case FailedAt.FundsCaptured:
await paymentProviderClient.Reverse(transactionId);
await logisticsClient.CancelShipment(trackAndTrace!);
break;
case FailedAt.OrderConfirmationEmailSent:
//we accept this failure without cleaning up
break;
default:
throw new ArgumentOutOfRangeException(nameof(failedAt), failedAt, null);
}
throw new OrderProcessingException($"Order processing failed at: '{failedAt}'");
}
private record StepAndCleanUp(Func<Task> Work, Func<Task> CleanUp);
private enum FailedAt
{
FundsReserved,
ProductsShipped,
FundsCaptured,
OrderConfirmationEmailSent,
}
#endregion
}