Use file based namespaces, make classes internal sealed

Signed-off-by: Marc Duiker <marcduiker@users.noreply.github.com>
This commit is contained in:
Marc Duiker 2025-04-01 12:30:07 +01:00
parent 9c04f72365
commit 9562e155d6
No known key found for this signature in database
GPG Key ID: 5E708BC9F3163E81
35 changed files with 169 additions and 175 deletions

View File

@ -1,48 +1,47 @@
using System;
using Dapr.Workflow;
namespace WorkflowApp
namespace WorkflowApp;
internal sealed class NonDeterministicWorkflow : Workflow<string, string>
{
public class NonDeterministicWorkflow : Workflow<string, string>
public override async Task<string> RunAsync(WorkflowContext context, string orderItem)
{
public override async Task<string> RunAsync(WorkflowContext context, string orderItem)
{
// Do not use non-deterministic operations in a workflow!
// These operations will create a new value every time the
// workflow is replayed.
var orderId = Guid.NewGuid().ToString();
var orderDate = DateTime.UtcNow;
// Do not use non-deterministic operations in a workflow!
// These operations will create a new value every time the
// workflow is replayed.
var orderId = Guid.NewGuid().ToString();
var orderDate = DateTime.UtcNow;
var idResult = await context.CallActivityAsync<string>(
"SubmitId",
orderId);
var idResult = await context.CallActivityAsync<string>(
"SubmitId",
orderId);
await context.CallActivityAsync<string>(
"SubmitDate",
orderDate);
await context.CallActivityAsync<string>(
"SubmitDate",
orderDate);
return idResult;
}
return idResult;
}
}
public class DeterministicWorkflow : Workflow<string, string>
internal sealed class DeterministicWorkflow : Workflow<string, string>
{
public override async Task<string> RunAsync(WorkflowContext context, string orderItem)
{
public override async Task<string> RunAsync(WorkflowContext context, string orderItem)
{
// Do use these deterministic methods and properties on the WorkflowContext instead.
// These operations create the same value when the workflow is replayed.
var orderId = context.NewGuid().ToString();
var orderDate = context.CurrentUtcDateTime;
var idResult = await context.CallActivityAsync<string>(
"SubmitId",
orderId);
// Do use these deterministic methods and properties on the WorkflowContext instead.
// These operations create the same value when the workflow is replayed.
var orderId = context.NewGuid().ToString();
var orderDate = context.CurrentUtcDateTime;
await context.CallActivityAsync<string>(
"SubmitDate",
orderDate);
var idResult = await context.CallActivityAsync<string>(
"SubmitId",
orderId);
return idResult;
}
await context.CallActivityAsync<string>(
"SubmitDate",
orderDate);
return idResult;
}
}
}

View File

@ -1,19 +1,18 @@
using Dapr.Client;
using Dapr.Workflow;
namespace WorkflowApp
{
public class IdempotentActivity : WorkflowActivity<string, string>
{
public override async Task<InventoryResult> RunAsync(WorkflowActivityContext context, OrderItem orderItem)
{
// Beware of non-idempotent operations in an activity.
// Dapr Workflow guarantees at-least-once execution of activities, so activities might be executed more than once.
// For instance, can you insert a record to a database twice without side effects?
// var insertSql = $"INSERT INTO Orders (Id, Description, UnitPrice, Quantity) VALUES ('{orderItem.Id}', '{orderItem.Description}', {orderItem.UnitPrice}, {orderItem.Quantity})";
// It's best to perform a check if an record already exists before inserting it.
}
}
namespace WorkflowApp;
record OrderItem(string Id, string Description, double UnitPrice, int Quantity);
}
internal sealed class IdempotentActivity : WorkflowActivity<string, string>
{
public override async Task<InventoryResult> RunAsync(WorkflowActivityContext context, OrderItem orderItem)
{
// Beware of non-idempotent operations in an activity.
// Dapr Workflow guarantees at-least-once execution of activities, so activities might be executed more than once.
// For instance, can you insert a record to a database twice without side effects?
// var insertSql = $"INSERT INTO Orders (Id, Description, UnitPrice, Quantity) VALUES ('{orderItem.Id}', '{orderItem.Description}', {orderItem.UnitPrice}, {orderItem.Quantity})";
// It's best to perform a check if an record already exists before inserting it.
}
}
internal sealed record OrderItem(string Id, string Description, double UnitPrice, int Quantity);

