State Machine As Redux Middleware

Ofir Attia
Geek Culture
Published in
7 min readApr 6, 2021

Nowadays, Web Applications becoming more and more complex, the demand from customers to create complex flows increases to improve their business and user experience for their clients.

When the product owner designs a flow, He will probably start with a simple diagram, this diagram will have a starting point and endpoint and He will state what conditions apply when moving from Step A to Step B.

When the developer implements the flow, he needs to think about where to put the right logic to fulfill the requirement, When the client click on button A he will be redirected from Page A to Page B or Scenario A to Scenario B — This is fine, for one flow, flat one, but it’s not the case today.

The flows today are complex, one flow can evolve to many flows, the user don’t know and don’t care how the flow is being managed but for developers its important because as developers we need to know how to handle the flows in a way that we will able to maintain it, an example would be that product owner request to change the steps in a flow or change the business logic of existing flow, how fast? or how easy we will able to fulfil such request?

In this article you will see the best way to manage your flows in your application, How to create the harmony between product owner, customers and developers and en-force them to talk the same language.

Basics

In the introduction, we talked about moving from Step A to Step B and on the fact that every flow has a starting point and end point, its reminds you something? if not, I will remind, its sounds like talking about State Machines.

A finite-state machine (FSM) or finite-state automaton (FSA, plural: automata), finite automaton, or simply a state machine, is a mathematical model of computation. It is an abstract machine that can be in exactly one of a finite number of states at any given time.

https://en.wikipedia.org/wiki/Finite-state_machine

When thinking about a model that we can leverage to handle complex flows on the UI, this is the first thing that came.

When we are planning our Web Application, we need to think about all the possible scenarios for our end-users, we don’t want a dead end, in fact, we need that every step will take the user to the next step according to the our business logic.

Our solution based on an open source library that manage state machines, XState — you can read more here: https://github.com/davidkpiano/xstate

So we have a library that manages state machines, now what?

Since we are using modern technologies for developing our web applications, we will need to create the integration layer between this library by enhancing it and connect it to our technology, in this article I will talk about the open source that we developed that connecting this library to our Redux based app, in general, this state machine will be managed in our Redux Store, What do you think? starting connecting the dots? Let’s continue.

Redux Flow Manager

Our open source deal with managing complex (or simple) flows, it converts our flows, our steps and their conditions to a state machine, with this approach we will able to know on every single step, what is the next step, what is the business scenario that the user located on and what is the flow — this will enable the developers to take care for the business logic only and not handle the transitions or what to show or not, where I should place the redirection for the user and where not.

How you should use it?

Step 1: Add the flow manager reducer to your project.

Step 2: Create the Steps Configuration file — steps config define the set of steps for each sub flow types.

Steps configuration define for each flow and sub flow, the set of steps that the user will need to complete in your application.

Step Object Properties:

  • key: flowType (Example: COP) - the key represent the flowType
  • key: subFlowType (Example: planOnlyFlow) - represent the subFlowType
  • steps: array - set of steps for this flowType and subFlowType

The order of the Sub flow objects in the object is important since the calculation will run by that order. For example: if you define planOnlyFlow before planOnlyFlow,changePlanFlow, then planOnlyFlow always will be always set before planOnlyFlow,changePlanFlow.

Step 3: Create Flows Configuration file — flow config file define the sub flow types name and the conditions that should be meet to make a sub flow valid.

Flows Config is an array of object, that each object define the sub flow that may be in your entire application. Each flow object have conditions array and each condition defined with a callback. The callback can check anything related to the condition, if the condition you check is fulfilled then it will return resolve, if the condition is failed then it will return reject.

If all conditions of a sub flow pass, then this sub flow added to the subFlowTypes array.

Flow Object Properties:

  • flowName: string - unique name of the sub flow type
  • runInFlowTypes: array<string> - define the list of the main flowTypes that this set of conditions will run in
  • conditions: array<condition> - an array of Condition Object
  • conditionName: string - the name of the condition
  • onCheck: function - a promise function that return resolve when the condition success and reject if the condition should be failed
  • mandatory: boolean - optional property. define if to remove that sub flow from the array if this condition failed. true by default.

Each Condition function get the following props:

  • state - the current state in the store.
  • context - the current context in the state machine.
  • event - the state machine event who invoke that condition

NOTE The Order of the Flow Objects in the array are important. The calculation will run by that order.

Step 4: Call CreateFlowManagerAPI with your store, reducer slice name, flows configuration and steps configuration.

Step 5: CreateFlowManagerAPI return an instance of Flow Manager with functionality that will help you manage flows in your app.

Can you think what would be the next step? feel free to answer on the comments.

Photo by Tatiana Rodriguez on Unsplash

Simulating the State Machine

with XState you can check your configuration, simulate the conditions to see that the flow defined exactly as you planned.

By calling getMachineFlowConfig() of the Flow Manager Instance it will return an XState config that can be set in XState visualizer to see your state machine created by your config.

You can try by yourself here: https://xstate.js.org/viz/

Photo by Leon on Unsplash

Talking the same language

The idea of creating a language between product owners, developers and the customer is to reduce the effort of requesting a change (customer), plan the change (product owner) and implementing the change (developers).

Let’s take a look how we should define a system with evolving flows, how we define the steps and how we define the conditions by designing the system as following, it will ensure that the implementation will be focused only on the business logic.

Reusing

You probably noticed that steps are being shared between the flows, same apply for the conditions, for example isUserLoggedIn can be a condition that will be used in multiple places so once we change this condition it will effect all others, again, we want to reduce the effort, we want to save time those will enable us quick-time-to-market.

Want to understand how to utilise this feature in your project? feel free to contact.

Wrapping Up

This article should give you what is the right approach to manage complex or simple flows for your web application to provide the best architecture to your app.

Who am I?

I am Ofir Attia, a Web developer, focusing on design and architecture. Currently working at Amdocs as Software Architect.

Contributing

Special thanks to my co-worker Refael Oknin, Refael and I develop open-source libraries together for the past 2 years.

the Redux Flow Manager feature comes as part of the harmony-boilerplate that we developed.

Want to know more about Harmony? read the following article.

--

--