State Machine
State-machines are a powerful framework for defining business processes. They are fundamental in understanding how we model work in our applications. One of the main principles of NS theory is separation of states which essentially states tasks should be transparent in their execution. Interaction and execution of tasks can be modeled very clearly using state-machines.
Fundementals
There are two basic components; States and Transitions. State belongs to a specific object and describes the current status of that object. Transitions define all changes in the status of an object we want to allow. In a software system however it is not always possible to have atomic transitions. This means the application produces side effects while transitioning from one state to the other. Consider the state-machine of a door below:
While opening the door it gets stuck halfway somehow, which means we are now in an undefined state! This can be resolved by either closing the door again or by defining a new state in which someone has to look at it and fix the issue. This problem is not unique to this example of a door, and in general every task which performs some level of work has this same problem. This is why we will use a slightly different definition for transitions.
Now our transition itself is also stateful, by indicating it is in progress through an interim state and indicating it failed through the failed state. In this example 'OpeningDoor' is our interim state and 'OpeningDoorFailed' is the failed state. By defining these additional states, we can at any point employ the correct strategy. Is the door opening? Then we should probably not interfere with it. Did we encounter a failure? Then we might revert to our begin state, 'Closed', or define another transition to resolve the issue.
Initial state
A state machine should always have exactly one initial state to indicate the starting point of execution. A common mistake is to define state machines with effectively multiple initial states. This means there is some external process making a decision, negatively impacting the locality of behavior of the current state machine.
The state machine should always make such choices itself based on the available
data. Consider a registry of packages being sent with a nullable field sent_on
and the following state machine:
Instead of defining two initial states (Processing
/Sent
) we have a single
initial state. The state machine itself can now decide based on the available
data (sent_on
) which transition to take. This ensures all decision logic is
captured by the state machine and not provided by some external source.
Branching transitions
In the previous example 'OpenDoor' has only one successful outcome in 'Open'. It is however possible to have multiple outcomes, depending on anything that can happen within the executed task. We call these successful outcomes end states of which there should always be at least one. When a transition has multiple end states we call it branching. Note that the failed state is only representing technical failure while functional errors are represented as a successful outcome of a transition.