Living .NET...

Musings on .NET, and the like - Manoj G [MVP, Connected Systems Developer]

July 2008 - Posts

Best practices in Workflow Design - Part 2

Naming convention in Workflows

Workflow activities should be named appropriately. As much as possible, they should reflect names form the business domain. For example, it wouldn’t appear good to have names like ifelseActivity1 or callExternalMethodActivity1. Instead, it is good to have names like ifProductsExists and MakePayment respectively. Shown below is a good example of the same:

Good Naming conventions

Custom activity names should really represent their purpose. Good examples are ApproveRequestActivity. It is customary to suffix custom activity classes with “Activity”. It is also a good practice to provide apt descriptions to all the activities in the workflow. This would help better understand the nuances of the workflow logic.

Transactions in Workflows

In application design, avoid scenarios in which transactions need to span between business components and workflow logic. Either a database transaction should complete before the workflow is invoked, or should be started and completed within the execution of the workflow sequence (using a TransactionScope activity, for example). In either case, care must be taken to ensure that the transaction (and hence, locks on shared resources) should not be kept alive for a long period, as this adversely impacts performance. For long running transactions, compensation should be employed for undoing the effects of a prior transaction. WF has adequate measures (compensatable activities) to incorporate the same.

Sequential versus State machine workflow

It is definitely possible to represent a workflow scenario of a business application as a sequential or a state machine workflow. State machine workflows appear to be the right logical choice for long running business operations, which can typically be visualized as those transitioning from one state to another by means of some human or process input. A sequential workflow may be harder to represent the entire workflow process in these cases, thought it is not impossible. 

State machine workflows also provide for better tracking of the process. Since each state in a state-machine workflow represents a logical step in a business process, the give better visibility into the current state of the process. Refer to this blog post for more info on the usefulness of state machine workflows.
Note that states within a state machine could have sequence of activities. Sequential workflows have their own place and have to be used appropriately.

Workflow Instance Identifiers - Correlation

Workflow scenarios, typically state machine ones, involve persistence. Here, the workflow waits for an event to occur and hence its state is persisted into the database. The workflow is brought to life by another part of the application which loads the hydrated instance and executes the workflow (perhaps transitions it to the next state). The unloading and loading is correlated by the WorflowInstanceId (a Guid value). It is the application’s responsibility to keep this value and then use it at a later point of time. It could be a wise thing to store this value in the database alongside with the other information and then fetch the same for correlation at a later point of time.

It is perhaps, not a good practice to wait for the runtime to start the workflow so as to get the WorkflowInstanceId. A better approach is to generate the Guid value and pass it the workflow runtime as a parameter. This helps in situations where starting a workflow is a small part of a transactional business operation, and you need to keep the WF runtime pieces out of transaction scope.

Error Handling in Workflows

It is important to understand that error handling in workflows is as important as those in the business components.  Workflows that encounter an error, either through an exception thrown by a called service or that is raised internally, go in faulted state and complete prematurely. Since the execution nature of workflow is asynchronous, unhandled exceptions are not raised to the application and go unnoticed. This could adversely affect several parts of the application expecting a persisted workflow, especially in the case of a state machine workflow. Since the faulted workflow goes to the closed state, the application expecting the workflow to be in a specific state is left in an unrecoverable situation.

To avoid such situations, one should include appropriate fault handlers in the workflow.  An example of the same is shown in the figure below. It would be the responsibility of the fault handler to take the workflow to a recoverable state. The fault handler could do things like logging the exception details, persist recovery information and so on, analogous to a catch block in C#.

Error handling in State Machine Workflows

State machine workflows are to be handled in a different way. State machine workflows do not have a workflow-wide fault handler collection (it does not make sense either). Here, one should provide a fault handler for every sub-activity within each state activity. It is a good practice to keep the semantics of a state machine even in the event of a fault. This can be done by having the fault handler move the workflow to a user-defined error state. At this state, the workflow could either wait for recovery to happen, might just log and exception and move to completed state, or whatever is the apt handling logic for the application. You could consider options for retrying for a fixed number of times as well.
 
Workflow Cancellation

Cancellation of workflows is handled similar to Faults. One should wire appropriate Cancellation handlers to every activity. Like a fault handler, the cancel handler should gracefully close a workflow by taking the appropriate action.
 

Posted: Jul 16 2008, 02:36 AM by Manoj G | with no comments
Filed under: ,
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):

Bad Workflow Design

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!

 

Posted: Jul 12 2008, 06:00 AM by Manoj G | with no comments
Filed under: ,