In today’s startup world, agile development and delivering new features to the user as often as possible is vital as it contributes to having more iterations and finding the right market fit.
To achieve this agility, smaller builds ready for daily deployments are required. This also brings with itself, more testability and maintainability that results in a more robust and scalable application.
Back in 2016, the Metigy frontend app was a big monolithic React app consisting of several sub-applications. Each sub-application was responsible for a module in our product offering. Within each sub-application, hash routes were used to access different sections. Even though there were some parts of code shared and used in different sub-applications, each one was working sort of separately and independently from the others. However, all sub-applications were structured in a single repository without any private npm packages. You can imagine a structure similar to the below image:
There were several issues with that structure. The following list is just to mention a few:
- Long build times as we had only one build process for that big repository. This resulted in having to build and deploy the entire app with all sub-applications for every deploy even for a change as tiny as a typo fix.
- One deploy every few weeks as the build and deploy process was such a time-consuming and cumbersome process.
- Prone to breaking unexpected areas because of the shared code between sub-applications. Which itself contributed to lack of confidence for each deploy.
- We had a less maintainable code and more coupling.
Plan of Action
So, we started to think about ways of restructuring our application in order to solve some of the aforementioned issues for our future large-scale react app.
We started by moving each sub-application out into its own package but we didn’t stop there. Eventually, it was broken into four layers as shown below, where each layer could use packages from the same layer or beneath layers.
We took the following steps to break down the monolith:
- Break sub-applications into multiple repositories.
- Set up a pipeline to automate build/deployment.
- Support for release deployment to a selected subset of users. We achieved this by having the latest stable version as the default release for all users, with the possibility of overriding the version per user.
- Begin new projects in a new repository whether to be a library to be used in an application or to be an application itself.
- Create a design kit and implement basic elements (without business logic) that conform to the design kit. This ensures consistency throughout the application.
- Develop an API library for communicating with the server where all sending and receiving data as well as caching logic resides.
- Create a layer of more complex self-contained components with simple props that can easily be used by other parts of the application.
- Set up a development environment per repository. in order to develop faster and easier. (more details)
- Develop mini-applications to encapsulate the complex parts of business logic in separate repositories.
And now, with this new react app architecture, we build applications that are composed of primitive and complex elements as well as mini-applications.
The biggest outcome of this breaking down was, of course, ease and speed of development and deployment. But, that wasn’t the only benefit. In the table below, a couple of factors that improved in this process are listed:
This new approach has gone a long way to improving our app and processes, but this is not the end. There are still challenges we face and many more improvements we can make to make it all even more scalable.
For example, despite the fact that build time has decreased a lot, it’s not ideal yet. Moreover, if a change is made in the nested libraries, all related repositories up to the application level need to be built and deployed which can be cumbersome if the change is many levels down.
We’re thinking of ways to automate these processes so stay tuned for our future updates!