mirror of https://github.com/dapr/docs.git
update per Ryan quickstart pr and add diagram
Signed-off-by: Hannah Hunter <hannahhunter@microsoft.com>
This commit is contained in:
parent
92721d33df
commit
caa8db80c4
|
@ -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.
|
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" %}}
|
{{% 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 %}}
|
{{% /alert %}}
|
||||||
|
|
||||||
|
|
|
@ -93,8 +93,8 @@ Want to put workflows to the test? Walk through the following quickstart and tut
|
||||||
|
|
||||||
| Quickstart/tutorial | Description |
|
| Quickstart/tutorial | Description |
|
||||||
| ------------------- | ----------- |
|
| ------------------- | ----------- |
|
||||||
| [Workflow quickstart](link) | Description of the quickstart. |
|
| [Workflow quickstart]({{< ref workflow-quickstart.md >}}) | Run a .NET workflow application with four workflow activities to see Dapr Workflow in action |
|
||||||
| [Workflow tutorial](link) | Description of the tutorial. |
|
| [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
|
### Start using workflows directly in your app
|
||||||
|
|
||||||
|
|
|
@ -6,70 +6,29 @@ weight: 77
|
||||||
description: Get started with the Dapr Workflow building block
|
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 %}}
|
||||||
|
|
||||||
<img src="/images/pubsub-quickstart/pubsub-diagram.png" width=800 style="padding-bottom:15px;">
|
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" >}}
|
In this guide, you'll:
|
||||||
<!-- Python -->
|
|
||||||
{{% codetab %}}
|
|
||||||
|
|
||||||
### 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:
|
<img src="/images/workflow-quickstart-overview.png" width=800 style="padding-bottom:15px;">
|
||||||
|
|
||||||
- [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started).
|
Currently, you can experience Dapr Workflows using the .NET SDK.
|
||||||
- [Python 3.7+ installed](https://www.python.org/downloads/).
|
|
||||||
<!-- IGNORE_LINKS -->
|
|
||||||
- [Docker Desktop](https://www.docker.com/products/docker-desktop)
|
|
||||||
<!-- END_IGNORE -->
|
|
||||||
|
|
||||||
### Step 2: Set up the environment
|
{{< tabs ".NET" >}}
|
||||||
|
|
||||||
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 %}}
|
|
||||||
|
|
||||||
<!-- JavaScript -->
|
|
||||||
{{% 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/).
|
|
||||||
<!-- IGNORE_LINKS -->
|
|
||||||
- [Docker Desktop](https://www.docker.com/products/docker-desktop)
|
|
||||||
<!-- END_IGNORE -->
|
|
||||||
|
|
||||||
### 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 %}}
|
|
||||||
|
|
||||||
<!-- .NET -->
|
<!-- .NET -->
|
||||||
{{% codetab %}}
|
{{% codetab %}}
|
||||||
|
@ -86,81 +45,219 @@ For this example, you will need:
|
||||||
|
|
||||||
### Step 2: Set up the environment
|
### 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
|
```bash
|
||||||
git clone https://github.com/dapr/quickstarts.git
|
git clone https://github.com/dapr/quickstarts.git
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 3:
|
In a new terminal window, navigate to the `order-processor` directory:
|
||||||
|
|
||||||
### Step 4:
|
|
||||||
|
|
||||||
### Step 5:
|
|
||||||
|
|
||||||
{{% /codetab %}}
|
|
||||||
|
|
||||||
<!-- Java -->
|
|
||||||
{{% 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.
|
|
||||||
<!-- IGNORE_LINKS -->
|
|
||||||
- [Docker Desktop](https://www.docker.com/products/docker-desktop)
|
|
||||||
<!-- END_IGNORE -->
|
|
||||||
|
|
||||||
### Step 2: Set up the environment
|
|
||||||
|
|
||||||
Clone the [sample provided in the Quickstarts repo](todo).
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/dapr/quickstarts.git
|
cd workflows/csharp/sdk/order-processor
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 3:
|
Install the dependencies:
|
||||||
|
|
||||||
### Step 4:
|
|
||||||
|
|
||||||
### Step 5:
|
|
||||||
|
|
||||||
{{% /codetab %}}
|
|
||||||
|
|
||||||
<!-- Go -->
|
|
||||||
{{% 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/).
|
|
||||||
<!-- IGNORE_LINKS -->
|
|
||||||
- [Docker Desktop](https://www.docker.com/products/docker-desktop)
|
|
||||||
<!-- END_IGNORE -->
|
|
||||||
|
|
||||||
### Step 2: Set up the environment
|
|
||||||
|
|
||||||
Clone the [sample provided in the Quickstarts repo](todo).
|
|
||||||
|
|
||||||
```bash
|
```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<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).
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
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`
|
||||||
|
|
||||||
### Step 4:
|
|
||||||
|
|
||||||
### Step 5:
|
|
||||||
|
|
||||||
{{% /codetab %}}
|
{{% /codetab %}}
|
||||||
|
|
||||||
|
|
||||||
{{< /tabs >}}
|
{{< /tabs >}}
|
||||||
|
|
||||||
## Tell us what you think!
|
## Tell us what you think!
|
||||||
|
@ -170,12 +267,8 @@ Join the discussion in our [discord channel](https://discord.com/channels/778680
|
||||||
|
|
||||||
## Next steps
|
## Next steps
|
||||||
|
|
||||||
- Set up Workflow using HTTP instead of an SDK.
|
- Set up Workflow with any programming language using [HTTP instead of an SDK]({{< ref howto-manage-workflow.md >}})
|
||||||
- [Python](todo)
|
- Walk through a more in-depth [.NET SDK example workflow](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow)
|
||||||
- [JavaScript](todo)
|
|
||||||
- [.NET](todo)
|
|
||||||
- [Java](todo)
|
|
||||||
- [Go](todo)
|
|
||||||
- Learn more about [Workflow as a Dapr building block]({{< ref workflow-overview >}})
|
- Learn more about [Workflow as a Dapr building block]({{< ref workflow-overview >}})
|
||||||
|
|
||||||
{{< button text="Explore Dapr tutorials >>" page="getting-started/tutorials/_index.md" >}}
|
{{< button text="Explore Dapr tutorials >>" page="getting-started/tutorials/_index.md" >}}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
Loading…
Reference in New Issue