Best practices in Workflow Design - Part 1
It’s been a awfully long time since I have published a blog entry, and it seems like the world of technology has changed quite a bit since! The hiatus is over now, and I would like to starting putting down some thoughts and experiences on my new interest – Connected Systems. WF and WCF are the pretty much the atoms for building connected systems on the .NET platform, and a large percentage of my upcoming posts would be focused on these technologies.
In this post, I want to talk about some aspects of workflow design based on my experience, which I think can be classified as idioms or best practices.
Workflows should contain only ‘flow of logic’ and not the logic itself
It is easy to visualize workflows as business logic which is modeled using a designer. It is common to see developers transform their typical business logic into workflows by separating different parts of the logic into separate workflow activities. Here, we see lots of procedural code gets transformed into workflow models. This is clearly not the right thing to do. Workflows are not be be plain replacement for existing business logic components. Workflows are to be used in specific scenarios - like long running business processes, rule driven logic, dynamic flow changes etc (I'll cover this in a later post as well).
The figure shown below contains a snippet of a bad workflow design where procedural logic is transformed to a workflow through usage of code activities (in an order processing application):

Avoid business logic code within workflows
Ideally, workflows should not contain busines code themselves. In other words, as much as possible, usage of CodeActivity should be avoided (at least, it should not contain business logic). One way to take out business logic contained in code activities is to replace them with CallExternalMethod activity, where you invoke upon a method of a registered service. A better way would be to create a custom activity, where we override the Execute method, and within it delegate the business operation to a registered service. This way, taking individual pieces of business logic out of the workflow also helps us to unit test them in isolation. If a workflow is designed well, it would not have any code in its code-behind logic. This approach would also help us in scenarios where we have only XAML-based activation of workflows.
Given below is a snippet of a custom activity which is used in the Purchase Order creation workflow, and updates the status of a workflow in the database. The Execute method contains the usage of a registered service to which the business operation is delegated to (updating of the status field in this case).
public partial class UpdateOrderStatusActivity: Activity
{
private static DependencyProperty PurchaseOrderProperty = DependencyProperty.Register("PurchaseOrder", typeof(PurchaseOrder), typeof(UpdateOrderStatusActivity));
public PurchaseOrder PurchaseOrder
{
get { return (PurchaseOrder)base.GetValue(PurchaseOrderProperty); }
set { base.SetValue(PurchaseOrderProperty, value); }
}
private static DependencyProperty OrderStatusProperty = DependencyProperty.Register("OrderStatus", typeof(string), typeof(UpdateOrderStatusActivity));
public string OrderStatus
{
get { return (string)base.GetValue(OrderStatusProperty); }
set { base.SetValue(OrderStatusProperty, value); }
}
public UpdateOrderStatusActivity()
{
InitializeComponent();
}
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
IOrderProcessWFService orderService = executionContext.GetService<IOrderProcessWFService>();
orderService.UpdateOrderStatus(PurchaseOrder,OrderStatus);
return ActivityExecutionStatus.Closed;
}
}
I'll try to cover more best practices in my next post. If you have better suggestions or comments, please do share it!