View File

@ -1,44 +1,43 @@
using Dapr.Workflow;
namespace WorkflowApp
namespace WorkflowApp;
internal sealed class LargePayloadSizeWorkflow : Workflow<string, LargeDocument>
{
public class LargePayloadSizeWorkflow : Workflow<string, LargeDocument>
public override async Task<LargeDocument> RunAsync(WorkflowContext context, string id)
{
public override async Task<LargeDocument> RunAsync(WorkflowContext context, string id)
{
// Do not pass large payloads between activities.
// They are stored in the Dapr state store twice, one as output argument
// for GetDocument, and once as input argument for UpdateDocument.
var document = await context.CallActivityAsync<LargeDocument>(
"GetDocument",
id);
// Do not pass large payloads between activities.
// They are stored in the Dapr state store twice, one as output argument
// for GetDocument, and once as input argument for UpdateDocument.
var document = await context.CallActivityAsync<LargeDocument>(
"GetDocument",
id);
var updatedDocument = await context.CallActivityAsync<LargeDocument>(
"UpdateDocument",
document);
var updatedDocument = await context.CallActivityAsync<LargeDocument>(
"UpdateDocument",
document);
// More activities to process the updated document...
// More activities to process the updated document...
return updatedDocument;
}
return updatedDocument;
}
}
public class SmallPayloadSizeWorkflow : Workflow<string, string>
public class SmallPayloadSizeWorkflow : Workflow<string, string>
{
public override async Task<string> RunAsync(WorkflowContext context, string id)
{
public override async Task<string> RunAsync(WorkflowContext context, string id)
{
// Do pass small payloads between activities, preferably IDs only, or objects that are quick to (de)serialize in large volumes.
// Combine multiple actions, such as document retrieval and update, into a single activity.
var documentId = await context.CallActivityAsync<string>(
"GetAndUpdateDocument",
id);
// Do pass small payloads between activities, preferably IDs only, or objects that are quick to (de)serialize in large volumes.
// Combine multiple actions, such as document retrieval and update, into a single activity.
var documentId = await context.CallActivityAsync<string>(
"GetAndUpdateDocument",
id);
// More activities to process the updated document...
// More activities to process the updated document...
return documentId;
}
return documentId;
}
}
public record LargeDocument(string Id, object Data);
}
internal sealed record LargeDocument(string Id, object Data);

View File

