Finite-state machine in web-development

In this article we will consider simple finite-state machines, learn which problems may be addressed using them, skim through its theory and finally consider an example application of the technique using Symfony framework and a few of its bundles.

What is it?

A finite-state machine is an abstract mathematical entity which has some finite number of states with all the transitions between them being fixed and strictly determined. One may look in Wikipedia for deeper theory.

Finite-state machines became common in such fields as complex user interfaces, artificial intelligence, games and other challenging areas.

Introduction

Let us imagine a simple blog. Any blog engine must have a feature to add a new article. Before the article is published it is created as a draft. Let us picture this process:

The “publish” method may look like this:

In the first place, we should check that the article is a draft, i.e. it was not ever published. This is essential to disable duplicate publishing, thus eliminating unwanted operations in the future. Subsequently we set publication date and the new state. In a nutshell, that is all, since the entity may be only in two states.

Then, let us consider a more complex example which is taken from a real project. There is a new user registration scenario. New user goes through several steps during registration:

  1. Filling out a form;
  2. E-mail confirmation;
  3. Mobile device binding via QR-code;
  4. Registration completion.

Assume that we need to implement a method which sets User state. The method should check user’s current state and ability to switch it to a new one in accordance with all business rules. For example, it should deny mobile binding if the e-mail is unconfirmed. Similarly, it should reject repeated confirmation of a previously used e-mail. It is possible to use switch statement to implement this method:

The other way is to use a conditional statement:

One may notice that the implementations suffer from some drawbacks:

  1. The method must be changed if business-rules change.
  2. The code is poorly readable.
  3. As the time goes by, implementations get spread over the project, which in turn can lead to errors.

Let us try to address these drawbacks using a state machine concept.

A finite-state machine is a general concept. Exact implementations may differ depending on a programming language being used.

We will use PHP and Symfony framework with StateMachineBundle for this example. There are some other excellent bundles for state machine implementation on Symfony, which we will review at the end of this post.

Let us consider an online store. One of the key scenarios of an online store is a checkout. Before a checkout could be possible the user must enter one’s contact details, choose payment and delivery options. Notably, each step may change the order’s final cost.

This process may be presented as a multistep diagram. It is important that it is possible to return a step back from any step, which in turn may alter the final price.

We may implement this scenario in terms of a finite-state machine:

Our finite-state machine has 5 states and 4 transitions. Each transition is described by a list of initial states (from) and a final state (to). For instance, an order can be paid only if a payment method was chosen. In turn, a payment method can be chosen only after a delivery option is selected, and so on. This way the order state is valid all the time.

Now we should look at the client’s code. It is necessary that business rules are observed and the transition is valid. It is easily done using an Exception:

Our implementation has convenient ways of state validation. We may use one of them:

Evidently, this approach has advantages:

  1. The workflow is plain and simple, the code is clear, it does not require additional comments.
  2. It can be changed quickly and easily.
  3. It is suitable even for really complex tasks.

Symfony Bundles for state machines

There are several bundles implementing state machines on top of Symfony framework. The most popular ones are:

  • Workflow. This component was added in Symfony 3.2 release and is the obvious first choice for the most of new projects. It has an excellent integration with other Symfony components, supports such features as events and multiple states and has implementation of both workflows and finite state machines.
  • StateMachineBundle. This bundle is the simplest lightweight implementation of state machines. Unlike the rest, it does not support events or multiple states, but is compatible with 2.x and 3.x versions of Symfony and may become a good choice for projects that are based on LTS versions of Symfony.
  • Finite. This is a full-featured alternative for the standard Workflow component, that provides some additional interesting features, for example, transition properties that can be passed to corresponding event listeners on transition.
  • Unofficial backport of Workflow component for Symfony 2.3+.

Conclusions

A finite-state machine is a reliable, proven and advantageous tool to setup application’s logical structure. This approach allows the code to be clean and simple, therefore spend your time to learn how it could be implemented using your favourite programming language.

Useful links:

http://symfony.com/blog/new-in-symfony-3-2-workflow-component

https://github.com/winzou/StateMachineBundle

http://finite.readthedocs.io/

https://github.com/jakesgordon/javascript-state-machine

--

--