If your company spends time and money every couple of years to refactor Frontend projects because it turns out to be a complex system that nobody wants to maintain anymore, impacting innovation and therefore causing the loss of its best talents, this article is for you!
Commonly, all companies I have worked on in the past used to have large front-end projects with multiple businesses flows within a single application called “CMS” or “Portal”. These projects are usually complex, and making any update or technical evolution of the application is often very difficult (we know that in the front-end universe, evolution happens constantly).
Today we know that there is a real need to create large-scale front-end applications that are easy to maintain and that allow us to innovate within the project itself and its business constraints. 🤔😥
We are creating a scenario:
— Your team starts to develop a large e-commerce platform and you, along with the team, decide to use a certain framework to speed up the development of the platform. However, after some time working on it, a new version of the framework your project relies on is released and to top it up it includes breaking changes. 😥
Thinking of a monolithic application you have only two alternatives: not to update the framework or to refactor all points where that particular update affects the project.
In some cases, depending on the project, you cannot refactor the system as you do not have enough time to carry out this demand. This ends up generating a large technical debt in the project, since, as it is not possible to refactor, you will develop the other half of the project — including new technical debits. 😥
It demonstrates how not paying technical debt gets into a huge snowball that, once it gets big enough it is impossible to stop.
To solve this problem, I believe that front-end projects can use architectural models such as microservices. By creating several apps and using the composition concepts, we start to compose our various applications into a single application. 🤔
To achieve it, we need to understand how to separate our applications and how to identify our bounded contexts.
As Martin Fowler mentions in his https://martinfowler.com/bliki/BoundedContext.html, bounded context is a central pattern in Domain-Driven Design. It is the focus of DDD’s strategic design section which is all about dealing with large models and teams. DDD deals with large models by dividing them into different Bounded Contexts and being explicit about their interrelationships.
I will use as an example of an application that uses pure HTML pages as below:
The application at this time seems to be quite simple, but, the Product Manager comes up with a new requirement that only authenticated people should have access to the path /common-questions
You’ll probably break down the solution in the following steps:
- create a sign-up page
- create a sign-in page
- create a page to recovery password
- create a page to confirmation recovery password
- create integration with backend authentication service
Ok. Sounds cool. Can you see how all of the above features relate to the same context? Yeah, that one: authentication!
This is how our new bounded context looks like 🚀
At this point, given the complexity of the new features, your team decides to drop the implementation of pure HTML for new features and migrates to Next.js instead. Therefore, you have two options:
- Reject Next.js implementation and keep using your legacy code. 😢
- Refactor legacy code.
None of them are interesting, because rejecting the team proposal means that you have decided not to innovate in your application, and refactoring this small application, fine, but suppose it is a large application, it will take a long time that your team may not have. You need an alternative to composing your application without affecting the overall solution.
You have a third alternative which is to use a reverse proxy to coexist two applications using two different technologies
Creating a Reverse Proxy
In that case, it is necessary to create another application with a subdomain called auth.singleapp.com; after that, create a proxy on singleapp.com to define when the user accesses the context of the authentication path he sees the application hosted on auth.singleapp.com
Good, Now you have the authentication application completely isolated, which means that you can dedicate only a small team to maintain that application and they don’t need to know other contexts of the business. This team can now focus on creating an incredible user experience in this business domain.
This approach may lead us to have N applications running in production, which may produce different problems. In this case, we have two main issues:
- How is communication between applications?
Do I need to request a login on each application? Users need to be authenticated to access common questions.
- How to create reusable components between all applications that are easy for all teams to use?
To solve communication problems, we thought about creating a single application to solve the proxy configuration problem for all applications instead of sharing these proxy settings. This, based on our experience, is a nightmare to maintain. It works by using a single domain like singleapp.com, which allows child applications served within the same domain to use the same localStorage and therefore avoid any potential CORS problems.
This single application can be any application server like Nginx, IIS, Apache Tomcat, Cloudflare, Candy, etc.
To enable reusable components, you can choose to use a common style guide or create a style guide for your company. This is the best way to solve this problem.
Each application can be written using a different library or framework; the first application can be written in Elm, the second in React, Angular, Reason, Vue, etc. This decision depends on the problem that you’re trying to solve.
Note: this approach is very similar to the concept of micro-frontend mentioned in this site https://micro-frontends.org. In our case we are developing with different technologies living together in the same application, instead, they are different applications.
I’ll be covering micro-frontends in a future article 🚀
The proxy approach allows us to have an architecture which has:
- Technology is agnostic.
- Clear team isolation, which facilitates business understanding.
- Each team is responsible for its business context, for example, Authentication, etc.
- Relatively small applications, which makes maintenance simpler and more agile.
- Any new team member with a small amount of effort can quickly adapt to the project.
- Independent deploys It does not impact risk other applications, offering more security and reliability when publishing a new feature or hotfix, bugfix, etc.
- It Facilitates long-term platform evolution.
- Motivates the team to seek more innovation and new ways to solve business problems every day.
- The team feels more motivated, reducing the search for innovation opportunities in the market because you have the flexibility to innovate.
- The platform is in a process of constant evolution.
- It’s necessary to have a deep understanding of how the web server and proxies work.
- It’s harder to spin up the entire application because it requires the use of lots of applications
- Since there’s a single point to display our applications, there’s the need to create redundancy and guarantee 99.99% uptime of the proxy server.