Your frontend is a fish tank
Imagine you are working at a tropical aquarium. You are in charge of one of the main tanks, and have a ton of cool and beautiful fish swimming in there. All of them have specific needs, but you are good at your job and have created the perfect environment for all the fish in the tank to live together. The aquarium is successful and has happy customers, so your boss is planning to expand. Your boss wants to build a bigger tank with tons of new fish. He promised the visitors sharks, clown fish, salmon and koi carps.
Just make it work
To me, this tank serves as a perfect analogy for a monolith frontend application. Your boss (product owner) wants new fish (features) to impress the visitors (end-users), and probably expect you to just make it work. The visitors expect to see happy and healthy fish (expect the features to work). As a caretaker (developer), your task is to keep both your boss and the visitors happy whilst taking care of the technicalities that keep the fish healthy, but you know that placing some fish together is risky (might cause bugs or errors). You'll also need to install specific water plants (dependencies) for some fish to thrive, but they'll have a negative impact on some of the other fish.
Micro-frontends
An obvious solution would be to split the tank into multiple smaller ones, creating separated environments. Apply this to a frontend, and you're basically doing micro-frontends. You cut your massive monolith frontend into multiple smaller ones. They each have different functionalities and configurations, but they are pieced together in the browser to provide one working application to the end users. You'll get a few benefits from that:
- Smaller bundle sizes
- Faster pipelines
- Downtime only affects part of your app
- Trace bugs to less code
- Mix and match parts of your frontend
Our own fish tanks
In my team, we chose to build a micro-frontend framework from scratch with React and TypeScript. Mainly because we didn't totally understand the concept at first, we wanted to keep in control and we didn't want to pay money for a framework.
Over the course of two years, we built 6 frontends and 1 code library, that together form our BestOne Buying platform. We cut our frontend along the lines of our pages, which means that every page houses a different micro-frontend.
One for all...
We have one frontend that keeps the whole structure in place, which we call Umbrella. It's hosted on the root of our cluster and it has some core features:
- Handling user authentication
- Handling page navigation
- Rendering overlaying UI elements (like notifications)
- Rendering micro-frontends in the DOM
- Providing relevant data to micro-frontends
...and all for one
For every page in our platform, we have one micro-frontend. This micro-frontend is tasked with fetching, rendering and manipulating data for that specific page. For example, the micro-frontend for our collections page only knows how to CREATE, GET, UPDATE and DELETE collections, and renders UI elements that our users can utilise to perform these actions. It does not know how to authenticate the user, but it will receive the right credentials from Umbrella to be able to perform these actions. In turn, it can communicate back to Umbrella as well, so we have a two-way communication channel.
Let's take an example. Our user clicks on a page in the navigation menu while having an uncompleted form open in the micro-frontend they currently have open. Umbrella will send a message to this micro-frontend, basically asking if it is okay to close. That micro-frontend knows that there is a form open, and replies that it might not be okay, and it would be good to let the user confirm this action. Umbrella receives that signal and renders a confirm dialog on the page, informing the user that they will lose their progress if they navigate to a different page. If the user decides to continue, the micro-frontend will be closed and the targeted micro-frontend will be loaded instead. If the user decides not to continue, the navigate action will be ignored, and the micro-frontend will stay so the user can complete the form.
Challenges
Since we built our own micro-frontend-framework, we also encountered a few challenges that a framework could've solved for us. We've had to prevent any external code interfering with our communication channels. We do this by thoroughly screening all signals going back and forth between our frontends. Also, we don't want to have duplicate code in different frontends, so it was necessary to create our own code library. This way we can store logic, UI components, Typings and other things in one place, and have it available for all our frontends to use.
In the end, if you want to start using micro-frontends but you care more about speedy development than owning all the code, you should consider a framework like Bit , Luigi or Piral. They offer a lot of solutions for common problems out of the box, but you'll need to learn how to use them of course.
(But I think it is way cooler to build your own aquarium ;))
About the author
Jory Schiebroek
My name is Jory Schiebroek, I am from the Netherlands and I work as a Frontend Developer for team Buying at BESTSELLER Tech in Denmark. I am also a sucker for clean UI and satisfying UX, and I love experimenting with that in my applications.