SimpleStateMachine on CodePlex
I bet you didn't know hillbillies believe in kismet. Well, we do, mostly because it typically works in our favour. The minute things start going wrong, well, that's just plain someone else's fault and typically requires at least one lawsuit.
But today, kismet. That's the only reason I can think of why someone would post a SimpleStateMachine project on CodePlex not three days after I started working on this very problem.
<-- snip -->
I've just cut out an Imperial tonne of back story that upon re-reading was boring as all git out, though wittily told. I'm going to skip to the implementation.
Which still requires some knowledge of the worfklow I'm trying to implement.
This image encapsulates quite a few false starts, most notably in the places where it "branches" off (the beige boxes). My first implementation was a flow where a job could be in three states at once. For example, a job could be "To Be Invoiced", "Awaiting Hydro Approval" and "Awaiting Regulatory Approval" all at the same time. Not a traditional definition of a State Machine but one that I could start coding against nonetheless.
Then comes the SimpleStateMachine that was oh so enticing but also a little less liberal with the rules of a state machine. So I moved, after much deliberation, to the above diagram in which the branches represent the launching of separate workflows. Thus, in a sense, a job *can* be in more than one state at once, but not in a given workflow. Now I could try out the SimpleStateMachine and still appeal to my sense of ancestral ambiguity.
The code is surprisingly easy to follow, not least of which is because of the unit tests and demo application, both of which paint a pretty good picture of how the application can be used. When you need to step through it, you'll find things clearly separated out so it's pretty easy to figure out what's going on.
I'm including the entirety of my main workflow below partially because I like testing out the scrollbars of your RSS reader but mostly so you can see how simple it is. I'm told it's in Boo format but it's my first exposure to the language so I haven't verified. All I know is that it works.
workflow "Basic Job" state_identifier_target @JobStatus event_identifier_target @JobEvent on_enter_state @PersistState, "on_enter_state" _event @SetUp _event @DraftingCompleted _event @DraftingRejected _event @DraftingApproved _event @Invoiced _event @Paid _event @Uncollected _event @Cancel state @Ordered: when @SetUp >> @InDrafting when @Cancel >> @Cancelled state @InDrafting: when @DraftingCompleted >> @ToBeChecked when @Cancel >> @Cancelled state @ToBeChecked: when @DraftingRejected >> @InDrafting when @DraftingApproved >> @ToBeInvoiced when @Cancel >> @Cancelled state @ToBeInvoiced: when @Invoiced >> @AwaitingPayment when @Cancel >> @Cancelled state @AwaitingPayment: when @Paid >> @Closed when @Uncollected >> @BadDebt state @Closed state @BadDebt state @Cancelled
This is *almost* something I could assign to a business user to create. Not that I would. But they could certainly read it and figure it out with minimal coaching.
You'll notice the on_enter_state task at the top. This means that the PersistState task (which is a custom task I've written to write the job status to the database) will execute each time we enter a state.
This concept also applies to individual states. For example, this workflow doesn't totally match the diagram. I've omitted the part where it launches two other workflows. The way I plan to implement them is to modify the @ToBeInvoiced task as follows:
state @ToBeInvoiced: when @Invoiced >> @AwaitingPayment when @Cancel >> @Cancelled on_enter_state @LaunchHydroWorkflow, "on_enter_state(ToBeInvoiced)" on_enter_state @LaunchApprovalWorkflow, "on_enter_state(ToBeInvoiced)"
That is, I will create custom tasks to launch the two other workflows after we enter the ToBeInvoiced state.
So check out the library if you are working with workflows (and props to Ayende for pointing me to the library to begin with). As an added bonus, the author is amazingly quick in responding to issues. In the time it took me to type this up, he added the ability to use an IoC container with your custom tasks and a more efficient way of searching for custom tasks. Both in response to questions I asked on the discussion list.
Next post will likely be on the actual implementation of this workflow. Suffice it to say that the project lives up to the premise on the home page:
"The motiviation for creating this library was a dissatisfaction with the complexity and weight of Windows Workflow Foundation for creating, maintaining, testing and versioning of what should have been simple state machine workflows in our applications."
Kyle the Flowed