How To Experiment Micro Frontends With Angular and Module Federation?
30-Second Summary
- The story of Web frontend development is very interesting. Javascript is far more advanced than it was a decade ago and a new generation of high-performance frameworks emerge.
- The idea of Micro Frontends is to subdivide a big application into tiny parts. Those parts become individual applications.
- Experiment Micro Frontends is more about team organization and business domain architecture than technology.
- A very simple example of a configuration with Angular and Webpack Module Federation shows us how Micro Frontends extends existing possibilities and gives more flexibility to product teams.
The Past
At the start of this century, we wanted JavaScript to do nice things but the language was little known, and the developers stuck to transposing the basic instructions they knew into other languages like PHP or Java … when he was not doing VBScript.
In the early years of 2000 something called “Ajax” happened. It was truly an innovation. Except for a recalcitrant “Internet Explorer” browser.
No doubt why all the old Web developers (I’m afraid to say “like me”) hate Internet Explorer today. In reality, behind this term “Ajax”, there is
the XMLHttpRequest object and what it is allowed to provide data without reloading the page (except on IE, where you had to write several dozen lines of code using the ActiveX framework to achieve the same result). Despite its name, this technique can be used to retrieve any type of data. Not just XML.
Before that, things like Gmail were impossible (young upcoming developers don’t have to deal with that). The concept of web 2.0 was born. It was starting to get very interesting for developers. This is a great real, scalable quoted way to just write code once and make it run. Several browsers have started to appear on the horizon to compete with IE …. it was Firefox and Chrome.
That’s when JavaScript got really serious. The developers have started making big jQuery applications! And… let’s fix this horrible spaghetti jQuery code with frameworks like BackboneJs or AngularJs (the first version in 2010) next. Backbone was created by Jeremy Ashkenas (who is also known for Underscore.js). Thanks, Jeremy, for saving me a lot of time.
A small revolution was underway. Single-page applications (SPA) were born. We were starting to think that we were doing real software engineering. But very clever developers like Jordan Walke, specifically from Facebook, at the time, which was the best front-end tech company in the world, have come in and they said “No! No! No! No one knows the state of your app and your app is extremely large”. They will create “the way” to have the status of the application clear and correct. What is, in short, to eliminate the fear of the frontends to communicate with the backends in an efficient way to be able to manage the transmitted data. React was introduced in May 2013 at a JavaScript conference in the USA. That’s the Javascript short story because we’re not going to talk too much about the past.
The Future
Today, frontend developers evolve in a steady situation. Most browsers are now competing to meet compliance. CSS (who killed Macromedia Flash aka Adobe Flash) & JavaScript become more powerful and dominate the web. Most Javascript frameworks are stable and try to be compliant. React, AngularX, and Vue are the most popular frameworks. GraphQL emerges (the kind of tech everyone loves but nobody uses).
And while waiting for how the WebAssembly spread, it is very likely that the future of web applications will go through a new conceptual revolution of their architecture, as we have seen in recent years on the backend side with the arrival of the concept of Microservices concept and a whole bunch of design patterns. As is often the case in web development, frontends follow underlying trends that appear first in backends. Do you remember a while ago nobody got down to doing unit or functional testing at the frontend level when it was a common practice in the backend (are we a little schizophrenic?).
So what are micro-frontends? The author of Micro Frontends in Action, Michael Geers defines this concept on his website micro-frontends.org as follows:
The idea behind Micro Frontends is to think about a website or web app as a composition of features which are owned by independent teams. Each team has a distinct area of business or mission it cares about and specialises in. A team is cross functional and develops its features end-to-end, from database to user interface. — Michael Geers, Micro Frontends in Action
Today, micro-frontends continue to gain in popularity. But why?
How To Experiment Micro Frontends?
Why would we want to change if we have reached a maturity level? With the rise of the concept of frontend and backend separation and the maturity of these technologies, it is a better choice to develop web applications As the business grows, the single front end becomes more and more bloated, resulting in SPA that is not well scaled and deployed, and difficult to maintain
What do we need for good architecture? Today, the backend architecture is oriented towards microservices in order to better divide the codebases into small independent bricks and thus improve maintenance.
Just as with microservices, the independent deployability of micro frontends is crucial. The frontend architecture follows this trend in order to also be better structured and allow better reuse of independent modules within applications.
Each interested team could then take these fragments and integrate them into their application according to the needs of their end-users. The main novelty of micro front-ends is that development teams are not separated by technology (the classic example of backend teams and frontend teams) but are transversal teams with all the necessary profiles to develop full functionality.
When the microservice evolves, the suitable micro frontend will be developed at the same time by the same team. Very useful if you use DDD. Using this architectural style requires defining the integration points and interfaces between the different teams. This effort only makes sense in developments and teams of a certain size.
M. Geers describes the following concepts that lay the foundation of Micro-frontends:
Be Technology Agnostic
Each team should be able to choose and upgrade their stack without having to coordinate with other teams. Custom Elements are a great way to hide implementation details while providing a neutral interface to others.Isolate Team Code
Don’t share a runtime, even if all teams use the same framework. Build independent apps that are self contained. Don’t rely on shared state or global variables.Establish Team Prefixes
Agree on naming conventions where isolation is not possible yet. Namespace CSS, Events, Local Storage and Cookies to avoid collisions and clarify ownership.Favor Native Browser Features over Custom APIs
Use Browser Events for communication instead of building a global PubSub system. If you really have to build a cross team API, try keeping it as simple as possible.Build a Resilient Site
Your feature should be useful, even if JavaScript failed or hasn’t executed yet. Use Universal Rendering and Progressive Enhancement to improve perceived performance.
What are the skills and techniques used by the best product teams to experiment with Micro Frontends? Undoubtedly the first step is to understand that it is another type of organization around the development teams and the architectural division of an application.
A big software system is subdivided into tiny parts and those parts are individual applications. But you have a lot of tiny applications and so they can work individually they don’t need to coordinate.
Benoit Hediard tells about his experience on big Angular monolith application which has become very fat and how they are moving towards a micro frontend architecture. He defines three resulting benefits of this approach:
- Reduced cognitive load for teams with clear business domain decoupling and boundaries
- Release cycle autonomy: from design, and specs, to release and maintenance
- End-to-end full-stack ownership: from back end to front end (code and infrastructure)
Webpack Module Federation With Angular
So how to integrate all those tiny applications into a bigger solution? The new version of Webpack, Webpack 5, will eliminate some of the functionalities that had been deprecated and will bring some new ones such as the persistent cache or improvements such as hashing. One of the most striking features is the Module Federation, a plugin that will facilitate the creation of what is known as micro frontends, allowing the dependencies they have in common to be shared among the different builds, among other things.
Micro Frontends is just a bunch of little tiny applications. This was not really possible before… in fact, it was possible but with a lot of workarounds! This is changing now because Webpack and Module Federation give us an official way to load separately, compiled, and deployed a bunch of stuff separately.
Why not load this application on demand? How to load separately compiled code? That cannot be that difficult, why not just they can “dynamic import”? Why not use this dynamic import to point over to another application? In theory, this works perfectly. In practice, it’s another story. It does not work at all because of the limitations of current bundlers like Webpack for instance.
All those bundlers, Webpack assume everything needs to be known at compilation time. Time everything is compiled together, everything is optimized together, and then and only then it is cut into pieces for lazy loading but this is too late because we don’t know everything upfront.
In Micro Frontends, everything is separately compiled and everything is separately deployed. To make this possible Module Federation introduces two roles: the first role is the host and the second role is the remote. The application called “shell” accommodates micro “remote” applications.
So how to combine Module Federation with Angular? For that, Webpack provides a lot of Module Federation examples with Angular in his Github (I particularly appreciate the with Angular and Scully). Let’s explore the micro-frontend Angular example.
The bad news is, as you can read in this example, Angular and Angular CLI 11 are still using Webpack 4. Of course, version 5 is still experimental. The good news is the package.json file contains a section to override Webpack dependency. So, it’s easy to force version 5 (using Yarn only) instead of 4:
"resolutions": {
"webpack": "5.0.0"
},
So now, how to integrate the new configurations in each project? You can see extra Webpack configurations in angular.json :
"extraWebpackConfig": "projects/mdmf-shell/webpack.config.js"
Importing “ModuleFederationPlugin”, the “shell” application defines commonly used libraries as shared modules in a shared scope to avoid duplication of them in the page builds. A very simple configuration:
plugins: [
new ModuleFederationPlugin({
shared: {
"@angular/core": { eager: true, singleton: true },
"@angular/common": { eager: true, singleton: true },
"@angular/router": { eager: true, singleton: true },
},
}),
],
The remote application exposes an entry file, which is called “remoteEntry”, and each component to consume. In this example:
filename: "remoteEntry.js",
exposes: {
ProfileModule:
"./projects/mdmf-profile/src/app/profile/profile.module.ts",
},
The host application combines all remotes applications in a very simple configuration :
new ModuleFederationPlugin({
remotes: {},
...
}),
It does not define any remote application upfront but configures the packages we want to share with the remotes at runtime. In fact, if we go deeper in detail, we can find this function which is used by Module Federation to load remote modules:
export type LoadRemoteModuleOptions = {
remoteEntry: string;
remoteName: string;
exposedModule: string;
}; export async function loadRemoteModule(
options: LoadRemoteModuleOptions
): Promise<any> {
await loadRemoteEntry(options.remoteEntry);
return await lookupExposedModule<any>(
options.remoteName,
options.exposedModule
);
}
After loading the remote entry point, it uses Webpack’s runtime API to load the remote applications. It allows loading modules but with no need to know it at compile time.
Then, it is possible to use Angular routing, as usual, to lazy load separately compiled applications via the router. For instance:
const routes: Routes = [ {
path: "profile",
loadChildren: () =>
import("./profile/profile.module").then((m) => m.ProfileModule),
},
];
As simple as that.
Flexibility
You want, for example, to use multi versions of Angular core modules using the strict version parameter in the configuration.
You don’t use Angular and do you prefer React or VueJs? Here is an example for React.
Do you want to mix technologies, just as microservices allow, and import React applications as modules in your Angular shell application? Yes, it’s possible too. Remember the concept of the micro frontend is to be tech agnostic.
That’s it, we have reviewed the evolution of JS frontend technologies from the beginning to recent years. Module Federation gives us more flexibility and tends to revolutionize the frontend development by imitating the way development.
Thanks for reading. Follow me right here on Medium so you don’t miss the next articles. And don’t forget to keep learning and experimenting.