Ayende @ Rahien

Refunds available at head office

Simple State Machine

Nathan has posted Simple State Machine to CodePlex, it is the first project that I am aware of that uses Rhino DSL and the techniques that I am talking about in the book.

What is impressive about this is the level of professionalism that is involved in the project. It is a full scale DSL, with all the supporting infrastructure. I spent half an hour or so going through the entire thing, and I am impressed.

Put simply, this is how I think state based work flows should be defined. I could easily see myself extending this a bit to add persistence support & integration with NServiceBus, and be done with it.

Like most state machines, it has the ideas of states, events that can cause the state to be changed, and legal transitions from state to state. You can define tasks which will be executed upon changing a state, or upon entering / leaving a certain state.

Enough talking, let us look at a reasonably complex work flow:

workflow "Order Lifecycle"

#Event & State Identifier Targets.
#This section controls which Types will be used
#to resolve Event or State names into strongly typed CLR objects.
#--------------------------------------------------------
state_identifier_target @OrderStatus
event_identifier_target @OrderEvents

#Global Actions
#--------------------------------------------------------
on_change_state      @WriteToHistory, "on_change_state"
on_workflow_start    @WriteToHistory, "on_workflow_start"
on_workflow_complete @WriteToHistory, "on_workflow_complete"

#Event Definitions
#--------------------------------------------------------
define_event  @OrderPlaced
define_event  @CreditCardApproved
define_event  @CreditCardDenied
define_event  @OrderCancelledByCustomer
define_event  @OutOfStock
define_event  @OrderStocked
define_event  @OrderShipped
define_event  @OrderReceived
define_event  @OrderLost

#State & Transition Definitions
#--------------------------------------------------------
state @AwaitingOrder:
       when @OrderPlaced              >> @AwaitingPayment

state @AwaitingPayment:
       when @CreditCardApproved       >> @AwaitingShipment
       when @CreditCardDenied         >> @OrderCancelled
       when @OrderCancelledByCustomer >> @OrderCancelled

state @AwaitingShipment:
       when @OrderCancelledByCustomer >> @OrderCancelled
       when @OutOfStock               >> @OnBackorder
       when @OrderShipped             >> @InTransit

       #Individual states can define transition events as well
       on_enter_state @WriteToHistory, "on_enter_state(AwaitingShipment)"

state @OnBackorder:
       when @OrderCancelledByCustomer >> @OrderCancelled
       when @OrderStocked             >> @AwaitingShipment

state @InTransit:
       when @OrderReceived            >> @OrderComplete
       when @OrderLost                >> @AwaitingShipment

#NOTE: State definitions without any transitions will cause
#the state machine to Complete when they are reached.
#------------------------------------------------------------
state @OrderComplete
state @OrderCancelled

Here is the demo application UI, for the order processing life cycle:

image

As I said, impressive.

Comments

Shawn Neal
05/31/2008 03:51 PM by
Shawn Neal

This has me wondering if an internal DSL would be a useful component for an e-commerce shopping cart? Specifically use the DSL to calculate discounts between dependent products (buy one get one 50% off etc) and for promotion codes (promo code is good for product X, Y, Z only).

This would certainly be more flexible, but I wonder if it would be more difficult to maintain than an engine that is statically defined and driven by data. It seems some middle ground for this would be preferable, data driven, yet write the rules not in C#, but the DSL?

Also, how does deployment come into play? It would be preferable to support an XCopy style of deployment for the DSL script since I'm going to assume it would be constantly updated - several times a week.

Ayende Rahien
05/31/2008 05:14 PM by
Ayende Rahien

Shawn,

I would tend to say that there are several fixed actions in the system, and leave the policy to the DSL.

The DSL can be just dropped into a directory, and immediately take affect.

Mike
05/31/2008 05:57 PM by
Mike

I keep asking myself, why create a language for every little thing?

grega g
05/31/2008 06:36 PM by
grega g

if artist is not satisfied by any one language he should make his own.

anyway, nice work. since i battle with WF (well our company's hack of WF) i can appreciate such project. All it would need now are persistence & security.

Nathan
06/02/2008 02:27 AM by
Nathan

I have been thinking about persistence, but I'm not sure what there is to persist. In WF, the object graph of the whole state machine is persisted for each instance, but i'm sure that is useful in this case. The only thing I can think of to persist other than the business objects (which I presume are persisted independently) is the current state, and I figured that generally just be another property of the domain.

Dmitriy Nagirnyak
06/02/2008 02:31 PM by
Dmitriy Nagirnyak

What do you think about this state machine implemented in ECO:

http://www.capableobjects.com/downloads/docs/vs/VS04-Creatingastatemachine.pdf

Ayende Rahien
06/04/2008 07:47 PM by
Ayende Rahien

I stopped reading when I saw how many steps you need to perform there

Dmitriy Nagirnyak
06/05/2008 05:09 AM by
Dmitriy Nagirnyak

It might not be so obvious because state machine in ECO is part of the UML model and requires some background in ECO Framework.

But in a short:

  1. You create a model.

  2. You define an attribute of a class that keeps current state.

  3. You define (in UML) states, transitions, guards, effects etc.

  4. You use state it in code. Like so:

order.Approve() - changes statues to Approved (if guards allow doing so).

The idea: all is defined on the UML diagram and uses Model Driven Architecture.

Nathan
06/24/2008 08:14 PM by
Nathan

I'm using Rhino DSL in a migrations open source project that a friend and I are working on. I made a blog post about the first thing I used Rhino DSL to do: manage the configuration of different environments in a settings file.

http://nathan.whiteboard-it.com/archive/2008/06/24/settings-dsl.aspx

Ayende Rahien
06/25/2008 08:58 PM by
Ayende Rahien

Nice, thanks for letting me know

Comments have been closed on this topic.