-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathContextLocalPayloadExample.cs
More file actions
190 lines (161 loc) · 5.45 KB
/
ContextLocalPayloadExample.cs
File metadata and controls
190 lines (161 loc) · 5.45 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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
using Zooper.Fox;
namespace Zooper.Bee.Example;
public class ContextLocalPayloadExample
{
// Request models
public record OrderRequest(
int OrderId,
string CustomerName,
decimal OrderAmount,
bool NeedsShipping);
// Success model
public record OrderConfirmation(
int OrderId,
string CustomerName,
decimal TotalAmount,
string? ShippingTrackingNumber);
// Error model
public record OrderError(string Code, string Message);
// Main railway payload model
public record OrderPayload(
int OrderId,
string CustomerName,
decimal OrderAmount,
bool NeedsShipping,
decimal TotalAmount = 0,
string? ShippingTrackingNumber = null);
// Local payload for shipping context
public record ShippingPayload(
string CustomerAddress,
decimal ShippingCost,
decimal PackagingCost,
decimal InsuranceCost,
string? TrackingNumber = null);
public static async Task RunExample()
{
Console.WriteLine("\n=== Railway With Context Local Payload Example ===\n");
// Create sample requests
var standardOrder = new OrderRequest(2001, "Alice Johnson", 75.00m, false);
var shippingOrder = new OrderRequest(2002, "Bob Smith", 120.00m, true);
// Build the order processing railway
var workflow = CreateOrderRailway();
// Process the standard order (no shipping)
Console.WriteLine("Processing standard order (no shipping):");
await ProcessOrder(workflow, standardOrder);
Console.WriteLine();
// Process the order with shipping
Console.WriteLine("Processing order with shipping:");
await ProcessOrder(workflow, shippingOrder);
}
private static async Task ProcessOrder(
Railway<OrderRequest, OrderConfirmation, OrderError> workflow,
OrderRequest request)
{
var result = await workflow.Execute(request);
if (result.IsRight)
{
var confirmation = result.Right;
Console.WriteLine($"Order {confirmation.OrderId} processed successfully");
Console.WriteLine($"Customer: {confirmation.CustomerName}");
Console.WriteLine($"Total Amount: ${confirmation.TotalAmount}");
if (confirmation.ShippingTrackingNumber != null)
{
Console.WriteLine($"Shipping Tracking Number: {confirmation.ShippingTrackingNumber}");
}
else
{
Console.WriteLine("No shipping required");
}
}
else
{
var error = result.Left;
Console.WriteLine($"Order processing failed: [{error.Code}] {error.Message}");
}
}
private static Railway<OrderRequest, OrderConfirmation, OrderError> CreateOrderRailway()
{
return new RailwayBuilder<OrderRequest, OrderPayload, OrderConfirmation, OrderError>(
// Create initial payload from request
request => new OrderPayload(
request.OrderId,
request.CustomerName,
request.OrderAmount,
request.NeedsShipping),
// Create result from final payload
payload => new OrderConfirmation(
payload.OrderId,
payload.CustomerName,
payload.TotalAmount,
payload.ShippingTrackingNumber)
)
// Validate order amount
.Validate(request =>
{
if (request.OrderAmount <= 0)
{
return Option<OrderError>.Some(
new OrderError("INVALID_AMOUNT", "Order amount must be greater than zero"));
}
return Option<OrderError>.None();
})
// Process the basic order details
.Do(payload =>
{
Console.WriteLine($"Processing order {payload.OrderId} for {payload.CustomerName}...");
// Set the initial total amount to the order amount
return Either<OrderError, OrderPayload>.FromRight(
payload with { TotalAmount = payload.OrderAmount });
})
// Use specialized context for shipping-specific processing
.WithContext(
// Only enter this context if shipping is needed
payload => payload.NeedsShipping,
// Create the local shipping payload
payload => new ShippingPayload(
CustomerAddress: "123 Example St, City, Country", // In real world, this would come from a database
ShippingCost: 12.50m,
PackagingCost: 2.75m,
InsuranceCost: 5.00m),
// Configure the context with shipping-specific activities
context => context
// First shipping activity - calculate shipping costs
.Do((mainPayload, shippingPayload) =>
{
Console.WriteLine("Calculating shipping costs...");
// Calculate the total shipping cost
decimal totalShippingCost =
shippingPayload.ShippingCost +
shippingPayload.PackagingCost +
shippingPayload.InsuranceCost;
// Update both payloads
var updatedMainPayload = mainPayload with
{
TotalAmount = mainPayload.OrderAmount + totalShippingCost
};
return Either<OrderError, (OrderPayload, ShippingPayload)>.FromRight(
(updatedMainPayload, shippingPayload));
})
// Second shipping activity - generate tracking number
.Do((mainPayload, shippingPayload) =>
{
Console.WriteLine("Generating shipping tracking number...");
// Generate a fake tracking number
string trackingNumber = $"TRACK-{Guid.NewGuid().ToString()[..8]}";
// Update both payloads with the tracking number
var updatedShippingPayload = shippingPayload with { TrackingNumber = trackingNumber };
var updatedMainPayload = mainPayload with { ShippingTrackingNumber = trackingNumber };
return Either<OrderError, (OrderPayload, ShippingPayload)>.FromRight(
(updatedMainPayload, updatedShippingPayload));
})
)
// Finalize the order
.Do(payload =>
{
Console.WriteLine($"Finalizing order {payload.OrderId}...");
// In a real system, we would persist the order to a database here
return Either<OrderError, OrderPayload>.FromRight(payload);
})
.Build();
}
}