@ -1,49 +1,48 @@
using Dapr.Workflow;
namespace WorkflowApp
namespace WorkflowApp;
/// <summary>
/// This is the initial version of the workflow.
/// Note that the input argument for both activities is the orderItem (string).
/// </summary>
internal sealed class VersioningWorkflow1 : Workflow<string, int>
{
/// <summary>
/// This is the initial version of the workflow.
/// Note that the input argument for both activities is the orderItem (string).
/// </summary>
public class VersioningWorkflow1 : Workflow<string, int>
public override async Task<int> RunAsync(WorkflowContext context, string orderItem)
{
public override async Task<int> RunAsync(WorkflowContext context, string orderItem)
{
int resultA = await context.CallActivityAsync<int>(
"ActivityA",
orderItem);
int resultA = await context.CallActivityAsync<int>(
"ActivityA",
orderItem);
int resultB = await context.CallActivityAsync<int>(
"ActivityB",
orderItem);
int resultB = await context.CallActivityAsync<int>(
"ActivityB",
orderItem);
return resultA + resultB;
}
return resultA + resultB;
}
}
/// <summary>
/// This is the updated version of the workflow.
/// The input for ActivityB has changed from orderItem (string) to resultA (int).
/// If there are in-flight workflow instances that were started with the previous version
/// of this workflow, these will fail when the new version of the workflow is deployed
/// and the workflow name remains the same, since the runtime parameters do not match with the persisted state.
/// It is recommended to version workflows by creating a new workflow class with a new name:
/// {workflowname}1 -> {workflowname}2
/// </summary>
public class VersioningWorkflow2 : Workflow<string, int>
/// <summary>
/// This is the updated version of the workflow.
/// The input for ActivityB has changed from orderItem (string) to resultA (int).
/// If there are in-flight workflow instances that were started with the previous version
/// of this workflow, these will fail when the new version of the workflow is deployed
/// and the workflow name remains the same, since the runtime parameters do not match with the persisted state.
/// It is recommended to version workflows by creating a new workflow class with a new name:
/// {workflowname}1 -> {workflowname}2
/// </summary>
internal sealed class VersioningWorkflow2 : Workflow<string, int>
{
public override async Task<int> RunAsync(WorkflowContext context, string orderItem)
{
public override async Task<int> RunAsync(WorkflowContext context, string orderItem)
{
int resultA = await context.CallActivityAsync<int>(
"ActivityA",
orderItem);
int resultA = await context.CallActivityAsync<int>(
"ActivityA",
orderItem);
int resultB = await context.CallActivityAsync<int>(
"ActivityB",
resultA);
int resultB = await context.CallActivityAsync<int>(
"ActivityB",
resultA);
return resultA + resultB;
}
return resultA + resultB;
}
}

View File

