Declarative Workflow with MassTransit

One of the really cool features that is available in the 0.6 release of MassTransit is the ability to declaratively define a saga/workflow using a nested-closure syntax in combination with a fluent builder. While some wonder if fluent interfaces are going to become the “aluminum wiring” of the current generation of software development, I find them incredible valuable for building an expressive representation of logic.

A saga is a way of defining a long-lived transaction, typically involving multiple transactional actors. A workflow is a way of orchestrating a number of actors into a process. Both sagas and workflows can be modeled using the new declarative syntax. First, let’s look at what this new language looks like in code:

static DrinkPreparationSaga()
{
	Define(() =>
		{
			Initially(
				When(NewOrder)
					.Then((saga, message) => saga.ProcessNewOrder(message))
					.TransitionTo(WaitingForPayment)
				);

			During(WaitingForPayment,
			       When(PaymentComplete)
			       	.Then((saga, message) =>
			       		{
			       			Console.WriteLine("Payment Complete for '{0}' got it!", saga.Name);
			       			saga.ServeDrink();
			       		})
			       	.Complete()
				);
		});
}

(I’ve removed the class structure around the constructor to keep the code short)

If we read from the top down, it is easy to understand the behavior for this saga. Initially (which signifies the creation of a new saga based on an event in this block), when a NewOrder is received, then we’re going to call the ProcessNewOrder method on the saga passing it the message that was received. Once that method completes, the saga will transition to the WaitingForPayment state. During the WaitingForPayment state, if a PaymentComplete is received, the saga will output a message, call the ServeDrink method on the saga, and complete.

The states and events of the saga are defined by adding some static properties to the class:

public static State Initial { get; set; }
public static State Completed { get; set; }
public static State PreparingDrink { get; set; }
public static State WaitingForPayment { get; set; }

public static Event NewOrder { get; set; }
public static Event PaymentComplete { get; set; }

The static constructor for the state machine initializes these properties during the definition of the behavior as part of the Define() call. It is nice to be able to see all the states and events for the saga in one place, as well as the messages that are related to the events.

Another great feature allows us to ask the saga state machine engine to tell us what it is going to do based on how we defined the behavior of the saga. The StateMachineInspector class is a tool that will output a trace of the defined behavior of the state machine. For the above saga, the output looks like:

	During Initial
		When NewOrder Occurs Containing NewOrderMessage
			(custom action)
			Transition To WaitingForPayment
	During PreparingDrink
	During WaitingForPayment
		When PaymentComplete Occurs Containing PaymentCompleteMessage
			(custom action)
			Transition To Completed
	During Completed

This information is generated by using expressions and reflecting over the class and types contained. The above example is taken from the Starbucks example in the MassTransit trunk. For a service to subscribe and handle sagas like this, the service only needs to call:

var unsubscribe = bus.Subscribe();

Like all component-based consumers in MassTransit, the container should know how to build the saga and needs to have a ISagaRepository implementation for the saga as well.

I hope as more people start to use this syntax that it evolves in a really rich was of doing code-first workflow and saga definitions. One thing we definitely want to do is take the text-based visualization of the saga and output it in a format that can be used by GraphViz or MSGLEE to get a nice diagram of the behavior of the saga. Hopefully that will be coming sooner rather than later!

So that’s one of the really cool features of MassTransit that have been added.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Chris Patterson

Chris is a senior architect for RelayHealth, the connectivity business of the nation's leading healthcare services company. There he is responsible for the architecture and development of applications and services that accelerate care delivery by connecting patients, providers, pharmacies, and financial institutions. Previously, he led the development of a new content delivery platform for TV Guide, enabling the launch of a new entertainment network seen on thousands of cable television systems. In his spare time, Chris is an active open-source developer and a primary contributor to MassTransit, a distributed application framework for .NET. In 2009, he was awarded the Most Valuable Professional award by Microsoft for his technical community contributions.
This entry was posted in .net. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://robinclowers.blogspot.com Robin Clowers

    This looks really cool Chris, I have been building a workflow using windows workflow foundation, and I’ll tell you right now, this looks an order of magnitude simpler, more productive, and easier to refactor. Keep up the good work!

  • http://www.lostechies.com/members/phatboyg/default.aspx Chris Patterson

    Thanks. Any feedback you have would be great to help evolve the structure/feature set.

  • http://www.hollman-alu.nl Aluminium Kozijnen

    I think saga is a great way to transactional method.. Thanks for sharing this syntax..