I can't recommend Akka's state machines enough. It's an incredibly simple and rock solid state machines built right into the library. ( http://doc.akka.io/docs/akka/snapshot/scala/fsm.html ) We're already on a Play/Scala backend so adding these was incredibly easy for us, but I think they're one of the hidden amazing features of the akka/scala world.
Designing state machines
11–20 of 58 posts
Re: Designing state machines
#12As the article does not really explain how state machines work let me try to do it here so maybe you will go back to your code and put one in place :) In general, if anywhere in your code you have some property/field called status/state/etc... that you update depending on events then chances are that a state machine would improve the robustness of your code.
Ok, so you have some states. For business apps this usually comes from business. For our dummy example we will simulate a day (using Haskell here for conciseness, but no worry, it's trivial and you don't need to know any Haskell to understand it):
data State = Awake | Dressed | Fed | Ready | Work | Cafe | Home | Asleep
Now most apps stop here and thus they don't have a machinery to control how to move between these. If we were to draw a graph where vertices are the above states and edges are how we go from one to another then, without any restriction, we would implicitly get a complete graph where you can go from any state to any state. Eg. from "Ready" I could go straight to "Asleep" without "Work" :) Our aim is thus to restrict transitions from one state to another to transitions that actually make sense. Making sense obviously depends on the problem we are solving. For our example let's introduce the following events: data Event = Alarm | Dress | Eat | GoToWork | Think | Yawn | Coffee | GoHome | Read | WatchTV
Now that we have states and corresponding events we can draw a graph where vertices are states and edges are events. The graph is now explicit in that we can only move between states (vertices) via events (edges). To put this graph into code, let's create a function that takes a pair of state and event, eg. (Asleep, Alarm) and returns a new state, eg. Awake: myDay :: (State, Event) -> State
As said above, myDay is a function that takes a (state, event) pair, which we can also call a transition, and returns a state. This function is nothing more than a lookup table, aka. transition table, where we will write down how to transition between states: myDay (Asleep, Alarm) = Awake
myDay (Awake, Dress) = Dressed
myDay (Awake, Eat) = Fed
myDay (Dressed, Eat) = Ready
myDay (Fed, Dress) = Ready
myDay (Ready, GoToWork) = Work
myDay (Work, Think) = Work
myDay (Work, Yawn) = Cafe
myDay (Cafe, Coffee) = Work
myDay (Work, GoHome) = Home
myDay (Home, Read) = Asleep
myDay (Home, WatchTV) = Asleep
myDay (_, _) = error "invalid state transition"
That's it, we have codified our graph. We explicitly stated the valid transitions and anything else is by definition invalid. We are pretty much done at this point. A state machine itself can be thought of as a generic function that takes a transition table, a transition and returns a state. This is only to decouple a specific transition table from the general machine itself, but it literally does nothing other than lookup whether the transition is in the table. If yes then it gives back the corresponding new state otherwise we get and error.You can see a pretty picture of the above graph and some code here: https://github.com/pwm/fsm
Re: Designing state machines
#13For anyone else working in ruby, I've grown to love the "Workflow" gem[1] for my state machines. It really helps to streamline things, and also has a built-in way to generate a visual of your class's states and transitions. [1] https://github.com/geekq/workflow
More generally I get worried about my ActiveRecord objects getting too bulky and business logic getting all wound up in post-transition callbacks and such. But that's something that can be managed with service objects and being disciplined about triggering state changes.
Re: Designing state machines
#14Re: Designing state machines
#15I have recently created some dead simple code to explain the concept to colleagues whom, as the article notes, were not using this very simple and effective construct at all to control state transitions in business apps. As the article does not really explain how state machines work let me try to do it here so maybe you will go back to your code and put one in place :) In general, if anywhere in your code you have so…
Re: Designing state machines
#16I have been thinking of using Postgresql CTE to do this. When a cycle occurs, I just create a new step to the dB.
Anyone from CRM startups who have interesting learnings here ?
Re: Designing state machines
#17What about the DB side of things ? How do you model the state graph in the DB ? I have been thinking of using Postgresql CTE to do this. When a cycle occurs, I just create a new step to the dB. Anyone from CRM startups who have interesting learnings here ?
If you were speaking about storing the instances lifecycles, a simple model using a RDBS is to store one row per transition event in a separate table. This is what the papertrail[1] gem does for example.
Re: Designing state machines
#18What about the DB side of things ? How do you model the state graph in the DB ? I have been thinking of using Postgresql CTE to do this. When a cycle occurs, I just create a new step to the dB. Anyone from CRM startups who have interesting learnings here ?
Re: Designing state machines
#19For anyone else working in ruby, I've grown to love the "Workflow" gem[1] for my state machines. It really helps to streamline things, and also has a built-in way to generate a visual of your class's states and transitions. [1] https://github.com/geekq/workflow
Looks interesting! I've always used https://github.com/aasm/aasm , but this looks pretty good. More generally I get worried about my ActiveRecord objects getting too bulky and business logic getting all wound up in post-transition callbacks and such. But that's something that can be managed with service objects and being disciplined about triggering state changes.
Re: Designing state machines
#20https://hoverbear.org/2016/10/12/rust-state-machine-pattern/