What's new in Orchard Core Workflows

Orchard & Workflows

I've always been a big fan of Orchard's Workflows module. From implementing straightforward email notifications, user registration and user login flows to more advanced content approval flows, Workflows has been super useful because it allows me to implement logic that could be changed at runtime, without having to do a new deployment. Business logic stored as data is a powerful capability of any platform, and the Workflows module written by Sebastien Ros is what made Orchard exactly that. 

With the advent of Orchard Core, a high performant platform that has been rewritten on ASP.NET Core, I took the opportunity to not only port the Workflows module, but to also improve upon it. I took Sebastien's work, adapted it to new paradigms introduced by Orchard Core, and made the module more robust, flexible and added new capabilities that enable more advanced workflows.

You can find the full source code and Readme here: https://github.com/OrchardCMS/OrchardCore/tree/dev/src/OrchardCore.Modules/OrchardCore.Workflows

So what's new?

Let's have a look at what's new and what's different.

A Fresh New Look

Although I thought that Workflows in O1 looked pretty good, the ported version looks even better. Other than an improved stylesheet, the new editor no longer has a toolbox with activities from which you can drag & drop activities. Instead, there's now an Activity Picker that lets you pick Tasks and Events (which are specialized types of an Activity). Because of this new design, it's easier than ever to read an activity's description as well as finding the activity you're looking for by means of the category filter and text filter.

Rendering of activities on the editor's canvas has improved as well. In O1, an activity would most of the time simply display some icon and maybe a bit of text. In OC, many activities show more relevant details, making it easy to quickly understand what it does and how it is configured. Another improvement is the auto-resize feature of the canvas. In O1, the canvas had a fixed height. In OC, the canvas grows and shrinks automatically in height as you add or remove activities.

JavaScript and Liquid Expressions

In O1, Workflows took advantage of the Tokens feature to provide information to a workflow. A token is some sort of exoression that is processed at runtime, where the expression is evaluated and replaced with the outcome of that expression. Although this works quite nicely, in OC it works even groovier thanks to its Scripting and Liquid support. I find the JavaScript support much easier and more intuitive to work with than tokens, including the developer story of extending the available set of functions via custom modules.

Some activities allow you to set a value as a JavaScript expression while others allow you to set Liquid expressions, depending on the nature of a given activity. For example, the Email activity supports Liquid for its Body field, while the SetProperty activity supports JavaScript for its Value field.

Input, Output and Properties

In O1, when you programmatically invoke a workflow, you have the option to provide information to the workflow by passing an a dictionary of tokens. And as a workflow executes, activities within that workflow have the ability to add information to the workflow state as well. With OC, this is still true, but with two primary differences:

  1. Information is no longer passed in as a dictionary of tokens, but as a dictionary of already resolved objects.
  2. In addition to having input and properties (which are values provided by activities during workflow execution), a workflow now also has the notion of output.

Output is a dictionary of values that are set by activities, and can be retrieved by the code that invoked the workflow.

Although workflow output is not currently used by anything in OC, it does open up scenarios where for example a custom module let's the user define which workflow to execute to perform a calculation for example. Another use case is nested workflows, where a workflow can invoke another workflow, and receive its output and add it to its own set of properties.

Workflows for any context

In O1, a workflow was tightly coupled to content items. In OC, a workflow can be associated with anything. This decoupling is achieved by replacing the Content ID property of a workflow with a Correlation ID. A Correlation ID can be any identifier to associate the workflow with. A typical example is a content item ID. When a content item is created for which a workflow has been defined that handles this event, the workflow is correlated to that content item ID.

I took content ID as an example, but it's easy to imagine how this would work for other workflows that revolve around different types of entities. For example, you could have a custom module that provides CRM capabilities. Such a module could expose a Customer entity which is not implemented as a content item, but as a simple database record. Workflows can then be correlated to customer records.

Persist & Inspect Completed Workflows 

It is now possible to keep finished workflows for retroactive inspection. This might be useful not only to see how many times a workflow has executed, but also inspect individual workflow instances in detail. You will be able to see the full workflow state in the form of its JSON representation. Although not yet part of this early version, you'll also be able to see what path the workflow execution took, plus there will be a log trace available.

Singleton Workflows

