Workflow Core DSL for JSON and YAML

Shaikh Zaki Mohammed
4 min readMar 13, 2023

--

Workflow Core comes with many features one of which is its common DSL for JSON and YAML. In this article, we will explore JSON/YAML way of creating the process workflows.

If you are following along, we have already seen how to get started with Workflow Core with .NET Core (6.0); you can check Workflow Core Getting Started article. In continuation to that, we will understand how to use common DSL (JSON/YAML) provided by Workflow Core and re-create the same example with DSL instead of C# based workflow.

Check out the Workflow Core documentation and also explore the Git Repository, thanks to Daniel Gerlag.

Initial Workflow Core Setup

The initial setup of Workflow Core remains the same as explained in the previous article; except we need to add one more package for DSL:

dotnet add package WorkflowCore.DSL

The remaining steps will remain as is:

  1. Create Steps
  2. Create Workflow
  3. Register and start the Workflow from the main program file

The folder structure too remains the same:

workflow-start
|-- Workflows
|-- ProcessPayment
|-- Steps
|-- ApplyDiscount.cs
|-- ApplyShipping.cs
|-- Finalize.cs
|-- Initialize.cs
|-- ProcessPaymentWorkflow.cs
|-- ProcessPaymentWorkflow.json
|-- ProcessPaymentWorkflow.yml
|-- GlobalUsings.cs
|-- Program.cs
|-- workflow-start.csproj

Here, we have added JSON and YAML files for the workflow. But we need to revisit step 2 since our definition for the workflow will be written in DSL (JSON/YAML).

Define DSL for Workflow

Consider the below workflow that we have created in C# `..\Workflows\ProcessPayment\ProcessPaymentWorkflow.cs`

Workflows/ProcessPayment/ProcessPaymentWorkflow.cs

public class ProcessPaymentWorkflow : IWorkflow
{
public string Id => "ProcessPaymentWorkflow";

public int Version => 1;

public void Build(IWorkflowBuilder<object> builder)
{
builder
.UseDefaultErrorBehavior(WorkflowErrorHandling.Suspend)
.StartWith<Initialize>()
.Then<ApplyDiscount>()
.Then<ApplyShipping>()
.Then<Finalize>();
}
}

We will comply with this and create the same definition in YAML.

Id: ProcessPaymentWorkflow
Version: 1
Steps:
- Id: Initialize
StepType: Initialize, workflow-start-dsl
NextStepId: ApplyDiscount
- Id: ApplyDiscount
StepType: ApplyDiscount, workflow-start-dsl
NextStepId: ApplyShipping
- Id: ApplyShipping
StepType: ApplyShipping, workflow-start-dsl
NextStepId: Finalize
- Id: Finalize
StepType: Finalize, workflow-start-dsl

Let us zoom in to individual step definition:

- Id: Initialize
StepType: Initialize, workflow-start-dsl
NextStepId: ApplyDiscount

It includes the Id of the step, this can be given anything within the YML definition file. Then we have StepType which needs to match the step class name as is along with the project assembly name (workflow-start-dsl). Then we have NextStepId which points to the next step within the YML definition file; this must match the Ids given to each step within the YML definition file.

The same goes for JSON files too, even you can use simple YML to JSON conversion if you want to have both of the versions, as shown below:

{
"Id": "ProcessPaymentWorkflow",
"Version": 1,
"Steps": [
{
"Id": "Initialize",
"StepType": "Initialize, workflow-start-dsl",
"NextStepId": "ApplyDiscount"
},
{
"Id": "ApplyDiscount",
"StepType": "ApplyDiscount, workflow-start-dsl",
"NextStepId": "ApplyShipping"
},
{
"Id": "ApplyShipping",
"StepType": "ApplyShipping, workflow-start-dsl",
"NextStepId": "Finalize"
},
{
"Id": "Finalize",
"StepType": "Finalize, workflow-start-dsl"
}
]
}

This is the same except for the ugly curly braces.

Load Definition

For setting up the workflow we have to do the below steps in Program.cs file:

Add the Workflow DSL service after the Workflow server in the ServiceCollection object.

var serviceProvider = new ServiceCollection()
.AddLogging()
.AddWorkflow()
.AddWorkflowDSL()
.BuildServiceProvider();

Create a load definition method to load the workflow definition from DSL file:

static void LoadDefinition(IServiceProvider serviceProvider)
{
var type = Deserializers.Yaml;
var file = File.ReadAllText($"Workflows/ProcessPayment/ProcessPaymentWorkflow.{(type == Deserializers.Yaml ? "yml" : "json")}");

var loader = serviceProvider.GetService();
if (loader == null)
throw new Exception("Loader not initialized");

loader.LoadDefinition(file, type);
}

Call the LoadDefinition method instead of calling the RegisterWorkflow method of the host object:

var host = serviceProvider.GetService();
if (host == null)
throw new Exception("Host not initialized");

LoadDefinition(serviceProvider);

host.Start();

host.StartWorkflow("ProcessPaymentWorkflow");

Console.ReadLine();

host.Stop();

Now, when you run the project it will simply print these steps console text on you console window:

dotnet run
Initialize
ApplyDiscount
ApplyShipping
Finalize

We will continue to explore some of the other Workflow Core features in upcoming posts. Stay tuned!

Git Repository

Check out the git repository for this project or download the code.

Download Code

Git Repository

Summary

The Workflow Core’s common DSL provides a way to express your process workflow one step closer to the business understanding. Mainly YAML is considered a language of configuration and widely accepted by business analysts, automation, and DevOps professionals. This allows for defining workflows that can be easily used by both the hard-core developers and business bodies. One of the vital asks from any Workflow Core engine is to have a common language construct and Workflow Core provides it.

Hope this article helps.

Originally published at https://codeomelet.com.

--

--

Shaikh Zaki Mohammed
Shaikh Zaki Mohammed

Written by Shaikh Zaki Mohammed

Learner, developer, coder and an exceptional omelet lover. Knows how to flip arrays or omelet or arrays of omelet.

No responses yet