Micro Frontends: How It Changed Our Development Process
All the frontend applications are monolithic in nature. However, the “micro frontends” keyword is very popular in these days.
But what’s wrong with traditional-monolithic frontend applications?
- Even if you create awesome microservices on the backend, your frontend is still standing as a bottleneck.
- Working on a large, monolithic codebase is hard to manage the development, test, and deployment pipeline.
- When growing your application, loading performance is decreasing and application size is growing day by day.
In Trendyol, our goal is to create smaller, flexible, and maintainable codebases. Working with more scalable organizations with decoupled, autonomous, and small teams. With these goals, I’m going to explain how we created micro frontends over our legacy application.
Case Study: Trendyol Seller Center
In a nutshell, Seller Center is a single page application that offers the following features to our sellers. Manage their products, promotions, and coupons. Tracking orders and claims. And a lot of engagement features such as; dashboard report, notifications, seller store, ad management, etc.
With these features, the frontend team was growing and its area of responsibility was increasing day by day. Here is our modular frontend project.
We have followed these steps when splitting our frontend project.
Defining The Domains (Micro Applications)
When deciding your micro applications, you can follow domain-driven design(DDD) principles. Because DDD provides clear direction for managing backend projects, some of those practices may helpful for the frontend, too.
For example; according to our menu we can separate our applications like this “product”, “orders”, “payment and invoice”. (I’ll continue on this parts in the rest of the article)
OK, we defined micro applications but we have some problems. Header(menu) is a common part of all applications. You can fix this problem in three ways.
- Duplicate the header on each application.
- Create and publish an npm package like a component.
- Create a header application as a micro-app.
We don’t want the first two options. Because, if you duplicate the header component for each application, you have to implement all features and changes to each application. And if you publish your header to npm, version tracking will be difficult for each app.
The third option is the best for us. Now, we split our application as a horizontal, which allows multiple micro frontends per page. We need to load and compose two applications on a browser.
Separate The Common Parts
Each application using common components, utility functions, and similar packages. Before starting the split our codebase we separated them and avoid code duplication.
Create and Composing The Applications
There is two option to compose a micro-frontends on a browser:
Server-side composition
This pattern is most useful when you are using SSR. With this approach, the micro-apps composing and rendering on a server and serve to the client. We can give the following examples to this approach.
- Zalando’s Project Mozaic
- Airbnb’s Hypernova
- Trendyol’s Puzzle JS
Our storefront team using Puzzle JS on a production. It’s an awesome framework for this approach. You can also check it here.
Client-side composition
In this pattern, some application shell(stitching or bootstrapper layer) loads multiple micro-frontends directly as a js bundle. This way, the loaded js files append the DOM nodes and initialize the JavaScript application.
We chose this approach because our application working as a single page. We don’t want to create and send some HTML on a server-side. Here is our architecture’s diagram;
We have to create a manifest file for micro applications before loading and initialize steps. You can use a webpack-manifest-plugin to create simple manifest.json for each micro app. The manifest file is defining our micro application’s asset’s URLs. We’ll use these when loading applications.
We created our manifest files and we know where to run remote applications now. When the browser sends a request to bootstrapper application, we need to know which micro-app needs to load. We are using these pattern to our URLs;
https://yourdomain.com/{domainName}/{pageName}
The bootstrapper application can decide which part to load using the “domainName” part as a key.
But, how can I know all micro applications remote URLs?
You can create a simple config map to identify which application has which resources.
Here are some keywords;
manifest: Simple JSON file that you can access to the micro application’s assets. This URL is never changed. But, when you create a new deployment on any micro applications, manifest.json is changed with a new one.
dependencies: You can define custom styles and scripts with this keyword. For example; some micro applications use different styles and you want to load these styles to the specific applications.
After the define these key config files, we can send a simple request to the micro application’s manifest files for getting all assets and inject to DOM.
Communicate Between Applications
We created, published, and composed our micro applications. The next step is to define how your micro frontends communicate with each other.
EventBus is coming to the rescue!
When a micro frontend emits an event, the other micro frontends subscribed to that specific event react appropriately.
Conclusion
Micro frontends have given us the following benefits;
- We have smaller and maintainable codebases
- Each application has independent build, test, and deployment steps.
- Deployment frequency is also increased.
- We have more scalable and autonomous teams.
- Each team focused own domain.
The micro frontends approach is still growing. There is no one solution to do this. Of course, as I mentioned above, there are tried and applied solutions. You can use directly or you can take inspiration from their approach and create your own solution.
If you have feedback or some questions please contact me.
Thanks for reading.
Resources