Delegation Flow Implementation with Spring Statemachine
Hello World :)
When developing applications from scratch, firstly we need to decide which framework will be suitable for our business and technical requirements. It is generally not easy to find a proper design approach to start, in case if we did not develop any kind of application similar. We better understand the importance of designing the project with a suitable framework as the code base gets more complicated over time.
For these reasons, in Garanti BBVA as an agile team, we started to search what design approaches are used in similar projects with ours that we will start to implement.
By the way, our aim with this project was to transform all commercial loans proposal delegation flows into a dynamic structure supported by a decision engine.
We ended up our research with state machine design principle implemented in sort of projects that include business flows. I will explain it in this article but briefly, state machine helps to define workflows easily in three concepts and organize transition between states in a flexible way through events.
There are different frameworks that implement state machine concepts but we decided to move forward with spring statemachine because our in-house architecture is based on spring. Even if we were not dependent on spring, we would choose spring statemachine because it provides features to make implementation easy and powerful, and also as all we know spring itself is a powerful framework and has important features like AOP, transaction management, dependency injection, security, etc.
This is how our adventure with spring statemachine began :)
I will share my experiences on this framework at an introductory level. I believe this article will be useful for those who want to learn what is Spring statemachine, where, and how to use it.
Spring Statemachine is a framework for application developers to use state machine concepts with Spring applications.
This is how Spring defines Spring statemachine, in order to understand what Spring Statemachine is from this definition, we first need to know state machine concepts in the description.
According to Wikipedia state machine is
an abstract machine that can be in exactly one of a finite number of states at any given time. The FSM can change from one state to another in response to some inputs; the change from one state to another is called a transition.
As we see from the Wikipedia definition, the state machine brings us three concepts that I mentioned before. These are state, event, and transition. We can take the definitions from above to understand them.
State: One of a finite number of states at any given time.
Event: Triggers the change from one state to another.
Transition: The change from one state to another.
If the project we will develop includes these three concepts, we can apply the state machine principle.
We can find the concepts in the following examples;
- coin-operated turnstile: locked, unlocked
- traffic lights: green, yellow, and red.
- kettle: on, off.
- online shopping website: created, packaged, shipped, delivered.
I wrote down only the states of the above examples but I will explain coin-operated turnstile in three concepts to get a better understanding. Let’s think if it is locked and someone puts in a coin, then it becomes unlocked this change is a transition, and putting a coin is an event that triggers the change. If it is unlocked and someone turns the turnstile, then it becomes locked this change is also a transition, and turning the turnstile is an event.
The below diagram is another example for the state machine, I will use this scenario to code in spring statemachine. This also reflects a very small part of the actual scenario we implemented in our project.
First, I would like to explain this diagram in three concepts. The rectangles are the states, we have a finite number of states and each one is a state as we learned from the definition so there are five states here. The arrows that are the transitions show us a path from the source state to the target state. The events trigger the transition represented by labels.
Having such a diagram makes it easier to see the big picture and tells us the key points. I highly recommend creating a diagram before development especially if the business flow is complex.
Let's see how to code this diagram in Spring Statemachine :)
Maven dependency
Our build tool is maven so the first step is to add maven dependency in the pom.xml file. In case of using Gradle as a build tool, the Gradle dependency needs to be added to the project, we did not cover it here.
The project is also based on java 8 and Spring Boot.
State Enum Class
After that, define all states we see in the diagram with rectangles in an enum class.
Event Enum Class
Define all possible events in another enum class. In the diagram, we see them with labels. Send event appears two times but it is not necessary to add that in enum class two times. The class has to include all events just one time even if the same event is used more than one for flow.
By the way, spring statemachine supports enum and string type for defining states and events, enum is recommended for type safety.
Configuration Class
The next step is to configure the state machine.
The configuration class needs to extend StateMachineConfigurerAdapter because we will override methods from this adapter class.
After definition mark the class with @Configuration to be loaded by spring context. The other annotation @EnableStateMachineFactory starts an instance of state machine depending on business logic. An instance of the state machine is not created immediately on startup but started through the factory. @EnableStateMachine is an alternative way for starting an instance of the state machine. In this way, an instance of a state machine can be built and started immediately on application startup.
Configure All States
In configuration class, our first method is the configure takes StateMachine StateConfigurer as input and all states must be added in this method. Initial and end states are optional. For example, a coin-operated turnstile does not have an initial or end state, in this case, there is no need to add initial and end state in the configuration method. The initial state is assigned immediately after the state machine is created. End state applies the end of the life cycle of state machine instance.
Configure All Transitions
This second configure method includes all events and states responsible for building all transitions. It has to take StateMachineTransitionConfigurer as an input. We need to add all paths required to cover all flow. Every external transition needs a source and target event. Some transitions can be triggered by the same event names. This should work until there do not exist confusing doubled outgoing transitions.
Configure Listener
The third and last configure method here takes StateMachineConfiguration Configurer input. This method aims to listen to all state changes and we can trace transition logs in this way. The listener is built on a separate adapter StateMachineListenerAdapter. Spring statemachine uses the Spring event-based infrastructure. The state machine sends context events via StateMachineEventPublisher. So this method is not a good way to do repository operations. if we need some updates on the table, we should use an interceptor class.
Interceptor Class
After having the configuration class we will define an interceptor I just mentioned above. The opposite to the listener concept, the interceptor breaks the state change. So we can do repository activities in the interceptor class. Basically, all state changes include common updates to the repository therefore we can add the common code to this class and avoid writing the same code over and over in different classes.
The interceptor extends StateMachineInterceptorAdapter so we can override methods from this adapter class. I put only postStateChange method but other methods included too.
These are some of them;
- postStateChange: Called after a state change.
- postTransition: Called after of a transition if transition happened.
- preStateChange: Called prior to a state change.
- preTransition: Called prior to a start of a transition.
Business Service
After the definition of the interceptor, we finally come to the last topic of how to use state machine in business logic.
First, the service class injects stateMachineFactory and stateChangeInterceptor. We remember these two classes above, we injected;
- stateMachineFactory in order to create an instance of state machine
- stateChangeInterceptor to tell state machine as it will be used for intercepting.
Let’s have a build method that includes the implementation of these two.
This build method first creates a new StateMachine instance with a given id from stateMachineFactory then initial state DelegationState.INITIAL is triggered.
After that stops state machine then adds the interceptor class to the state machine and resets it to the given state, remember the first state was the initial. Finally, the state machine is started. Now it is ready to use.
The last part of the code is about how to provide a transition.
The code defines a message that is a standard Spring messaging framework object where the payload is of type Events, an application id is set to the header of this message. Then it calls sendEvent on a state machine that triggers an event on the current state and starts a transition. Finally current state changes to the new state. While this is happening, the interceptor class engages in the activity regarding interceptor methods.
That's all :)
Thanks for reading, you can find all codes in my GitHub account.