An overview of how the UI architecture at RIVIGO evolved from Angular JS to React JS and then from Plain Flux to Redux.
In an agile engineering environment, working on an iterative model is critical for both pace and innovation. This highlights the need for ample experimentation with regards to product development. This can often lead to situations where learnings from the first version of a product imply the need for a completely different approach to building the next version. Hence, all elements of engineering need to be designed in a way that support a technology development culture which is fast-paced and ever-evolving.
When considering our UI architecture, we had the following objectives to meet.
Our technology stack in 2016 was as follows.
Creating small SPAs using Angular 1.X was blazing fast and easy to build. However, when the application’s code and business logic complexity increased, it had the following limitations.
Upon identification of these issues, we initiated work on ReactJS. Plain flux implementation was our first choice for state management. However, the following issues surfaced with this approach.
We concluded that Flux served more as an implementation guideline and not a full-fledged state management system. This is why and when we evaluated Redux over plain flux. We also determined the part of the code that should be abstracted out to not let developers choose their path of writing code.
Since React JS is best suited to our case, we continued to work on it. As mentioned above, in order to have a proper state management system in picture, we replaced Flux with Redux which helped reduce boilerplate and increase abstraction. The following were charted out as key tenets in the process.
The diagram below shows the overall Redux architecture.
We have the following folders at the root.
A component comprises of all reusable components which can be used across the product. A single component structure is marked in the picture given above.
We kept all the component’s dependencies i.e. actions, reducers, sagas, selectors, constants etc. in one folder. We followed this practice over keeping separate folders for actions/reducers etc. This ensured that the component is 100% reusable.
A container acts as a controller for each application. The main container i.e., the App container handles all the routing for the application. Other containers are specific to applications and follow the same folder structure as that of components.
The architecture shown above solved the following problems for us.
Elimination of boilerplate code: Redux eliminates the need for writing watchers for a store. Once the components are connected as per our requirement, Redux does that by itself. For seeking a part of the global store for a single component, we needed to write the mapStateToProps function. This can be a repetitive task for each component. While it is important to use this in some cases, in majority of the cases the reducers can be designed in a way that mapStateToProps always fetches the entire reducer.
With the above component structure which ensured that each component had its own reducer, we did not have to write boilerplate in mapStateToProps function. We used the following smart function for retrieving a reducer from the store.
The snippet below shows how we deal with some custom scenarios where the component requires data from more than one reducer.
Abstraction of concerns (Adaptability): With the folder structure given above, we defined the abstraction around the approach to creating a component.
We used the same architecture for more than a year until we sensed some technical debt being created.
These problems led to the inception of our own NPM module that called rivigo-ui-commons.
We created a new repository that had the following key components.
We have used several tools and libraries to build this NPM module. The entire commons module was reorganised to make it a scoped module with versioning of each individual component.
We would be coming up with another article in this series to share how we built our commons NPM module and the roadmap for further improvements.
One of our key leadership principles at RIVIGO is ‘1% improvement every day’. The evolution of our UI architecture is a perfect testimony of this principle. The journey we undertook from Flux to Redux to building our own NPM module and forward, has helped us build a solid foundation for building agile and robust engineering practices. The key impact areas have been elimination of repeated code, 100% code coverage and scalability.
In the next article of this series, we will cover the details of the architecture of our NPM module.
More stories by Aman Juneja