Our experience in moving from a monolith to modular app in order to save time and build greater flexibility.
Every enterprise application typically has its own identity, including a theme, style guide, reusable components, services, custom controls, and more. Challenges can arise as the application grows and you realize that a lot of functional code from these components and services have already penetrated throughout the app. This makes it difficult to reuse or extend these elements if you want to migrate to another technology, or use a different third-party library, or even if you simply want to upgrade versions. And what if you start working on a new project and you do not want to develop everything all over again? The solution for us was to move from a monolith to modular app by building our own private registry.
Why build a private registry?
As our applications continue to grow, we wanted greater flexibility to add and edit new components without the risk of affecting other areas. Developing a private registry is not only a great way to store all these elements outside of the app, but it also makes it easier to independently develop, test, maintain, and even extend these components with minimal impact on the downstream code.
Our goal was to build a private registry that would allow us to add components without affecting code and to give us more flexibility in the future. Keeping in mind all the above challenges, we were making an effort to completely isolate the non-functional code from the functional code.
First, we created a new code repository for this and we tried to move all relevant code here. We moved custom controls, pipes, common services, and many more. It was important that these items did not have any functional code, and if they did, we had to refactor them. Obviously, this stage was challenging but, in the long run, we knew it would save us significant time with new projects.
Then, a new DevOps pipeline was created for this repository to publish code in our own private NPM registry. This registry was then referenced back in the front-end application and we fixed a couple of references.
Now we were ready to go.
Be prepared to invest time into this initiative. Here are some of the challenges we faced that took more time than we had originally anticipated.
Identifying the code to be moved out of the product.
Refactoring the existing code and separating the functional and technical code.
Since we were moving code in a staggered way, we had to change the product code many times and the whole team had to install the latest packages.
Our recommendation would be to invest more time into the planning stage to ensure a flawless execution.
Once you build new controls or services you need someplace to check them out to make sure they are ready to be used everywhere. This is where we added the playground app and where we keep a list of all the items that are in our private registry, their capabilities, different ways of configuring and styling them, and also where we keep the code sample on how to use it. Basically, this is the complete documentation of your code with the demo of all components, controls, and their different customization options available.
Technology Stack & Lifecycle
Version control: Since this is a separate repository and referenced by other products as an NPM package, different products can refer to different versions. So there is flexibility in upgrading products when you upgrade components.
Upgrade or downgrade at will: As mentioned, since products refer to this library by the version, you can easily upgrade or downgrade without having much downstream impact.
Fail fast: This is the new age mantra in development. If you are creating a new component you need to test it fast. If you have to test against the live product then you will need to spend more time adjusting the code to suit the application ecosystem which might be time-consuming. And if there are changes, it’s going to be more challenging.
Easier to migrate: If you have wrapped up any 3rd party tools, then it is easier to migrate to some other 3rd party tools or components without much downstream impact. All you need to do is update all projects with the newer version.
Easier to upgrade: Just like above, it is easier to upgrade to a higher version. If your code works fine and passes all tests in the shared library project and looks good in the playground, then you are good to go. All you need to do is to update all projects with the newer version.
Separation of concern: By separating technical and functional code, the application becomes more stable and easier to maintain.
Code maintenance: The above point obviously gives you better code maintenance.
Better code coverage: Better code separation results in adding unit and integration tests easier which will give you better code coverage.
Overall, this process took about 3 months to complete. Since building the private registry and playground app, we have already seen significant improvement in our processes and have greater confidence in the code coverage as we continue to grow our application. This was a challenging exercise, but in the end, we could achieve a lot more than what we anticipated and we strongly recommend moving from a monolith to modular app.