A workflow is a combination of two discrete ideas:
- A state machine
- A set of tasks associated with each state.
When trying to merge the two concepts into a business application, task responsibilities typically leak into the StateMachine and sometimes the other way around. This causes the code to deteriorate – becoming difficult to maintain, understand and grow
to meet changing business requirements.
Let’s take a typical blog engine where a comment may be moderated.
This is a clean diagram shows the discrete states in circles and the triggers for each state named in the arrows showing the transition points.
The diagram only captures part of the story, because the business requirements may state
- When entering the review phase, AutoApprove previously cleared users so their comments don’t have to be reviewed.
- If the review does not result in an AutoApprove, send the blog owner an email requesting a review.
- Keep a history of when the document moved from state to state.
These are hypothetical requirements and quite simplified but they convey the heart of our point which is:
- These tasks have relationships between each other.
- They may invoke other transitions.
- [The hidden requirement] The business requirements will change over time, so this needs to be flexible.
Now let’s look at a diagram that fills out the picture with our business rules:
Now that we have a graph that conveys the transitions across the state barrier, we can see a couple of key points:
- Recording state changes occurs when we leave a state. This could happen in other ways, but this seems to make the most sense.
- The state persistence is explicit here as the first step – only because it makes sense to save immediately given the rules, but our system should be flexible to change ordering.
- State Change activities could be launched when entering a state.
Based on these requirements, we can create a DSL that looks like this:
Notice that we are defining states, the triggers that can act upon the states and the state tasks that must execute when entering or leaving the states. Also notice that interrelationships are being defined through the ‘DependsOn’ method – which allows
for very easy modifications to the ordering of these tasks. This removes the desire to create ‘Uber-tasks’ that have many ordered responsibilities and instead we can drive to small, discrete tasks that are ordered by FluentWorkflow.
As you read through the documentation, you will see that the fluent configuration of FluentWorkflow is very close to this conceptual picture. With this picture, we even have enough information to generate the same graphs above – whether we use these
for in-application visuals, build-time artifacts or both.