Workflows, service buses, sagas…
Last few weeks I spent much time playing with Workflow Foundation. The goal was to evaluate WF programming model for the purpose of our projects. We are currently using home grown workflow manager, it helped us quit database deadlocks (yes, we went through sins of long running transactions), but it’s implementation is pretty naive and considered by everyone as a milestone on our way to the real thing.
This real thing might be a combination of WF and WCF. This is why I spent some time creating simple WF activities consisting of SendActivity and ReceiveActivity that were introduced in WF with .NET 3.5.
I have mixed feelings, I must say. Samples look great, as usual, but once you write all code that you have to write in order to make ends meet, the resulting code base does not look very elegant. The question I was trying to answer was if WF/WCF integration can free service component developers from thinking about workflow integration aspects. What I hoped is that developers could focus on exposing WCF contracts to their service components, and the actual orchestration could use those components as building blocks. While this is not very far from truth – contracts and their implementation don’t need to carry workflow-related information (except callback contracts), there is too much to care about integration details to call the integration smooth. One thing that really drives me nuts is configuration. “Convention over configuration” principle is very hard to implement to make WF and WCF play together. For example, if you want your SendActivity to call a WCF service using programmatically specified address, you have to do the following:
- In workflow designer define SendActivity’s ChannelToken property and assign it a name, let’s say sendActivity_ChannelToken
- Expand newly created channel token properties and specify an EndpointName, let’s say sendActivity_EndPoint
- In code create an instance of Endpoint with the name “sendActivity_EndPoint” and assign it the address it needs
- Create an instance of ChannelManagerService and pass custom endpoint (or endpoints) to the constructor
- Obtain a workflow runtime instance and add newly created instance of ChannelManagerService
To make the above process even more challenging, you have to carefully plan last two steps. You can only add ChannelManagerService instance to workflow runtime once, and before the runtime is activated. And you can’t later add more endpoints to it. So there’s very little you can achive dynamically, all workflows, services and interactions between them are supposed to be configured upfront.
If this is not enough, WorkflowServiceHost (context-enabled ServiceHost) creates a new workflow runtime for each workflow that is instantiated by its invocation. How expensive is to multiply workflow runtimes is another question (depends on afforded performance cost), but since each extra service (pesistence service, tracking service etc.) follows workflow runtime and must be added to it early enough, doing this runtime preparation each time you want to start a new workflow that contains ReceiveActivity does not look like a good programming model.
If you want to establish WCF communication between two workflows, you need to save workflow instance Ids and assign them to the channel context in BeforeSend event:
private void sendActivity_BeforeSend(object sender, SendActivityEventArgs e)
Dictionary context = new Dictionary();
e.SendActivity.Context = context;
This is not many lines of extra code, but when you have a powerful workflow designer, you could expect it to take care of such plumbing.
I look forward to test how WF 4 is going to solve these issues. Apparently smoother integration with WCF was one of the reasons for rewriting it from the ground up. If however I need to choose an enterprise orchestration framework in July 2009, I can as well check out some other options. One interesting alternative is NServiceBus. It will of course require full review and redesign of communication layer between service components, but we are doing it anyway, so it won’t necessarily cost more than adopt the system to use Workflow Foundation. Udi Dahan (a man behind NServiceBus) refers in his blog to Saga concept – a model to handle long-running transaction that was first described in 1987 in the article by Garcia Molina and Kenneth Salem. Since workflow with long-running transactions is our speciality, I continued search for information about Saga and found another project called Massive Transit that does exactly that. I looked at code examples and I think they are charming. Look for example at this one. Yes, both NServiceBus and MassTransit are not workflow frameworks, they are service bus implementations. So you can’t just prefer NServiceBus to WF or vice versa, you have to decide first what type of enterprise framework you need. But when all you have is a system built on the pillars of RPC-style communication, your hands are free and both directions seem to be open. Perhaps if I was already working with .NET 4, I would find WF model convincing for the features we need. But WF/WCF integration in .NET 3.5 is painful enough to be more open minded.