Welcome to MSDN Blogs Sign in | Join | Help

Syndication

State Machines in Domain Models

I was reminded today of Stateless, a little project that I’m quite fond of.

Stateless is a hierarchical state machine framework based on Simple State Machine for Boo, but configured using C# 3.0.

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.

There’s more than one way out. The State pattern can be one very elegant solution.

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.

image

The Telephone Call state chart is broken down into the following states and triggers:

image

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.

image

Once the state machine has been configured, the triggers can be ‘fired’. The side-effects from firing triggers drive the program.

image

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.

image

Users of the class interact with the state machine indirectly, by calling public methods on the domain object.

image

The state machine is configured to call two methods when entering and leaving the Connected state (see the configuration above.)

image

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!

Published Thursday, April 16, 2009 7:52 AM by niblumha

Filed under: ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: State Machines in Domain Models @ Saturday, April 18, 2009 4:13 PM

great followup to the DSL Devcon Nick -- looking forward to exploring it

ckapilla

# re: State Machines in Domain Models @ Sunday, April 19, 2009 1:55 AM

This book really inspired me to think about HSMs. In particular I liked the term "Behavioral Inheritance".  If you get a chance, check it out:

http://www.amazon.com/Practical-Statecharts-Quantum-Programming-Embedded/dp/1578201101/

Instead of workflow, I wish WF has gone more toward Data Flow like the MS Robotics guys have gone.  Combining your statemachine and the CCR go be a great combo.  Lead to some very interesting and robust multi-threaded programs.  Sticking with a DSL makes it very interesting.

Here is some clever us of the CCR:

http://www.extreme.indiana.edu/multicore/SOX.htm

Add a state machine controller and you have something pretty powerful.

Will Smith

# re: State Machines in Domain Models @ Monday, April 20, 2009 7:42 AM

FYI, with the way you have it programmed, your _totalMinutes TimeSpan will not have the value you intended after a call to StopCallTimer. I submitted a documentation error on this recently to MS (actually, on DateTime, but it applies to TimeSpan as well, and presumably other structs), and hopefully this points it out even more. It claims to change the value of the instance, but of course, since it's a value-type, it actually just returns the value, not, "Changes the value of the instance."

It's silly that anyone would write a piece of documentation to say that, and then at the very bottom buried in the remarks of the function it says that it doesn't change the ACTUAL value of the instance. Ugh.

Kyle Szklenski

# re: State Machines in Domain Models @ Tuesday, April 21, 2009 2:09 PM

Cool implmentation.

I am also maintaining an open-source state-machine project. It's a descendant of Leslie Sanford's state machine toolkit that was published on CodeProject. I took his project (with his permission of course) and added generics support and lots of other features.

You can find my source here: https://sourceforge.net/projects/smtoolkit/

A good intro page can be found here: http://smtoolkit.wiki.sourceforge.net/ with links to the original project at CodeProject, and the added features.

Omer Mor

# re: State Machines in Domain Models @ Wednesday, April 22, 2009 3:53 PM

@Will, thanks for the link, will definitely check it out!

@Kyle, good spotting, if I had a dollar for every time I messed that up :)

@Omer, thanks also for the links, it looks like an  interesting project.

niblumha

# re: State Machines in Domain Models @ Saturday, April 25, 2009 2:14 AM

I think your concept is a not completely faithful to the state machine pattern.

As I understand, a superstate contains substates, of which one of them is the initial substate. When entering a superstate, you end in the initial substate (unless you support history, and then you have some other options too). You can never be in a superstate without also being in one of its substates.

In your implementation, the superstate is a state of its own, and you have to explicitly transition to one of its substates.

In your example, to be faithful to the pattern, your Connected superstate should have a Talking substate which is the initial substate. You can transition from Talking to OnHold or vice versa.

You should also not handle the HungUp trigger in the OnHold state: It enough to handle it in the Connected superstate.

Omer Mor

# re: State Machines in Domain Models @ Monday, May 18, 2009 2:15 AM

how to deal with the delays ...

saqibmax

# re: State Machines in Domain Models @ Saturday, August 01, 2009 8:29 AM

I have discarded the state machine in Windows Workflow for the stateless library.  Integrating it with my project took less than a day, and I was able to jettison a ton of code written to integrate the WF state machine with a web application.

The beauty of this library is in its simplicity.  With WF you typically need 2 - 4 projects that support passing data in and out of the workflow, mechanisms to persist the workflow, and potentially MSMQ for high volume transactions.  Stateless eliminates all of that complexity.

Taken from the perspective of maintaining a workflow, since Stateless is just a class there are many ways of making the state machines it would support configurable.  Representing states and triggers as data types means I can make adjustments or version state machines based on data in a database, JSON or XML datasource.  I can't do that with Windows WF without re-compiling as there is no way to add states and triggers.  

In short, you saved me a ton of time, and I am really grateful.  Good work.

David Robbins

# re: State Machines in Domain Models @ Sunday, August 02, 2009 1:55 PM

Wow, thanks David! I'm really glad to hear that it's been useful.

niblumha

# re: State Machines in Domain Models @ Sunday, August 16, 2009 12:39 AM

@Omer, sorry about the lagging response :)

It's the implementer's decision whether to use the superstate as a valid state as I have done, or to require an 'initial substate' as you suggest.

Stateless supports both of these styles, I've chosen the one that fits best in this particular example.

niblumha

Leave a Comment

(required) 
required 
(required) 

  
Enter Code Here: Required
Page view tracker