views:

139

answers:

2

I'm developing sales promotion system and I just stepped on something that could be probably handled with state machine pattern, but I have no experiences with state machines yet. Maybe the state machine is totally useless in this situation :) So I have a sales promotion which has some duration, some assigned customers, products, discounts etc. Each promotion also has it's state. There's about 5 states. The transitions between states are strictly defined - it's not possible to change state 1 to state 3 directly - user has to change state to 2 first. There're some limitations like "it's not possible to add more products when promotion is in state 3-5". Or limitations like "only super-users can edit promotion costs when it's in state 3-5".

I just read about http://www.codeplex.com/SimpleStateMachine , but I'm not sure if it isn't too complex for this case. I could handle the state logic in my service layer using things like:

if (promotion.state == statesRepository.GetState3() && false == loggedUser.IsInRole("superUser")){
   throw new PromotionStateException("user not allowed to edit promotion in this status");
}
...

or

public void ChangePromotionStatus(promotion, newStatus){
  if (promotion.Status == status1 && newStatus != statesRepo.GetState2()){
    throw new StateTransitionException("unable to change from status 1 to " + newStatus);
  }
}

But I don't like this kind of code - there must be some better approach :) Does anybody have an advice? I could separate the concerns of course and develop services like PromotionStatusChangeReviewService, PromotionEditPermissionService etc to make the code less coupled, but there's probably some better solution I can't see at the moment.

+4  A: 

Five states isn't too complex for a state machine, but I think you're running into some problems by trying to make a few of the transitions be handled specially or explicitly. Here's some tips:

  • State machines aren't helpful unless you can label the states in a meaningful way. "status 3" means nothing; you need to call it something useful like "Promoted", "Active", "Completed", et cetera.

  • Part of the State Machine pattern assumes that you have a detached entity that understands the states and how to transition between them. You shouldn't have a method like the ChangePromotionStatus() in your example, for instance, where it blows up if the state shouldn't be allowed. The state machine should simply prevent transitions that can't occur.

  • If the number of possible transitions is small and well-defined, and it makes sense to label them, I'd also recommend naming the transitions as well. This is probably especially useful if the transitions all do the same thing but in slightly different ways.

John Feminella
I simplified state labels to numbers for simplicity - they're named "proposed", "confirmed", "planned",....To "ChangePromotionStatus"-reaction - I understand the transition logic should be placed in specific class. I can't figure out how to combine it all together.I'll read more about SM,thx.
Buthrakaur
+2  A: 

John hit the nail on the head - you want your process, as modeled using a state machine, to be decoupled from your domain entities. Your domain entities probably shouldn't have direct knowledge that they are being used in a state machine, which will manage the job of transitioning them from state to state based upon certain events, but they will understanding the business meaning of each state they are in, and be able to enforce or apply business rules related to those states.

Ideally, your domain entities that are appropriate for being managed by a state machine will have some kind of status that acts as a state for the state machine, and will raise events which can be used by the state machine to determine when a transition is appropriate.

However, if it is indeed true that your states are completely sequential - i.e. they always go a-b-c-d-e or backwards and forwards exactly one step, I'm not sure a state machine does make sense because there is no special context needed to choose the next state - you can only go backwards or forwards, so any ordered list of states (such as an Enum) coupled with Next/Previous logic should suffice - but real world processes are rarely that strictly linear, even though they may appear to be at first blush.

Nathan