State Machines in Domain Models
I was reminded today of Stateless, a little project that I’m quite fond of.
When I announced Stateless last year, I hardly even explained its purpose, let alone its tongue-in-cheek name. I think I owe it a better start in life!
If you’re doing heavy duty state-machine based programming (writing parsers, radiotherapy machines or flight control systems) then Stateless is probably not what you’re looking for.
If you’re doing domain-driven design, and anxious about the ugly nested ‘if’ and ‘switch’ statements building up around _state variables, Stateless can help.
State-based behaviour is awkward because despite careful programming, it is difficult to see how the states relate to each other. Adding and removing states is error-prone, repetition creeps in, and refactoring gets tricky.
Another solution that works nicely is to create a declarative model of the states, transitions and associated actions, and have a state machine framework ‘run’ it for you. This is the approach enabled by Stateless.
The Telephone Call state chart is broken down into the following states and triggers:
You can use any types to represent states and triggers, but enumerations are convenient.
After creating an instance of StateMachine, each state is configured independently. Configuration for a state revolves around the triggers that the state can accept, the transitions that these triggers will cause (to new states) and the actions that will be performed when entering or leaving a state.
Once the state machine has been configured, the triggers can be ‘fired’. The side-effects from firing triggers drive the program.
You might wonder how this fits into a domain model. StateMachine certainly doesn’t belong on the public surface area of your domain model classes. It’s an implementation detail – like, for example, StringBuilder or Regex.
Users of the class interact with the state machine indirectly, by calling public methods on the domain object.
The state machine is configured to call two methods when entering and leaving the Connected state (see the configuration above.)
Stateless does all of the heavy lifting. For example, the state machine understands that because OnHold is a sub-state of Connected, the Connected entry/exit actions aren’t called when moving between these two states.
Now for a justification of the silly name…
Notice the way that the _state field belongs to the PhoneCall object, rather than the state machine? This allows the field to be easily mapped to a database column by NHibernate or your ORM of choice. The state machine reads and writes the _state value using the pair of lambdas provided to its constructor. In that sense you can almost say that the state machine itself is ‘stateless’. Almost.
There’s some more basic documentation and a source download at the site. I hope you’ll try Stateless and be pleasantly surprised by how expressive it can make your code. Search your domain model for “State” or “Status” and see what Stateless can do!
This post also comes with a challenge: Stateless is fairly simple and very hackable - if you’re a fluent-interface aficionado and think you can improve the configuration API, join the project!