Whether it is because you just want to migrate to a new framework or you are desperately in need of getting out of your current app, micro-frontends may be the way to get there.
Let us get to it.
Avoid Adding to Your Current Application
The first thing that might come to your mind when starting to implement that new framework in your app is at what point you can pass the control to that new section of the app. My suggestion, in this case, is to take a step back; your current and new apps should be independent. What I mean by this is your existing app should not bootstrap the new one or the other way around, I would advise against doing this since it can make your apps unstable, harder to follow, and it can bring unexpected and extra-difficult bugs. Unless you have two frameworks or libraries that can live in perfect harmony, let your apps be independent. Having a wrapper one layer higher, which bootstraps your apps, can be just what you are looking for.
Separate Entry Points
If you can tell which are the different sections of your app, then it is very likely that you can divide it and give each section a home node in your HTML document. As an example, let us say your app has a header, sidebar, and main content; designate a mount node in the document for each of this sections, if you do not already.
The main goal behind this is to have your app sectioned in a way where you can either gradually migrate these small parts of your app (micro-apps) to other micro-apps written in the new framework and using the new tools or choose when that section of the app is temporarily unmounted and replaced with a new one based on the state of your app.
Let us say, for this specific route, you unmount the app running on your main content node and render the other one that has all these crazy new things; the reverse happens when navigating out of that route, not bad, huh?
Less Exposing == More Flexibility
When it comes to sharing dependencies and exposing libraries on the window object, you might want to avoid it. You may feel tempted about using the window object to share a dependency, but it is likely that you will end up stuck on a library version because it is harder to update all your micro-apps at once. Moreover, having libraries globally available can create conflicts when you try to use a similar or even the same one in a newer version. You can definitely gain some flexibility at the expense of bundling the dependencies with each app. There may be exceptions to this rule, but generally, I would suggest letting your app be independent.
It may be ok to share some dependencies if some sections of your app were born together and were split later on to make the micro-frontends implementation possible. I will let you be the judge.
Event Emitters as Communication Channels
Let us state the obvious. To have multiple small apps working as one, communication needs to happen. If you are "catching the vibe" of what I have been talking about here, you already know your apps need to be independent. Moreover, to make them data-independent as well, you might want to do data fetching as you would regularly do, but in each one of your apps.
I know you are already worried about unnecessary requests, and if you are not, you should be. For that, I would suggest combining your state manager with your event emitter. Data already requested means one extra request prevented, et voilà!
All the above may seem like a little bit of extra work, and it probably is. To make it a bit easier, let me suggest some tools that might help you with going down this path:
- single-spa — the top-level wrapper you may be looking for for your micro-apps;
- event-emitter — get your apps communicating.
This is it, for now, so, tell me, what else would you like to know?