diff --git a/daprdocs/content/en/developing-applications/building-blocks/workflow/howto-author-workflow.md b/daprdocs/content/en/developing-applications/building-blocks/workflow/howto-author-workflow.md index f8d74a07f..de51aa661 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/workflow/howto-author-workflow.md +++ b/daprdocs/content/en/developing-applications/building-blocks/workflow/howto-author-workflow.md @@ -9,7 +9,7 @@ description: "Learn how to develop and author workflows" This article provides a high-level overview of how to author workflows that are executed by the Dapr Workflow engine. {{% alert title="Note" color="primary" %}} - If you haven't already, [try out the workflow quickstart](todo) for a quick walk-through on how to use workflows. + If you haven't already, [try out the workflow quickstart]({{< ref workflow-quickstart.md >}}) for a quick walk-through on how to use workflows. {{% /alert %}} diff --git a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-overview.md b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-overview.md index 77ac1b422..08207d2df 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-overview.md +++ b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-overview.md @@ -93,8 +93,8 @@ Want to put workflows to the test? Walk through the following quickstart and tut | Quickstart/tutorial | Description | | ------------------- | ----------- | -| [Workflow quickstart](link) | Description of the quickstart. | -| [Workflow tutorial](link) | Description of the tutorial. | +| [Workflow quickstart]({{< ref workflow-quickstart.md >}}) | Run a .NET workflow application with four workflow activities to see Dapr Workflow in action | +| [Workflow .NET SDK example](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow) | Learn how to create a Dapr Workflow and invoke it using ASP.NET Core web APIs. | ### Start using workflows directly in your app diff --git a/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md index 19cb8ba13..857708722 100644 --- a/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md @@ -6,70 +6,29 @@ weight: 77 description: Get started with the Dapr Workflow building block --- -Let's take a look at the Dapr [Workflow building block]({{< ref pubsub >}}). In this Quickstart, you will write workflow activities, write and register a workflow, and manage a workflow to demonstrate how Dapr enables a workflow pattern. +{{% alert title="Note" color="primary" %}} +Dapr Workflow is currently in alpha. +{{% /alert %}} - +Let's take a look at the Dapr [Workflow building block]({{< ref workflow >}}). In this Quickstart, you'll create a simple console application to demonstrate Dapr's workflow programming model and the workflow management APIs. -Select your preferred language-specific Dapr SDK before proceeding with the Quickstart. +The `order-processor` console app starts and manages the lifecycle of the `OrderProcessingWorkflow` workflow that stores and retrieves data in a Dapr state store. The workflow consists of four workflow activities, or tasks: +- `NotifyActivity` +- `ReserveInventoryActivity` +- `ProcessPaymentActivity` +- `UpdateInventoryActivity` -{{< tabs "Python" "JavaScript" ".NET" "Java" "Go" >}} - -{{% codetab %}} +In this guide, you'll: -### Step 1: Pre-requisites +- Run the order processor application. +- Start the workflow and watch the workflow activites/tasks execute. +- Unpack the workflow logic and the workflow activities and how they're represented in the code. -For this example, you will need: + -- [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- [Python 3.7+ installed](https://www.python.org/downloads/). - -- [Docker Desktop](https://www.docker.com/products/docker-desktop) - +Currently, you can experience Dapr Workflows using the .NET SDK. -### Step 2: Set up the environment - -Clone the [sample provided in the Quickstarts repo](todo). - -```bash -git clone https://github.com/dapr/quickstarts.git -``` - -### Step 3: - -### Step 4: - -### Step 5: - -{{% /codetab %}} - - -{{% codetab %}} - -### Step 1: Pre-requisites - -For this example, you will need: - -- [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- [Latest Node.js installed](https://nodejs.org/download/). - -- [Docker Desktop](https://www.docker.com/products/docker-desktop) - - -### Step 2: Set up the environment - -Clone the [sample provided in the Quickstarts repo](todo). - -```bash -git clone https://github.com/dapr/quickstarts.git -``` - -### Step 3: - -### Step 4: - -### Step 5: - -{{% /codetab %}} +{{< tabs ".NET" >}} {{% codetab %}} @@ -86,81 +45,219 @@ For this example, you will need: ### Step 2: Set up the environment -Clone the [sample provided in the Quickstarts repo](todo). +Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/workflows). ```bash git clone https://github.com/dapr/quickstarts.git ``` -### Step 3: - -### Step 4: - -### Step 5: - -{{% /codetab %}} - - -{{% codetab %}} - -### Step 1: Pre-requisites - -For this example, you will need: - -- [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- Java JDK 11 (or greater): - - [Oracle JDK](https://www.oracle.com/java/technologies/downloads), or - - OpenJDK -- [Apache Maven](https://maven.apache.org/install.html), version 3.x. - -- [Docker Desktop](https://www.docker.com/products/docker-desktop) - - -### Step 2: Set up the environment - -Clone the [sample provided in the Quickstarts repo](todo). +In a new terminal window, navigate to the `order-processor` directory: ```bash -git clone https://github.com/dapr/quickstarts.git +cd workflows/csharp/sdk/order-processor ``` -### Step 3: - -### Step 4: - -### Step 5: - -{{% /codetab %}} - - -{{% codetab %}} - -### Step 1: Pre-requisites - -For this example, you will need: - -- [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started). -- [Latest version of Go](https://go.dev/dl/). - -- [Docker Desktop](https://www.docker.com/products/docker-desktop) - - -### Step 2: Set up the environment - -Clone the [sample provided in the Quickstarts repo](todo). +Install the dependencies: ```bash -git clone https://github.com/dapr/quickstarts.git +dotnet restore +dotnet build ``` -### Step 3: +### Step 3: Run the order processor app + +In the terminal, start the order processor app alongside a Dapr sidecar: + +```bash +dapr run --app-id order-processor dotnet run +``` + +This starts the `order-processor` ID and triggers 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 dotnet run`: + +1. A unique order ID for the workflow is generated (in the above example, `6d2abcc9`) and the workflow is scheduled. +1. The `NotifyActivity` workflow activity sends a notification saying an order for 10 cars has been received. +1. 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. +1. Your workflow starts and notifies you of its status. +1. The `ProcessPaymentActivity` workflow activity begins processing payment for order `6d2abcc9` and confirms if successful. +1. The `UpdateInventoryActivity` workflow activity updates the inventory with the current available cars after the order has been processed. +1. The `NotifyActivity` workflow activity sends a notification saying that order `6d2abcc9` has completed. +1. The workflow terminates as completed. + +The workflow and its activities are defined in the following sample files: + +#### `order-processor/Program.cs` + +In the application's program file: +- The unique order ID is generated +- The workflow is scheduled +- The workflow status is retrieved +- The workflow and the workflow activities it invokes are registered + +```csharp +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(); + + // These are the activities that get invoked by the workflow(s). + options.RegisterActivity(); + options.RegisterActivity(); + options.RegisterActivity(); + options.RegisterActivity(); + }); +}; + +//... + +// 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). + +```csharp +using Dapr.Workflow; +//... + +class OrderProcessingWorkflow : Workflow + { + public override async Task 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( + 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` -### Step 4: -### Step 5: {{% /codetab %}} + {{< /tabs >}} ## Tell us what you think! @@ -170,12 +267,8 @@ Join the discussion in our [discord channel](https://discord.com/channels/778680 ## Next steps -- Set up Workflow using HTTP instead of an SDK. - - [Python](todo) - - [JavaScript](todo) - - [.NET](todo) - - [Java](todo) - - [Go](todo) +- Set up Workflow with any programming language using [HTTP instead of an SDK]({{< ref howto-manage-workflow.md >}}) +- Walk through a more in-depth [.NET SDK example workflow](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow) - Learn more about [Workflow as a Dapr building block]({{< ref workflow-overview >}}) {{< button text="Explore Dapr tutorials >>" page="getting-started/tutorials/_index.md" >}} diff --git a/daprdocs/static/images/workflow-quickstart-overview.png b/daprdocs/static/images/workflow-quickstart-overview.png new file mode 100644 index 000000000..c526a0486 Binary files /dev/null and b/daprdocs/static/images/workflow-quickstart-overview.png differ