By default, when a workflow is triggered, a new workflow instance will be created. Unless the workflow definition is configred to run as a Single Instance workflow. When that option is checked, the workflow manager will not spawn a new instance if there's already one that's not yet completed (or faulted).

Multiple Start Activities

In O1, only one activity could be marked as the start activity. In OC, on the other hand, any number of activities can be marked as a start activity. This enables scenarios where you for example have two events. Triggering any of these events will cause your workflow to execute.

New Activities

Because of some of the improvements to the underlying workflow engine, implementing new activities could not have been any easier. Here's a list of some of the new activities you'll find when working with OC Workflows:

Primitive Activities

This category includes the new Correlate, Set Output and Set Property activities. Each of these activities are configurable with a JavaScript expression, and set the returned value to the appropriate property on the workflow execution context. For example, use the Set Property activity to set a custom property with some value onto the workflow state. This value can then be used via JavaScript expressions in subsequent activities.

Control Flow Activities

New activities within the Control Flow category are If/Else, For Each, For and While. The If/Else activity provides two outcomes: True and False, and is based on a JavaScript expression that you can set that needs to evaluate to a boolean value. The other activities allow for executing a workflow path repeatedly. For example, the For Each activity iterates over a given list of items that you provide in the form of a javaScript expression. The For activity iterates an x number of times, and the While activity iterates for as long as the specified boolean expression evaluates to true.

HTTP Activities

The HTTP category provides two types of activities: Tasks and Events

HTTP Tasks

Although the HTTP Redirect activity is not new, it does provide an additional option, which is the actual HTTP Status Code to return: 301 or 302.

The HTTP Request task activity is an improved version of the Web Request activity from O1, and supports a wider range of content types to provide as the HTTP Content payload (which supports Liquid syntax) and more HTTP Methods. Additionally, the new version allows you to provide any number of HTTP status codes you wish to respond to. For example, if you enter "200, 404, 500", the activity will expose those 3 codes as outcomes.

HTTP Events

HTTP Request event activity is a brand new activity that executes whenever an HTTP request is made to your Orchard application on a specific route. This enables scenarios such as handling webhook events from some web service for example. For example, when something happens on a GitHub repository, GitHub can trigger your workflow, which can do things such as creating or updating content, sending emails, or even making an HTTP request back to GitHub to do something interesting there. 

The Filter Incoming HTTP Request activity behaves exactly like an MVC Action Filter - it is executed for every HTTP request made to your Orchard application matching a configurable route. For example, if you wanted to execute a certain workflow everytime a request is made to the "MyModule/Items/Values" route, this is the activity to start your workflow with.

Background Activities

There is currently just one background activity, which is the Timer activity. Although this activity has the same name as the one in O1, the implemetation is based on a CRON expression. Today, you have to enter the CRON expresson manually, but there is a roadmap item to turn this input field into a flull-fledged CRON expression editor, which is much more user friendly. Until that is available, you can of course generate CRON expressions online.

Other Activities

The complete set of available activities can be found in the module's documentation under the section called "Activities out of the box".

Custom Activity Development

Although the final set of out-of-box activities will take you a long way, it is to be expected that module developers will create their own, more specialized, set of activities. These activities can range from being general-purpose (and might be offered as NuGet packages) to being tailor-made to a specific problem domain. Whatever the case, activity development in Orchard Core is designed to be a breeze. Although implementing the actual Activity class itself remains largely similar to the way it's done in O1, implementing the activity editor and design views has changed significantly. Where you would use the Forms API in O1 to programatically define the activity editor form, in OC this is done via a display driver. In Orchard Core, the display driver system has been made generic, which means that we can reuse it for any kind of object we want to implement rendering for using shapes. Even if they are not content items, which is the case for workflow activities.

Conclusion

Orchard Workflows is an incredibly powerful module that unlocks numerous advanced scenarios with its long-running workflow support. There's a lot you can do already with the out-of-box activities, and it's easy to create new ones using custom modules.

In my next post, I'll walk you through the creation of a long-running content approval workflow to cover some of the more advanced scenarios, such as handling incoming HTTP requests, forking, joining, and signaling. We'll also see how to use JavaScript and Liquid expressions.

See you then!

Leave a comment