Quickstart: Workflow
Get started with the Dapr Workflow building block
Note
The workflow building block is currently in alpha.
Let’s take a look at the Dapr Workflow building block. In this Quickstart, you’ll create a simple console application to demonstrate Dapr’s workflow programming model and the workflow management APIs.
The order-processor
console app starts and manages the lifecycle of the OrderProcessingWorkflow
workflow that stores and retrieves data in a state store. The workflow consists of four workflow activities, or tasks:
NotifyActivity
: Utilizes a logger to print out messages throughout the workflowReserveInventoryActivity
: Checks the state store to ensure that there is enough inventory for the purchaseProcessPaymentActivity
: Processes and authorizes the paymentUpdateInventoryActivity
: Removes the requested items from the state store and updates the store with the new remaining inventory value
In this guide, you’ll:
- Run the
order-processor
application. - Start the workflow and watch the workflow activites/tasks execute.
- Review the workflow logic and the workflow activities and how they’re represented in the code.
Currently, you can experience the Dapr Workflow using the .NET SDK.
Step 1: Pre-requisites
For this example, you will need:
Step 2: Set up the environment
Clone the sample provided in the Quickstarts repo.
git clone https://github.com/dapr/quickstarts.git
In a new terminal window, navigate to the order-processor
directory:
cd workflows/csharp/sdk/order-processor
Step 3: Run the order processor app
In the terminal, start the order processor app alongside a Dapr sidecar:
dapr run --app-id order-processor dotnet run
This starts the order-processor
app with unique workflow ID and runs the workflow activities.
Expected output:
== APP == Starting workflow 6d2abcc9 purchasing 10 Cars
== APP == info: Microsoft.DurableTask.Client.Grpc.GrpcDurableTaskClient[40]
== APP == Scheduling new OrderProcessingWorkflow orchestration with instance ID '6d2abcc9' and 47 bytes of input data.
== APP == info: WorkflowConsoleApp.Activities.NotifyActivity[0]
== APP == Received order 6d2abcc9 for 10 Cars at $15000
== APP == info: WorkflowConsoleApp.Activities.ReserveInventoryActivity[0]
== APP == Reserving inventory for order 6d2abcc9 of 10 Cars
== APP == info: WorkflowConsoleApp.Activities.ReserveInventoryActivity[0]
== APP == There are: 100, Cars available for purchase
== APP == Your workflow has started. Here is the status of the workflow: Dapr.Workflow.WorkflowState
== APP == info: WorkflowConsoleApp.Activities.ProcessPaymentActivity[0]
== APP == Processing payment: 6d2abcc9 for 10 Cars at $15000
== APP == info: WorkflowConsoleApp.Activities.ProcessPaymentActivity[0]
== APP == Payment for request ID '6d2abcc9' processed successfully
== APP == info: WorkflowConsoleApp.Activities.UpdateInventoryActivity[0]
== APP == Checking Inventory for: Order# 6d2abcc9 for 10 Cars
== APP == info: WorkflowConsoleApp.Activities.UpdateInventoryActivity[0]
== APP == There are now: 90 Cars left in stock
== APP == info: WorkflowConsoleApp.Activities.NotifyActivity[0]
== APP == Order 6d2abcc9 has completed!
== APP == Workflow Status: Completed
What happened?
When you ran dapr run --app-id order-processor dotnet run
:
- A unique order ID for the workflow is generated (in the above example,
6d2abcc9
) and the workflow is scheduled. - The
NotifyActivity
workflow activity sends a notification saying an order for 10 cars has been received. - The
ReserveInventoryActivity
workflow activity checks the inventory data, determines if you can supply the ordered item, and responds with the number of cars in stock. - Your workflow starts and notifies you of its status.
- The
ProcessPaymentActivity
workflow activity begins processing payment for order6d2abcc9
and confirms if successful. - The
UpdateInventoryActivity
workflow activity updates the inventory with the current available cars after the order has been processed. - The
NotifyActivity
workflow activity sends a notification saying that order6d2abcc9
has completed. - The workflow terminates as completed.
order-processor/Program.cs
In the application’s program file:
- The unique workflow order ID is generated
- The workflow is scheduled
- The workflow status is retrieved
- The workflow and the workflow activities it invokes are registered
using Dapr.Client;
using Dapr.Workflow;
//...
{
services.AddDaprWorkflow(options =>
{
// Note that it's also possible to register a lambda function as the workflow
// or activity implementation instead of a class.
options.RegisterWorkflow<OrderProcessingWorkflow>();
// These are the activities that get invoked by the workflow(s).
options.RegisterActivity<NotifyActivity>();
options.RegisterActivity<ReserveInventoryActivity>();
options.RegisterActivity<ProcessPaymentActivity>();
options.RegisterActivity<UpdateInventoryActivity>();
});
};
//...
// Generate a unique ID for the workflow
string orderId = Guid.NewGuid().ToString()[..8];
string itemToPurchase = "Cars";
int ammountToPurchase = 10;
//...
// Start the workflow
Console.WriteLine("Starting workflow {0} purchasing {1} {2}", orderId, ammountToPurchase, itemToPurchase);
await workflowClient.ScheduleNewWorkflowAsync(
name: nameof(OrderProcessingWorkflow),
instanceId: orderId,
input: orderInfo);
//...
WorkflowState state = await workflowClient.GetWorkflowStateAsync(
instanceId: orderId,
getInputsAndOutputs: true);
Console.WriteLine("Your workflow has started. Here is the status of the workflow: {0}", state);
//...
Console.WriteLine("Workflow Status: {0}", state.RuntimeStatus);
order-processor/Workflows/OrderProcessingWorkflow.cs
In OrderProcessingWorkflow.cs
, the workflow is defined as a class with all of its associated tasks (determined by workflow activities).
using Dapr.Workflow;
//...
class OrderProcessingWorkflow : Workflow<OrderPayload, OrderResult>
{
public override async Task<OrderResult> RunAsync(WorkflowContext context, OrderPayload order)
{
string orderId = context.InstanceId;
// Notify the user that an order has come through
await context.CallActivityAsync(
nameof(NotifyActivity),
new Notification($"Received order {orderId} for {order.Quantity} {order.Name} at ${order.TotalCost}"));
string requestId = context.InstanceId;
// Determine if there is enough of the item available for purchase by checking the inventory
InventoryResult result = await context.CallActivityAsync<InventoryResult>(
nameof(ReserveInventoryActivity),
new InventoryRequest(RequestId: orderId, order.Name, order.Quantity));
// If there is insufficient inventory, fail and let the user know
if (!result.Success)
{
// End the workflow here since we don't have sufficient inventory
await context.CallActivityAsync(
nameof(NotifyActivity),
new Notification($"Insufficient inventory for {order.Name}"));
return new OrderResult(Processed: false);
}
// There is enough inventory available so the user can purchase the item(s). Process their payment
await context.CallActivityAsync(
nameof(ProcessPaymentActivity),
new PaymentRequest(RequestId: orderId, order.Name, order.Quantity, order.TotalCost));
try
{
// There is enough inventory available so the user can purchase the item(s). Process their payment
await context.CallActivityAsync(
nameof(UpdateInventoryActivity),
new PaymentRequest(RequestId: orderId, order.Name, order.Quantity, order.TotalCost));
}
catch (TaskFailedException)
{
// Let them know their payment was processed
await context.CallActivityAsync(
nameof(NotifyActivity),
new Notification($"Order {orderId} Failed! You are now getting a refund"));
return new OrderResult(Processed: false);
}
// Let them know their payment was processed
await context.CallActivityAsync(
nameof(NotifyActivity),
new Notification($"Order {orderId} has completed!"));
// End the workflow with a success result
return new OrderResult(Processed: true);
}
}
order-processor/Activities
directory
The Activities
directory holds the four workflow activities used by the workflow, defined in the following files:
NotifyActivity.cs
ReserveInventoryActivity.cs
ProcessPaymentActivity.cs
UpdateInventoryActivity.cs
Watch the demo
Watch this video to walk through the Dapr Workflow .NET demo:
Tell us what you think!
We’re continuously working to improve our Quickstart examples and value your feedback. Did you find this Quickstart helpful? Do you have suggestions for improvement?
Join the discussion in our discord channel.
Next steps
- Set up Dapr Workflow with any programming language using HTTP instead of an SDK
- Walk through a more in-depth .NET SDK example workflow
- Learn more about Workflow as a Dapr building block
Last modified February 9, 2023: dapr run command installs dependencies for dotnet (6d8ab826)