In my post about SymfonyCon a while ago, I went to an interesting talk about state machines.
I have since used state machines a few times in e-commerce. I read a StackOverflow post that shows how to implement a state machine in Java.
I thought it would be interesting to couple this idea with JPA’s enumeration support so that it is easily persistable. There is actually a Spring State Machine project, but if something simpler could work, it is just too heavyweight.
First, I created a
Product entity (getters and setters omitted), which stores its state through an enum called
If you just have the H2 database dependency in your project, this is enough for Spring Boot to tell Hibernate to automatically generate the database definition, as documented in the Spring Boot Common Application properties reference.
spring.jpa.hibernate.ddl-auto= # DDL mode. This is actually a shortcut for the “hibernate.hbm2ddl.auto” property. Defaults to “create-drop” when using an embedded database and no schema manager was detected. Otherwise, defaults to “none”.
Next we have the
The idea is, is that the enumeration can be loaded from the database, and then can respond it through the
process function. The events themselves are represented in another enum
The events are imperative actions. I have used a
switch statement so that the states can respond to events. I have also seen this implemented using a table, which might be clearer, however it works for now.
One of the benefits of a state machine is that it is easier to debug than a long collection of
else statements: you can look at the state in the database, and go directly to the state in the code, and look at the route it is allowed to take.
In the GitHub repository, the state machine can also handle the pathway of two failed deliveries, which results in the product being returned to the business. This avoids the need for an extra column in the database, such as “delivery attempts”, and the logic to branch at a certain number of attempts, as the product itself “knows where to go”.
All the client needs to do is send
ProductEvents to the enumeration’s
process function. I encapsulated this functionality into the
Product entity itself to follow the “tell, don’t ask” principle.
Have a look at the Github repository for a the whole code sample and some tests.