@ -2,7 +2,7 @@ using Dapr.Workflow;
namespace ChildWorkflows.Activities;
public class Activity1 : WorkflowActivity<string, string>
internal sealed class Activity1 : WorkflowActivity<string, string>
{
public override Task<string> RunAsync(WorkflowActivityContext context, string input)
{

View File

@ -2,7 +2,7 @@ using Dapr.Workflow;
namespace ChildWorkflows.Activities;
public class Activity2 : WorkflowActivity<string, string>
internal sealed class Activity2 : WorkflowActivity<string, string>
{
public override Task<string> RunAsync(WorkflowActivityContext context, string input)
{

View File

@ -2,23 +2,21 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Dapr.Workflow;
using ChildWorkflows.Activities;
using Dapr.Workflow;
namespace ChildWorkflows
namespace ChildWorkflows;
internal sealed class ChildWorkflow : Workflow<string, string>
{
public class ChildWorkflow : Workflow<string, string>
public override async Task<string> RunAsync(WorkflowContext context, string input)
{
public override async Task<string> RunAsync(WorkflowContext context, string input)
{
var result1 = await context.CallActivityAsync<string>(
nameof(Activity1),
input);
var childWorkflowResult = await context.CallActivityAsync<string>(
nameof(Activity2),
result1);
var result1 = await context.CallActivityAsync<string>(
nameof(Activity1),
input);
var childWorkflowResult = await context.CallActivityAsync<string>(
nameof(Activity2),
result1);
return childWorkflowResult;
}
return childWorkflowResult;
}
}

View File

@ -7,7 +7,7 @@ using ChildWorkflows.Activities;
namespace ChildWorkflows;
public class ParentWorkflow : Workflow<string[], string[]>
internal sealed class ParentWorkflow : Workflow<string[], string[]>
{
public override async Task<string[]> RunAsync(WorkflowContext context, string[] input)
{

View File

@ -36,8 +36,8 @@ app.MapPost("/registerShipment", async (
app.Run();
public record Order(string Id, OrderItem OrderItem, CustomerInfo CustomerInfo);
public record OrderItem(string ProductId, string ProductName, int Quantity, decimal TotalPrice);
public record CustomerInfo(string Id, string Country);
public record ShipmentRegistrationStatus(string OrderId, bool IsSuccess, string Message = "");
public record ShippingDestinationResult(bool IsSuccess, string Message = "");
internal sealed record Order(string Id, OrderItem OrderItem, CustomerInfo CustomerInfo);
internal sealed record OrderItem(string ProductId, string ProductName, int Quantity, decimal TotalPrice);
internal sealed record CustomerInfo(string Id, string Country);
internal sealed record ShipmentRegistrationStatus(string OrderId, bool IsSuccess, string Message = "");
internal sealed record ShippingDestinationResult(bool IsSuccess, string Message = "");

View File

@ -3,7 +3,7 @@ using Dapr.Workflow;
namespace WorkflowApp.Activities;
public class CheckInventory : WorkflowActivity<OrderItem, ActivityResult>
internal sealed class CheckInventory : WorkflowActivity<OrderItem, ActivityResult>
{
DaprClient _daprClient;
@ -30,4 +30,4 @@ public class CheckInventory : WorkflowActivity<OrderItem, ActivityResult>
}
}
public record ActivityResult(bool IsSuccess, string Message = "");
internal sealed record ActivityResult(bool IsSuccess, string Message = "");

View File

@ -3,7 +3,7 @@ using Dapr.Workflow;
namespace WorkflowApp.Activities;
public class CheckShippingDestination : WorkflowActivity<Order, ActivityResult>
internal sealed class CheckShippingDestination : WorkflowActivity<Order, ActivityResult>
{
private readonly HttpClient _httpClient;
@ -28,4 +28,4 @@ public class CheckShippingDestination : WorkflowActivity<Order, ActivityResult>
}
}
public record ShippingDestinationResult(bool IsSuccess);
internal sealed record ShippingDestinationResult(bool IsSuccess);

View File

@ -2,7 +2,7 @@ using Dapr.Workflow;
namespace WorkflowApp.Activities;
public class ProcessPayment : WorkflowActivity<Order, PaymentResult>
internal sealed class ProcessPayment : WorkflowActivity<Order, PaymentResult>
{
public override Task<PaymentResult> RunAsync(WorkflowActivityContext context, Order order)
{
@ -11,4 +11,4 @@ public class ProcessPayment : WorkflowActivity<Order, PaymentResult>
}
}
public record PaymentResult(bool IsSuccess);
internal sealed record PaymentResult(bool IsSuccess);

View File

@ -3,7 +3,7 @@ using Dapr.Workflow;
namespace WorkflowApp.Activities;
public class RegisterShipment : WorkflowActivity<Order, RegisterShipmentResult>
internal sealed class RegisterShipment : WorkflowActivity<Order, RegisterShipmentResult>
{
private readonly DaprClient _daprClient;
@ -25,4 +25,4 @@ public class RegisterShipment : WorkflowActivity<Order, RegisterShipmentResult>
}
}
public record RegisterShipmentResult(bool IsSucces);
internal sealed record RegisterShipmentResult(bool IsSucces);

View File

@ -2,7 +2,7 @@ using Dapr.Workflow;
namespace WorkflowApp.Activities;
public class ReimburseCustomer : WorkflowActivity<Order, ReimburseCustomerResult>
internal sealed class ReimburseCustomer : WorkflowActivity<Order, ReimburseCustomerResult>
{
public override Task<ReimburseCustomerResult> RunAsync(WorkflowActivityContext context, Order order)
{
@ -11,4 +11,4 @@ public class ReimburseCustomer : WorkflowActivity<Order, ReimburseCustomerResult
}
}
public record ReimburseCustomerResult(bool IsSuccess);
internal sealed record ReimburseCustomerResult(bool IsSuccess);

View File

@ -3,7 +3,7 @@ using Dapr.Workflow;
namespace WorkflowApp.Activities;
public class UpdateInventory : WorkflowActivity<OrderItem, UpdateInventoryResult>
internal sealed class UpdateInventory : WorkflowActivity<OrderItem, UpdateInventoryResult>
{
DaprClient _daprClient;
@ -43,4 +43,4 @@ public class UpdateInventory : WorkflowActivity<OrderItem, UpdateInventoryResult
}
}
public record UpdateInventoryResult(bool IsSuccess, string Message = "");
internal sealed record UpdateInventoryResult(bool IsSuccess, string Message = "");

View File

@ -3,7 +3,7 @@ using WorkflowApp.Activities;
namespace WorkflowApp;
public class OrderWorkflow : Workflow<Order, OrderStatus>
internal sealed class OrderWorkflow : Workflow<Order, OrderStatus>
{
public override async Task<OrderStatus> RunAsync(WorkflowContext context, Order order)
{
@ -62,4 +62,4 @@ public class OrderWorkflow : Workflow<Order, OrderStatus>
}
}
public record OrderStatus(bool IsSuccess, string Message);
internal sealed record OrderStatus(bool IsSuccess, string Message);

View File

@ -80,8 +80,8 @@ app.MapGet("/inventory/{productId}", async (
app.Run();
public record ProductInventory(string ProductId, int Quantity);
public record Order(string Id, OrderItem OrderItem, CustomerInfo CustomerInfo);
public record OrderItem(string ProductId, string ProductName, int Quantity, decimal TotalPrice);
public record CustomerInfo(string Id, string Country);
public record ShipmentRegistrationStatus(string OrderId, bool IsSuccess, string Message = "");
internal sealed record ProductInventory(string ProductId, int Quantity);
internal sealed record Order(string Id, OrderItem OrderItem, CustomerInfo CustomerInfo);
internal sealed record OrderItem(string ProductId, string ProductName, int Quantity, decimal TotalPrice);
internal sealed record CustomerInfo(string Id, string Country);
internal sealed record ShipmentRegistrationStatus(string OrderId, bool IsSuccess, string Message = "");

View File

@ -3,7 +3,7 @@ using ExternalEvents;
namespace ExternalEvents.Activities;
public class ProcessOrder : WorkflowActivity<Order, bool>
internal sealed class ProcessOrder : WorkflowActivity<Order, bool>
{
public override Task<bool> RunAsync(WorkflowActivityContext context, Order order)
{

View File

@ -3,7 +3,7 @@ using ExternalEvents;
namespace ExternalEvents.Activities;
public class RequestApproval : WorkflowActivity<Order, bool>
internal sealed class RequestApproval : WorkflowActivity<Order, bool>
{
public override Task<bool> RunAsync(WorkflowActivityContext context, Order order)
{

View File

@ -2,7 +2,7 @@ using Dapr.Workflow;
namespace ExternalEvents.Activities;
public class SendNotification : WorkflowActivity<string, bool>
internal sealed class SendNotification : WorkflowActivity<string, bool>
{
public override Task<bool> RunAsync(WorkflowActivityContext context, string message)
{

View File

@ -3,7 +3,7 @@ using ExternalEvents.Activities;
namespace ExternalEvents;
public class ExternalEventsWorkflow : Workflow<Order, string>
internal sealed class ExternalEventsWorkflow : Workflow<Order, string>
{
public override async Task<string> RunAsync(WorkflowContext context, Order order)
{
@ -47,4 +47,4 @@ public class ExternalEventsWorkflow : Workflow<Order, string>
}
}
public record ApprovalStatus(string OrderId, bool IsApproved);
internal sealed record ApprovalStatus(string OrderId, bool IsApproved);

View File

@ -26,4 +26,4 @@ app.MapPost("/start", async (Order order) => {
app.Run();
public record Order(string Id, string Description, int Quantity, double TotalPrice);
internal sealed record Order(string Id, string Description, int Quantity, double TotalPrice);

View File

@ -2,7 +2,7 @@ using Dapr.Workflow;
namespace FanOutFanIn.Activities;
public class GetWordLength : WorkflowActivity<string, WordLength>
internal sealed class GetWordLength : WorkflowActivity<string, WordLength>
{
public override Task<WordLength> RunAsync(WorkflowActivityContext context, string input)
{

View File

@ -7,7 +7,7 @@ using FanOutFanIn.Activities;
namespace FanOutFanIn;
public class FanOutFanInWorkflow : Workflow<string[], string>
internal sealed class FanOutFanInWorkflow : Workflow<string[], string>
{
public override async Task<string> RunAsync(WorkflowContext context, string[] input)
{

View File

@ -8,7 +8,7 @@ namespace Basic.Activities;
/// Activity code typically performs a one specific task, like calling an API to store retrieve data.
/// You can use other Dapr APIs inside an activity.
/// </summary>
public class Activity1 : WorkflowActivity<string, string>
internal sealed class Activity1 : WorkflowActivity<string, string>
{
/// <summary>
/// The RunAsync method is an abstract method in the abstract WorkflowActivity class that needs to be implemented.

View File

@ -2,7 +2,7 @@ using Dapr.Workflow;
namespace Basic.Activities;
public class Activity2 : WorkflowActivity<string, string>
internal sealed class Activity2 : WorkflowActivity<string, string>
{
public override Task<string> RunAsync(WorkflowActivityContext context, string input)
{

View File

@ -13,7 +13,7 @@ namespace Basic;
/// Workflows orchestrate activities and other (child)workflows, and include business logic (if/else or switch statements).
/// Workflow code must be be deterministic. Any non-deterministic behavior should be written inside activities.
/// </summary>
public class BasicWorkflow : Workflow<string, string>
internal sealed class BasicWorkflow : Workflow<string, string>
{
/// <summary>
/// The RunAsync method is an abstract method in the abstract Workflow class that needs to be implemented.

View File

@ -2,7 +2,7 @@ using Dapr.Workflow;
namespace Monitor.Activities;
public class CheckStatus : WorkflowActivity<int, Status>
internal sealed class CheckStatus : WorkflowActivity<int, Status>
{
public override Task<Status> RunAsync(WorkflowActivityContext context, int input)
{
@ -13,4 +13,4 @@ public class CheckStatus : WorkflowActivity<int, Status>
}
}
public record Status(bool IsReady);
internal sealed record Status(bool IsReady);

View File

@ -3,7 +3,7 @@ using Monitor.Activities;
namespace Monitor;
public class MonitorWorkflow : Workflow<int, string>
internal sealed class MonitorWorkflow : Workflow<int, string>
{
public override async Task<string> RunAsync(WorkflowContext context, int counter)
{

View File

@ -2,7 +2,7 @@ using Dapr.Workflow;
namespace TaskChaining.Activities;
public class Activity1 : WorkflowActivity<string, string>
internal sealed class Activity1 : WorkflowActivity<string, string>
{
public override Task<string> RunAsync(WorkflowActivityContext context, string input)
{

View File

@ -2,7 +2,7 @@ using Dapr.Workflow;
namespace TaskChaining.Activities;
public class Activity2 : WorkflowActivity<string, string>
internal sealed class Activity2 : WorkflowActivity<string, string>
{
public override Task<string> RunAsync(WorkflowActivityContext context, string input)
{

View File

@ -2,7 +2,7 @@ using Dapr.Workflow;
namespace TaskChaining.Activities;
public class Activity3 : WorkflowActivity<string, string>
internal sealed class Activity3 : WorkflowActivity<string, string>
{
public override Task<string> RunAsync(WorkflowActivityContext context, string input)
{

View File

@ -7,7 +7,7 @@ using TaskChaining.Activities;
namespace TaskChaining;
public class ChainingWorkflow : Workflow<string, string>
internal sealed class ChainingWorkflow : Workflow<string, string>
{
public override async Task<string> RunAsync(WorkflowContext context, string input)
{

View File

@ -2,7 +2,7 @@ using Dapr.Workflow;
namespace WorkflowManagement.Activities;
public class SendNotification : WorkflowActivity<int, bool>
internal sealed class SendNotification : WorkflowActivity<int, bool>
{
public override Task<bool> RunAsync(WorkflowActivityContext context, int counter)
{
@ -12,4 +12,4 @@ public class SendNotification : WorkflowActivity<int, bool>
}
}
public record Notification(string UserId, string Message);
internal sealed record Notification(string UserId, string Message);

View File

@ -3,7 +3,7 @@ using WorkflowManagement.Activities;
namespace WorkflowManagement;
public class NeverEndingWorkflow : Workflow<int, bool>
internal sealed class NeverEndingWorkflow : Workflow<int, bool>
{
public override async Task<bool> RunAsync(WorkflowContext context, int counter)
{