Micro-Frontends with Module Federation and Angular

Bookmark

Ever more companies are choosing Micro-Frontends. However, they are anything but easy to implement. Fortunately, Module Federation introduced with webpack 5 has initiated a crucial change of direction.


In this interactive workshop, you will learn from Manfred Steyer -- Angular GDE and Trusted Collaborator in the Angular team -- how to plan and implement Micro-Frontend architectures with Angular and the brand new webpack Module Federation. We talk about sharing libraries and advanced concepts like dealing with version mismatches, dynamic Module Federation, and integration into monorepos.


After the individual exercises, you will have a case study you can use as a template for your projects. This workshop helps you evaluate the individual options for your projects.


Prerequisites:

You should have some experience with Angular.



Transcription


So, first of all, it's really a pleasure for me to be here with you and to work with you today on one of my favorite topics, namely on micro frontends with module federation. Examples will use angular because I'm specialized in angular, but nevertheless, everything we see today works also with other frameworks like vue or react because at the end of the day, it's webpack, isn't it? webpack and perhaps in some advanced scenarios, web components. So, first of all, let me get started with a tiny picture. Perhaps you know this picture here. This is the helicopter that has been designed by Leonardo da Vinci. In 1400 something Leonardo da Vinci designed this helicopter in 1400 something. That's quite a thing, isn't it? Because back then not a lot of people thought about flying devices, but Leonardo did. The thing is he only designed it because he didn't have the material for building it. And finally, nowadays we have the material and scientists tried it out. Scientists tried to build this very helicopter and they figured it would really fly. How cool is that? And somehow this whole story reminds me on the current situation we have with micro frontends because the idea is everything but new. The idea has been around for let's say several years. I have customers that work with this idea for 10 years. Back then we didn't even have that term micro frontend. The idea is quite old, but so far we didn't have the right material. But this is changing now with module federation which is in the core of this today's workshop. Module federation gives us a straightforward way of implementing micro frontends. Well, when I'm talking about micro frontends, of course, the first big question is what do I refer to when saying micro frontends? Well, I'm referring to a huge system that is split into tiny parts, into tiny individual parts that can be developed by teams of their own. Each and every team has its own tiny part. Each and every team has its fraction of the overall system and they can do local decisions. They can deploy when they are done and that means they can provide business value on a regular basis. They don't need to coordinate with other teams that much. They can just do their decisions and this can even lead to own technology decisions. And the best of this is this brings back the agility of a small team to a huge software product that is built by tens or hundreds of people. So this is the main motivation, scale your teams so that tiny teams can work on fractions of your overall system. If you just have one team then perhaps, and I think you will see it and feel it during the examples, this here is a total overkill for you. So as mentioned, this is always about scaling teams and as a good friend of mine told me, teams is pro, that means we have to have more than once, otherwise this will get an overkill. So the thing is if we have a lot of tiny micro-frontends, somehow we need to integrate them into a bigger common thing and this is what I'm calling the shell. Here we have a very simple shell on the left and this shell is capable of loading this and that micro-frontend on demand. And so the user has the impression that this is just one single page application, one integrated application. But from our perspective, it's a bunch of applications that have been developed in an orthotic way, that have been compiled in an isolated way and published separately. This is what all of this is about. However, building a shell was a nightmare in the past. We had to deal with a lot of tricks and work around, so it was everything but fun. But nowadays, and I've already mentioned it, thanks to module federation which shipped with webpack 5, this is really straightforward. And this is the very topic of this workshop. We will look into module federation, we will look into a lot of possibilities, even into some advanced scenarios like dealing with version mismatches to find out how this works, to find out how this can help to build big applications. Regarding the contents, in the first part of this workshop, I will present some theory. This theory is about module federation in general. This theory is about version mismatches or dealing with version mismatches, you know, as soon as you start sharing dependencies. So, Federated angular is about how to use this all together with angular. For angular, we need some tricks we don't need for react and vue. This is because of angular is shielding a lot of details from us. With react or vue, you can directly go with your webpack configuration. With angular, you have your CLI, and somehow you need to tell the CLI to use webpack module federation. We will even look into dynamic federation. And as a bonus chapter, I have something for you that's perhaps quite interesting. It's about mixing and matching module federation with web components to even mix and match different versions of your framework or to mix and match, let's say, different frameworks. Of course, we don't do this just for fun. We don't mix react with angular for fun. We need a good purpose for this, like a migration strategy or like integrating something another team did. Perhaps we had a merger in this case. It can be that we have different parts based upon different frameworks. This is something I will show you in the first part of this workshop. And I will not only talk about it, I will also show some live coding. In this first part, you can land back and listen my words. Of course, we can also discuss. I'm always happy if I have some questions. And then in the second part, it's mainly your turn. For the second part, I have prepared a lab description, and you can go through this lab description and implement your very own micro front-end architecture. And of course, I'm here for you to help you if there are any questions. Okay, cool. So before we get started, let me also introduce myself. I am Manfred. I'm a trainer and consultant for angular. I'm doing a lot of workshops for angular. In times like this, most of them are done remotely. They are either consulting or trainings workshop. And my focus is angular for the enterprise environment. That means I help my customers with building huge enterprise scale angular applications. And besides this, I'm quite connected to the angular community. I live in Austria. I do a lot of stuff in Germany. And I'm always happy if I can work together with people around the world nowadays remotely, which makes it a bit easier like today. Okay, cool. Let's get started with the first chapter, which is about webpack 5, module federation in general. Well, the main question module federation is answering is this question here. How to load separately compiled source code? And perhaps you are saying now, hey, Manfred, come on. That cannot be that difficult because we have dynamic imports. And we could use a dynamic import to point to a micro frontend that has been separately compiled and deployed. The thing is, in theory, this works perfectly. In theory, in practice, this does not work at all because of how all the current bundlers work. All the current bundlers like webpack itself or the angular CLI using webpack. All those bundlers try to squeeze out the last bit of your bundles. And for this, they assume that everything is known at compile time. Everything, even the lazy parts, need to be known at compile time because everything is compiled together. Everything is optimized together. For instance, think on tree shaking and ten and only ten. Everything is cut into chunks for, for instance, lazy loading. But for our purpose, this is too late because if we talk about micro frontends, we talk about frontends that are developed by different teams that are compiled separately, that are deployed separately. We don't want to coordinate that much with other teams. We just want to provide business value on a regular basis as we were a little tiny age child. And so, this is too late. We don't want to know the other parts upfront. Yeah, and this very issue is solved by webpack, by webpack 5 module federation. webpack 5 is there since last October. And with webpack 5, they also shipped module federation. And, you know, webpack is really heavily used out there. And that means on the spot, we have a big distribution of module federation too, as it ships alongside the webpack. So, I'm sure you're wondering what is webpack 5 module federation doing. Well, it defines two roles. Basically, it just defines two roles. The first role is the host. In our case, it's the micro frontend shell. The second role is the remote. In our case, it's the micro frontend. And now we can configure both sides. We can say, hey, shell, I want to map a URL, the URL MFE1. It shall point over there to something that's called MFE1 over there, a separately compiled and deployed micro frontend. This here could be an alias. In this case, it's rather a non-alias because in my situation, it has the same name on both sides. But in the remote, I can expose files. I can say, well, this file with this component, this file with this module, or just this file with this function is exposed for the shell. And when doing so, I'm assigning a simple name. And now something beautiful happens. I guarantee you, you will love it. Now you can use a dynamic import. You can use it together with the mapped names. And that means you can grab over to the separately compiled micro frontend. MapBack will do the trick to load the exposed file from it. The best about this is that your framework, for instance, angular, does not even recognize that we are using micro frontend. For angular, this is just like lazy loading. We go with a dynamic import. That's everything. That means angular does not even know that we have this architecture in place. And so you can use angular as always. You can use angular as is, as it has been intended to be used. You don't need any tricks. You don't need any meta frameworks. Meta frameworks are rather complicated frameworks you use to orchestrate different micro frontends in your browser. No, you don't need it. And this really lowers a big, big pain when it comes to this architectural stuff. Underneath the covers, MapBack is doing the heavy lifting for you. Of course, we need some meta data of our micro frontend. And this meta data is exposed by MapBack when building the micro frontend. And normally, it's called the remote entry or remote entry point. In production mode, this file has about 2K. And those 2Ks, perhaps 3Ks, needs to be loaded into the shell. You can do it upfront in a rather static way, like here. You can even do it in a dynamic way. You can use a dynamic script loader to load it. Those 2 or 3Ks tell the shell everything it needs to know to interact with this micro frontend, to load it, to share dependencies with it, to call methods. Talking about sharing libraries, this is perhaps one of the coolest features of module federation. You can share libraries. For instance, you could say, well, I'm using angular. You are using angular. So why not just share it? Because it does not make sense to load angular 10 times because we have 10 micro frontends leveraging angular. No. In a perfect world, we would just load angular once and share it across all the micro frontends and, of course, the shell. And this is as easy as opting in into sharing for this or that library. Just mention your library, like here in your webpack configuration. Of course, this might lead to version mismatches, but we will come to this. You don't need to worry. We will come to this. And the good message is module federation got you covered when it comes to version mismatches. There are several strategies that help you to deal with such a situation. Yeah. And this exactly brings me to the second chapter I have prepared for you today, dealing with version mismatches. The thing is, and I think that's really a nice story, I know the people behind the webpack product a bit, and that's why I know they implemented module federation twice. First, they implemented the alpha version. It grew into a beta version. And then during the beta version, when they discussed with companies, they figured that they need a better story for exactly this topic here, for dealing with version mismatches. And yeah, so they just rewrote it. They completely rewrote it. They implemented it one more time to bake in several strategies for this. And even though you can configure several strategies, the good message is already that default behavior is a quite smart one. The default behavior is selecting the highest compatible version of each and every shared library. The highest compatible version. Sounds a bit complicated. Yes, I feel you. That's why I prepared a tiny thought experiment. Let's think on a situation where we have angular 10 on the one side and angular 10.1 on the other side. In this case, we would go with angular 10.1 because 10.1 is the highest compatible version. That means when your shell starts up, there is a negotiation. The shell looks, what do we have here? What do we have there? And then the shell figures, well, this 10.1 is the highest compatible one. Let's go with this version. And this really fits well to semantic versioning where a higher minor version or a higher patch version is always backwards compatible. However, perhaps we have this situation. On one side, we have version 11. On the other side, we have version 10.1. You know, according to semantic versioning, a major version can introduce breaking changes. So chances are high that version 11 introduces breaking changes compared to version 10. And that's why just going with the highest version is not possible. And that's why we don't have a highest compatible version. So what is webpack doing now by default? Well, by default, it loads both. By default, it says, well, I see it in the meter data. You need version 11. You get it. You need version 10.1. You get it. This is the default behavior. And of course, it's not perfect. It is a hit on your bundle size. You need to download more bytes over the wire. But on the other side, if bundle size is not your biggest concern, then this might be a nice solution because it allows to work independently. It allows one theme to work independently from others. And if we talk here about, let's say, stateless, about a stateless library, then this is completely fine if bundle size is not an issue. Because one part of your application is not concerned if another part of your application is using the same, let's say, underscore version or the same version of our XJS. They just don't care. However, if we are talking about a stateful framework, your leading framework like angular, then this here is the issue. Because angular knows all the components. But now here we have two versions of angular side by side. And yeah, there are some pitfalls when doing this. And besides this, if we manage to deal with the pitfalls, we still have two containers. angular 11 knows those three components. angular 10 knows these three components. And the components don't know each other because they live in different worlds, in different universes, in different angular versions. So using them together is really difficult in this case. If you really, really, really, really need something like this, then I would think twice if you really, really need it. And if you say, yeah, I'm still really, really needing this, then you can bring web components into the blade. In this case, you can wrap your angular 11 components using web components. You can wrap your angular 10.1 components using web components. The good thing is a web component is just implementing a browser standard. That means the browser does not care how this or that component was built. The browser can just make it to render on the screen. And yeah, so we can even mix and match different angular versions or even different framework versions. Of course, everything is getting a bit more complicated. You need some additional workarounds. You have an even bigger hit on your bundle sizes because you need to download more. But if you really, really need it, I got you. I will come to this very special scenario in the end of this workshop. Okay, so to cut a long story short, if there is not the highest compatible version, both versions will be loaded. This is fine for a stateless library. If it comes to a stateful framework, go with web components in addition. Let me show an example for all of this. This example shows how smart module federation is. Let's assume we have the shell that is using version 10 of something. Let's assume we have a library that is using version 10.1 of something. Furthermore, let's assume we have another micro frontend that is using version 9. And the third micro frontend is using version 9.1. And now because of this version negotiation, module federation will decide to give the shell and the micro frontend version 10.1 because here this is the highest compatible version. For both of them, this is the highest compatible one. And it will decide to give micro frontend 2 and 3 version 9.1 because 9.1 is the highest compatible version. Yeah, I think this really shows how well this can work and how this is balancing the trade-off between isolated bundles and using different versions of something. There are even more possibilities. You could configure an application as a singleton. That means that this very library can only be loaded once. This is like the Highlander principle. You know the Highlander principle. There can only be one. So let's assume someone is using version 11 of the singleton. Someone is using version 10.1 of the singleton. In this case only version 11 is loaded. Even though version 11 is not the highest compatible version, well it's the highest one, but it's not a compatible version to 10. But nevertheless it's loaded because we told module federation to go with this singleton behavior for this very library. And this might be okay-ish or not. It really belongs on your current situation and at the end of the day you are responsible. You are responsible. Officially this is not working because we have a different major version, but perhaps nevertheless it works. If you think on angular, in the last versions of angular not much changed in the core of angular. So chances are high that a new major version does not introduce that much breaking change. However, because this is not officially supported by semantic versioning, you will get a warning on your javascript console when doing this. And if you say well a warning is not enough because this might be a severe issue, then set strict version to true. In this case you don't get a warning but an error message. Fail fast is the theme here. Fail fast. You start your application and immediately the shell will throw an error. And this might be a good idea because you don't want your users to discover such an error on runtime. You want to discover this error very early. Perhaps within your first end-to-end test, within your first integration test. Fail fast. Don't make the user use it because it's not a nice UX if they fill out hundreds of fields just to get an error because of versions. However, you can also say well, trust me, I'm an expert. I know this application was compiled against version 10, but I know nothing much did change and I'm not using that much features. So it works with everything in this very version range, with everything between version one and it's also possible. But one more time, here you are responsible. The risk is on you and that also means you need to have a sound set of integration tests. On the other side, you always need a sound set of integration tests if you go with micro-frontends because what does micro-frontends mean? micro-frontends mean that you are turning a compile time dependency into runtime dependencies and you never know what happens at runtime. So better have a sound set of integration tests. Cool, so you've seen module federation gets you covered when it comes to version conflicts and you can even use web components to decouple everything a bit more. There is one good question from Alexandre. He is asking does direct version option also fails when it requires 10.1 but 10.2 is present? No, this is completely fine. So if there is a highest compatible version, then this is completely fine and it will just go with the highest compatible version. By the way, and I think you know this from npm in general, you decide what's the highest compatible version by placing the right version numbers in your package JSON within dependencies or peer dependencies. Because if you say you need angular core in version 10 and if you use this we call it rooftop, then you clearly say well a newer minor version or a newer batch version would be totally fine. If you just use the delta, then just a newer batch version is fine. So at the end of the day, it's not semantic versioning but you define with your versions in your package JSON what's a compatible version. Normally we go with the rooftop and so the rules are as shown in my little thought experiment. Okay, so let's come to the third point I've prepared for you. This is about how to combine this goodness we have seen so far with angular and especially with the angular CLI. And here I have a good message for you, namely the angular CLI is already using webpack underneath the covers and since version 12 which arrived about a month ago, we are even using webpack 5 so we have everything in place here. However, the CLI is shielding webpack from us and this is now an issue. Normally this is perfect because honestly we don't want to get in touch directly with webpack. It's quite complicated especially if you look at the webpack configuration the CLI generates to squeeze the last bit out of your bundles. So normally it's fine that we don't even see it but now it's not fine because now we need to squeeze in our module federation and so the big question is how can we do this? And the answer is we can go with a so-called custom builder. Perhaps you know it the CLI is really flexible. The CLI has I always call it a driver concept. You can exchange most of the logic of the CLI and the exchangeable logic for building applications and libraries is part of the so-called builder. By the way builder is a bit a misleading name because builders are also used for executing tests and for executing the linda so perhaps executor would be a better name for this but at the end of the day it's an exchangeable strategy for some of the commands the CLI provides and what we could do now is we could now write a custom builder for ng-build that is delegating our module federation config to webpack to webpack underneath the covers and then the custom builder can delegate to the default builder let's put it here db default builder and the default builder is doing what it always does it delegates to webpack to build this very application but now it's built with our module federation this is the tiny trick we need here if you don't use angular if you use react fuse welt or something else then just go with your extended webpack config and you are in business of course we could now write our own custom builder that would be totally possible however you don't need to build it by yourself because i already did it i created this package called angular architect's module federation and basically it does three things for you it generates the skeleton for a module federation config that means you don't need to learn it by heart you can just fill in place all this that's the first thing the second thing is it installs a custom builder that enables module federation we just discussed this on the last slide and then last but not least it assigns a new port for ng-serve so that you can enter different micro-frontend side by side and there are some additional features we will look into a bit later the usage of it of course i'm biased but if you ask me it is straightforward just add it adjust the generated webpack config and ng-serve your application and this should be everything to get started with your very module federation based micro-frontend based architecture yeah i know this is all a bit abstract and that's why i've prepared a demonstration for you a demonstration that shows everything in action you can really land back you can just watch this demonstration and ask questions if there are any and you will get some time afterwards to work on these topics with the lab descriptions you will get in the second part of the workshop so let's get started with this demonstration here so in this demonstration in this demonstration i need my right folder on yeah here it is we have a so-called mono repository yes i trust myself this feature i have a so-called mono repository with a shell and a micro-frontend and no this is not a prerequisite you can totally go with different repos you can have one repo here one repo there and integrate everything at runtime but for the sake of demonstration it is easier to just use a mono repository in addition some people also do this in practice because sharing libraries is a bit easier if everything is here in one repository there are advantages and disadvantages on this and as i figured a lot of strong meanings people are fighting over if a mono repo or multiple repos are better honestly i have customers using mono repos i have customers using multiple repos both works both works fine but both has different consequences here we go with a mono repo well and as you see here we have a shell and we have a first micro frontend so let me start it up for this i'm going here with my windows terminal and she served the shell let's use a unique port and minus oh and do the same here let's do the same here mfe1 board 3000 and oh so and yeah we all know angular this is now a tiny lesson in patience together we will make this perhaps we can use the time to answer some questions as now yeah if i could have a question yeah so we are talking about the mono repos and multiple repositories the example you showed i assume is using typical angular cli generated project but often often people uses nx for handling the mono repos and nx is like giving another abstraction layer over the angular cli so would your approach for module federation also work the same way yeah there would be no conflict with the nx cli wrapper around the angular cli and then webpack and so on and so on yeah you're right that really works perfectly i've tried it out and it really makes a lot of fun to use this together with nx because in this case you can use the access restrictions of nx to make sure that one micro frontend is not directly touching the source code of another micro frontend but nevertheless you can define selected libraries you want to share across them for instance for authentication or the sake of passing around messages all right thanks yeah the answer is yes and the reason is also nx is using the same builders the cli uses especially for applications so yeah i think there was another question dennis is asking how does the script point to different remote entry files if you have multiple remote files how does that back know which script points to which remote yeah that's that's a good question the short answer is you can define this in the webpack configuration at both at compile time but also at runtime this is the short answer and the long answer will be shown during this demonstration okay cool so meanwhile everything started up meanwhile here we have the shell here we have the micro frontend and when we click within the shell on flights we see that nothing works because this is now our task or to be more precise my task it's my task to load the micro frontend into this very route even though we have two different compiled and deployed applications here we have localhost 5000 and here over there go away we have localhost 3000 and no this is not the prerequisite you can deploy everything on the same server but having different boards really proves that we can deploy and compile everything separately awesome okay now let's bring this all to life let's use micro frontends let's use module federation to load the micro frontend into the shell and for this as shown on the slides i'm angi adding the package we've looked into so yeah when we see this then we should abandon at latest i'm really curious if you also need to abandon at latest during your labs i think i have some issue with my local cache here i think it's a local issue do you want to proceed yes please i want to proceed i trust the author of this package so and then you say well i want to activate the shell for module federation and by the way the shell shall use port 5000 and now you see a lot of stuff is generated for instance a webpack config with your entries for module federation you don't need to worry this is not a full blown webpack configuration this is just a partial one this just contains the tiny settings for module federation the rest is still generated by the sealer so thankfully because webpack you know it it can be quite complicated can't it the angular json is extended because the builder is registered the builder that activates module federation underneath the cards and some other files are updated we will look into some of them during the demonstrations here okay so before we proceed with the source code let's do exactly the same for the micro front end here i have a separate step for it it does not need to be of course a separate step but it's a bit easier for my brain to get all this done if i have one tab per application yeah i want to enable micro front end one for this and i want to use the port 3000 by the way for all of these they are command line parameters so if you don't want to go with an interactive prompt if you want to scrap this for a huge amount of existing projects it's possible okay the same happens now let's move to the source code let's have a look into our micro front end one and if we would exomine this a bit more closely then we would see it's just a traditional angular application just a traditional angular application nothing special in here nothing special but this webpack config we just generated this is everything you need to adjust to get the desired behavior so it's a partial webpack configuration you see it's not that huge it's merged with everything the cli generates and the most important part here is this the registration of the module federation here we have two sections the first section is for remotes or to say the least for micro front ends the second section is for hosts or to say the least for shells here we are talking about the micro front end so let's go with the first section let's get rid of the second one yeah and now i can expose files by the way here we have a unique name for our micro front end here we have the name of the remote entry file you can call it as you like if you want to look it cool you can assign a base64 encoded string no please don't do it normally it's called remote normally it's called remote entry or remote entry point and under exposes you can define some pretty name it does not matter but what matters is you need to point to your file you want to expose so in this case i want to expose a flights module the shell shall be capable of loading this flights module from the remote and if you look into this it's really an ordinary angular module nothing special in here an ordinary module with child roots how close this yeah then here we have the shared section we already discussed this before and yeah here of course i want to share angular stuff angular core common common hdb the router for instance is what i want to share also with this object here we can define how to behave in the case of a version conflict what to do if there is more than one version of angular core and if those versions are not compatible with a job well here i'm going we've discussed this before with the singleton behavior and with strict version true which is most of the times quite a good idea when it comes to angular the leading framework yeah that's that's everything besides this we have a traditional angular application now let's have a look into the shell so here within the shell one more time we have a traditional angular application everything is as usual plus we have this webpack config and it really looks the same we have the module federation plugin we have one section for the remotes we don't need it because here we are talking about the shell here we have the remote and this is one possibility to point to the remote entry file we can say well i want to map a url mfe1 over there it's also called mfe1 and this file board 3000 gives you all the meter adapter you need to know to load it to deal with it at runtime this is also partially answering the question of dennis but this is just one answer as we will see shortly there are other possibilities to define this file name for the shell because the thing is if you define this in the webpack config it's hard coded yeah it's hard coded even though you define it in the webpack config because the webpack config is only read at compile time it's never read again at runtime and so yeah it's really hard coded this is what i'm calling strict no not strict static federation federation and for getting started for local testing this is total fun totally fun for production you need a bit more and this would be dynamic federation this is what we need for production purposes we will look into this in some minutes for the time being let's get started with static federation so the rest is as before we can share libraries we can define an object like seen on the slides that tells you how to behave in the case of a version and was there a question no i thought someone started a sentence and now we just need to load mfv1 we need a lazy route pointing to the url mfv1 and this can be done in your shells routing configuration here i have my app routes and here i've already prepared my dynamic route or my lazy route to say the least it's as as usual we have a path we have load children pointing to a lambda using inboard to load something that we just want to load on demand the only difference here is we are using the map names the map names pointing over to the flight map names pointing over to the flight module in our remote in our microphone this is totally fine for angula because from angula's perspective this is just lazy loading this is totally fine for webpack because webpack knows how to resolve this path we have configured it however it is not fine for type script type script is complaining type script is saying i don't know this path here and yeah type script is right it does not know this path because this path does not exist this is just a virtual path a virtual path resolved by a webpack at runtime but there is not a file with this name and now we need to find a way to ease the type script compiler to tell the type script compiler come on type script trust us we are experts this will work at runtime and for this you need a dts file the name does not matter as long as you call it vts or as long as you assign the ending dts and within this file you can declare your stuff you can say well this works this is there and yeah wow it's cool isn't it now it's also fine for that type script yeah that should be enough that should be enough as you have seen traditional angula application plus this configuration so now of course we need to start both our shell and our micro frontend and if you don't want to work with two windows you can run run all which is a script that comes with the backhatch we've seen and yeah it is just starting all your applications in your mobile repo i wrote it because you can imagine i drove crazy than working with so many different angula cli processes little concurrency issue because you know angula is converting libraries meanwhile on the fly and this is mutual exclusive so two processors are not allowed to work on the same library but nevertheless it looks quite well if you ask me here on locale 3000 we have the micro frontend here in the shell we have flights let's reload it shall i click on flights yeah let's kick it and yeah it works it's always amazing because you know when doing live coding then a lot of things happen the human brain is a wonderful thing until you start programming in front of an audience then it completely stops working but yeah in this case everything is fine everything is fine there was a question in the chat let me look it up when should i use module federation over angula libraries well i would say module federation is only the right fit for you and your colleagues if you want to load something that was not known at compile if you want to load something that is separately compiled and separately deployed on this or that server if you don't need this this plug-in behavior then just go with traditional angula libraries then this here is an overview okay cool i always like to look a bit underneath the covers let's press f12 let's have a look into the network tab when i'm clicking here and you see it here only the micro front end is loaded only the micro front end with 10k this really proves that angula is this really proves that angula is reused we are just loading angula once and then we are sharing it between the shell and the micro front end because otherwise the overall bundle size would be far bigger and it also shows that from angula's perspective this is like lazy loading from ours perspective it's far more because we talk about something that has been separately compiled and deployed to prove this one more time i have localhost 3000 and localhost 5000 there which is not the prerequisite it's just to proving it marek is asking question if you will redeploy a micro front end one then you need to kick the shell as well yeah yeah that's true so the shell is not automatically reloading if you have a new version of micro front end one someone needs to press this button in order to get the newest version of micro front end one or we could work with some notification mechanisms server send events or web sockets to inform the client that it shall be reloaded but in my opinion this is not that a huge issue it's a huge issue on the server side with micro services but with micro front ends i think not much users would assume that their application is automatically refreshed refresh is enough yeah yeah uh no we just need a refresher the refresh is enough do we need cores cross origin resource sharing in this scenario not if we just want to load source code we don't need it and the reason is that back is creating dynamic scrap tags and for scrap tags you don't need cores you can totally point to another origin with a scrap tag without configuring cores however if you want to load some data from an endpoint over there yeah then you need cores or you need to dangle it through the shells backend because here everything runs in the context of the shells origin good question another question when you use strict version what happens when another team upgrades a remote you use to the next major version and they don't match up at runtime will your app error out in production yeah so it will get a runtime error immediately and this is a good thing because hopefully you run a sound set of integration tests before going into production and because this runtime error is thrown immediately you will very quickly find out that something does not work what about state management like nxurx it has global store which could get into a conflict yeah yeah that's true so normally you want to share nxurx the library the bits and bytes across your micro frontends because you don't want to load the store 10 times just because of having 10 micro frontends however as the micro frontends should be isolated from each other each and every micro frontend should only know its main branch so if this is here the app root the app app state the app state then create a main branch for micro frontend one create a main branch for micro frontend two and make sure they don't know each other and you can do this by using interface segregation that means you create one interface for the app state that is respecting this view and the second interface how to throw it is respecting that view and there is not an open app and so you can more or less guarantee that they don't start interacting without each other and this is really vital because we always have to think about why do we do micro frontends we use this architectural approach to decouple domains business domains from each other to from each other to the couple tiny parts of our overall system from each other and so they really should not know much about each other is creating a shareable ui library a common use of module federation yeah yeah i think so and this is not an issue because a shareable ui library is not use case specific your first and foremost wants to decouple use case specific stuff but sharing a framework or even a self-made ui library a design system is totally fine and i think it's even more or less required in order to provide a common look and feel yeah so the answer is totally yes yeah cool so we have a first running solution and it works kind of nice yeah it's true however there is no drawback currently we don't really like namely if we move to the webpack config of the shell currently the remote entry is hard coded and this is not as flexible as we want it to be that's why now let's move to what i'm calling dynamic federation dynamic federation means we get rid of this setting here we don't want to configure anything upfront for the shell instead we are moving over to our app roots file and here we are switching out this import for a helper function that is called load remote module and load remote module gives you or takes from you three pieces of data namely the remote entry which is the file with the meter data the remote name which is mfe1 in our case and the exposed module which is dot slash module in our case that means we are just providing the key data for data for loading something from over there at runtime here we are really providing strings and even though those strings are hard coded here they can be loaded from some backend perhaps we are using i call it a registry service perhaps the shell is hitting this register service on startup to get informed about currently available micro-frontends and for all of them we could create a route here dynamically perhaps we don't even know the amount of micro-frontends in this case we can completely dynamically set up our routing data i mean the routing table at the end of the day is just an array so setting it up dynamically is not a big yeah that should be everything of course i need to restart my shell because i've changed the webpack configuration so let's do this by the way implementing this helper script was a matter of one or two hours and then i needed about another two hours to decide upon the colors i want to display because i want to have pretty colors colors i like those colors are random colors but i want to have random colors i like you know and so i had to invest another two hours well let's have a look at this the micro front and the shell and when i'm clicking here the micro front end is loaded let's press f12 let's reload everything let's click on flights and now you'll see the micro front end is loaded on demand but also the remote entry yes the meter data and here you see boom here you see boom it has 28k i've promised you two or three case now we have 28k of meter data what's going on here well i tell you what's going on here debug mode it's going on here if we are in production mode then it will indeed only has two or three kings so you don't need to worry about this however there is another thing we should worry about namely loading the remote entry on demand is most of the times too late why is it too late well i've told you before that module federation is doing version negotiation it looks at the version we have here it looks at the version we have there and then it decides what's the highest compatible version this version negotiation as i call it as triggered when the application starts and version negotiation needs this meter data it contains the versions we use the versions of all the shared libraries and that's why it would be smart to load this remote entry point at the beginning when the application is bootstrapped because we need it for this negotiation in this case we cannot respect everything that's in here if this here came with a higher compatible version we could not respect it it's too late so in this case it's a good idea to skip this here and to move this to our main ts let's load it before the application bootstraps and now you have to be very strong because this is not the main ts you know look at this this is how your main ds looks like it is just importing a bootstrap file the bootstrap file and this main gs have been generated by the plugin when you engine edit this happens perhaps you have it on the console no it's already cleared it's told you that main ts is changed and that a bootstrap ds is created so what's bootstrap ds well it's your former main ts and at first sight this does not make sense at all why to go with a main ts that is just importing a bootstrap ds that in turn is just doing what the main ts did before what's going on now the important piece of this here is that this import is not a static import this is a dynamic import a dynamic import that returns the imported file using a promise you see in the case of an error i'm catching the error of this promise and this dynamic import gives module federation the opportunity to squeeze itself in here module federation squeezes itself in here it looks at the bootstrap file and at everything bootstrap importing and so it knows which versions to negotiate which versions for which libraries to negotiate here it knows we we need angular core so let's negotiate the version we want to use for angular core we use angular platform and so on and so first we even have indirect dependencies like app module is using i don't know angular router all of this is negotiated here and this is only possible because we have a dynamic import at the beginning and the good message is the plugin got you covered it generated all of this for you so there was a version in the chat if the strategy marik is asking if the strategy for dependencies versions will allow all versions what will happen if the version of micro front end one will change during runtime we'll shall install the dependency or download it from the micro front end one remote entry no the answer is nothing will happen we only know what we have in our meter data what we have in this meter data file we are loading into the shell but normally that's not a big deal because the deal because the user can press f5 and even though we have also a newer version in meantime each bundle has a hash value as part of its name and so there are no conflicts that means we can run different versions side by side this shell can run version 9 this shell can run version 10 so there is no magic for this okay so this is the right place to squeeze in our meter data and to load the meter data there is another helper function called load remote entry we are passing the name of the remote entry file the url and the name of the micro front end of course this is this is asynchronous one more time that means after loading this we can go on with that guy yeah and that's it first of all we are loading the remote entry file and then we are bootstrapping the application of course this does not need to be hard coded you can fetch this data using the fetch method from some registry hdbs my registry api micro front ends and perhaps this gives you all this data you need to load however you need to use fetch or xhr because you cannot use angular for this here you have an nx issue we did not even bootstrap angular because we need to decide which version to bootstrap and so we cannot use the angular based hdb client but fetch got you covered okay that should be everything let's try it out everything was reloaded i'm happy about this let's reload this here let's click on flights and yeah it works and now only the micro front end is loaded the meter data was loaded up front let's repeat this yeah it should be somewhere remote entry from over there locally host 3000 okay i have one more thing for you before we go into the first break and before we start with the exercise after the break namely communication between micro front ends sometimes you need to pass some data around your micro front ends perhaps it's a security token to have a mutual login perhaps it's some kind of key data who is the current customer what is the current time frame we are talking about if you think about an accounting solution then perhaps you want to share the time frame you are talking about you don't want to select the same time frame in each and every micro front end time and again you don't want to select the same the same user the same patient in the hospital time and again in each and every micro from so we need to share data and the good message is we already know everything to share data we just need a shared library a shared library that holds the shared data could just be a variable could be an object could also be some message bus subject if you think on rx jazz so that you can do messaging just share a library here can be your very own library my company that means i don't want to npm publish libraries i want to have everything in here in this folder structure let's go away save it please i want to have everything in here so the big question is how can we share something that is part of our monorebo well let's try it out for this i'm generating a library i'm calling it outlet authentication library and this authentication library they'll get an outlet service so for me this service is good enough and this service will just hold the current username so we don't have strict mode hence assigning now is completely fine and i have a login method it takes username and password and it logs in the username this is what i'm calling authentication for honest people i found out that authentication is far more easy if everyone is honest and of course for our purpose this is good enough username service dot t s logical federation service dot t s module federation plug-in example visual studio code oh what's what happened here did you hear this i think this was the screen reader yeah so it started talking with me okay so this here is my of lip service and hopefully it's exposed by the public api of our library yeah we are lucky so as this is a monorebo normally we create a path mapping a path mapping pointing to this library above mapping that looks like an ordinary nvm package but in real world it is just pointing to this library for this let's jump into the ds config jason and here we have it or flip normally when going with the angular cli it points to the distribution folder when going with an axe you should really look into it it points to the source folder and this is what i'm preferring because i don't want to recompile everything after each change so let's go to off lip the projects off lip do i have in deli sense for stuff like this here and there should be the public api somewhere it's even within the source folder public api ds this is how normally a monorebo works we are defining names that look like the names of nvm packages and they point to this or that file we could even use some prefix something like this here totally fine okay now let's restart the type script language service because studio code is only reading this file once namely when we start studio code or if we open a folder that's why we need to restart the type script language service so that this new setting is red and now we can use it so i just use it as in each and every monorebo i'm going to my shell my shell has somewhere a app component i'm using it in the constructor how was it called out lip service out lip service i love in deli sense i even got the right map name which is beautiful and here i'm saying hey people i am manfred password i don't know yeah and over there in my micro front end i want to do the same but i want to read this username let's go over to the micro front end and flight search is beautiful here ignore everything that's in here private this and that i love it because very often in deli sense has an issue but today it really works what a beautiful day and you excuse me this is not a reactive approach it would be even more beautiful if it was reactive with a behavior subject but for the time being let's go with this non-reactive approach let's fetch the username into something i'm calling username and then let's bind the username to our output current user this and that so in this case this works but nothing will be shared perhaps we can look at this at runtime we are here we are there that we have an error nothing is loaded anymore we are here we are there but you see the username is not shared and the reason for this is that both the micro front end and the shell is separately compiled and we did not tell module federation to share this authentication library holding the username at runtime that means we need to configure module federation to share we need to configure module federation to share everything at runtime and as mentioned before normally you would place here your npm package however our out library is not an npm package it just has a name that looks like the name of an npm package well i discussed this with the webpack team the founder of webpack is from germany so it's quite easy for me to discuss with him and he told me there is a way to do this you can tweak module federation to assume that a library is just in a folder somewhere here or there and the tooling here is just doing the heavy lifting for you everything you need to do to get started with this is register the name the map name here in this array and the rest is done by the tooling by this plugin in a way the founder of webpack told me it should work yeah that's everything the magic is done underneath the covers and of course we need to do this twice each and every micro front end and shell needs to opt in sharing that means i have to mention it on both sides but that's quite the same situation as with the other shared libraries both sides or all sides need to opt in okay that's it let's cross fingers let's npm run all this again yeah you know this lock from the cli everything should be fine does not know a thing of course but the shell is setting the username i'm switching over here and hey here it is it means we really manage to share data saying this don't share too much data normally a handful of properties should be enough because as mentioned before you don't want to couple everything to each other this architectural style is for decoupling and if you share a whole store or a lot of properties you create coupling something you wanted to prevent nice do we have any questions so far yeah you can totally share data using a local storage or an index db or session storage that's totally fine everything here runs in the origin of the shell that means each and every micro front end will have access to the shells index db local storage session storage that's really totally fine they even have events that notify you if the key changes and for instance i really like to do this to share access tokens because normally existing libraries use one of those mechanisms to save an access token and that's why sharing it is quite straightforward so danis is asking if you use load remote entry and load remote module do you still need webpack config yeah you still need the webpack config because you need to define what to share you need to define the key data for the remote for the micro front end you need to define the key data for sharing for the shell everything you don't need when going with dynamic federation is this here defining the pointers the mappings from the shell to the micro front ends up front with dynamic federation this happens at runtime are there any other questions okay awesome so oh yeah there's one more or two more questions do you know some company that already uses webpack 5 module federation in production oh yeah i know several companies that are using it a lot of companies have been looking forward to it because it really lowers the pain of them and i cannot tell your names of course but beyond those companies you'll find big software vendors international ones big banks big insurance companies also companies from the industry and telecommunication so really a lot of companies are looking forward to this and yeah some of my customers are early adopters for this and it really makes sense if you want to scale your teams if you only have one or two teams this really might be an overkill especially because you are exchanging compile time dependencies for runtime dependencies are there any further questions before we proceed with a break and with exercises now it's your turn now it's time for you to try everything out i've shown you so far and for this you will get your lab descriptions by the way one of the first things you will do in your lab is this here this here and she adds angular architects module federation and for some reason on my machine i always get an old version and i see this when looking at this line here when this yellow error message comes or let's call it warning it's a warning then you have an old version of this library in this case you need to add at latest then everything should be fine so i would be really curious if you need this at latest this at latest if this comes this yellow warning then you need it then ctrl c cancel it and repeat it with at latest and perhaps you can tell me via the chat if you need that this at latest or not okay so let's get started with the lab the lab by the way guides you through all the steps there is a first part a second part a third part and i would say everything after part four everything after part four is a bonus that means the goal is that everyone makes part one two three and if you have some time part four and five is some time but four and five is also fine but the goal is that everyone makes part one two three if you have any questions feel free to reach out to me i can open a breakout session so that we can do a one-on-one to solve your very issue to share your screen to discuss your current situation so don't be shy reach out to me and here you have the url here you have the url enjoy that i have one question for you if i may yeah um yeah i want to go forward to reactjs actually with that but i intentionally joined here because i just wanted to see how does it work for angular yeah and uh i know that you wrote this plugin architect angular architect right so do you need similar thing or is it already available on react for reactjs yeah so the good thing when it comes to reactjs is that reactjs is not shielding that back from you even if you start with create react you can eject from uh create react and directly interact with your webpack configuration and that means you can just take the generated or the handmade webpack configuration and add to it what we added today with the plugin uh something like those settings here so if you merge what you learn today with what you already have in your react application your existing webpack config then everything should be fine okay thanks very cool presentation thanks yeah thank you okay great it's nine p.m so let's go on with some additional advanced stuff so first of all i have a little present for you i have this ebook and meanwhile it has about 100 pages and it's the third edition and about the half of this book is about what we talked about today it's about module federation and micro frontends the other half is about nx and domain driven design or to say the least about strategic design if you want to read this then you can find it here here you have the hyperlink okay that's the first thing i wanted to tell you the second thing is so far we've worked with this plugin angular architects module federation which is enabling module federation for the angular cli recently i've published another package which is called module federation tools and originally i didn't want to publish it but i really saw a need for it because some people want to do something like this here they want to mix and match different versions they want to mix and match different frameworks i mean this here is an extreme example but i think it shows the point it shows that it's possible if you combine web components with module federation because when doing so and i think this graphic here shows it you get the best of both worlds when doing so you can share libraries across different applications if they use the same version or a compatible version here for instance i'm sharing angular in the version x between the shell and the micro frontends and i'm using angular y for micro frontend 2 and micro frontend 3 of course this is a hit on the bundle size but it would be even worse if each and every micro frontend came with its very own angular version when possible we are sharing and because we have web components we can also hide the used angular version or the used framework the rest of the application does not care and so we can combine react with angular with other frameworks however doing something like this increases the overall complexity and calls for some workarounds and that's why i would think twice or even three times before implementing something like this but if you want to implement it it's possible however when teaching this i really figured it was somehow hard to teach this because i needed to talk about so many workarounds to make this work in a more or less seamless way and to prevent you from using those workarounds i've created this library this library is shielding those workarounds from you it is hiding those workarounds behind some helper functions so before i show you what this library does for you let me show this all in action meanwhile i have updated our project i had some time during you did the labs and now i have some additional routes like this react route which is loading a micro frontend wrapped in a web component using react here i'm using a different angular version version 12.3 here i'm using the same version the shell is using 12.0.0 here i'm using 12.03 this is also a web component which has its own router which brings another challenge because now somehow we need to tell the shell that the shell is only interested into this part of the route and the inner router is interested into the rest yeah and just for the sake of completeness here i have a few web component and angular jazz which shows that all of this can be used for upgrading your applications upgrade it little by little slice by slice domain by domain as you want to call it well there is something that's really interesting here when it comes to this scenario and it's about bundle sizes so let me have a look at the network tab let me reload the welcome page and let's load our flights web component as mentioned before this web component is really slim it has about 11k because here the angular framework itself is shared with the shell we don't need to load angular one more time angular itself is shared with the shell if we load react it's hidden within a web component then we see of course we need to load direct ones once which has about 40k and then we have some additional case for the application itself if i am loading another web component using a different angular version then here we see we need to load angular common angular core look at this here angular common angular core and so on and so forth and if you look at the numbers here it's quite huge we need to load another 100k for this very angular version because here as mentioned i'm using 1203 and the shell is using 120 perhaps you're saying now well they should be compatible to each other yeah they should be compatible to each other and they are but just to show this how it behaves if we have different angular versions i did remove this rooftop with this rooftop everything would be okay in this case version negotiation would tell the micro front and shell to just go with the highest compatible one i've removed this and so 1203 is not compatible to 1200 and that's why it's here loaded one more time however if i load the second micro front end using 1203 only the source code of the application is loaded because angular 1203 is already loaded it came with this angular one micro front end here so we can share it even though it's an additional angular version we can share it across our micro front ends and the same happens here with angular three only the source code is loaded the rest was already loaded with the other angular micro front ends and for the sake of completeness here we have few it's quite a tiny framework and angular chess which tells us we can use this for migrating everything step by step so to make this work you need some workarounds as mentioned those workarounds are hidden by my tools library you can use it the first screen here or the first example in the readme shows you how to expose an angular application as a web component and this is indeed quite straightforward you need this package angular elements it provides you with a method called create custom element and in your app module you can create then this ng-do bootstrap method it's automatically called by angular if you don't have any bootstrap components and look what happens here i'm calling great custom element an angular component goes in a custom element goes out a custom element or as most people would say a web component and then we are registering this custom element with a api/talks">browser api under this or that name so this is what it takes to wrap an angular component as a web combo perhaps you're wondering how to do something like this with angular js or with react well you just need to write such a component by hand and then wrap it with a handmade web component i have even an example for this for instance the series react i'm not a react expert so i'm sure i did something that should not be done however this is everything you need to do to wrap and react component inside of a web component create a naked a naked web component which is a class extending html element html element has this method here connected callback it is called when the web component is added to the dome and within here you can render your react component into this which is the web component itself so everything you need is this tiny wrapper and then you need to register it under this or that name and you are in business and obviously the same works with angular js and all the other frameworks so what we can do now is we can expose the whole bootstrap ds file the whole file that is bootstrapping our angular application or our react application or our view application under a given name using module federation which would be web components here to deal with all the workarounds we need to do when bootstrapping the application this library has this bootstrap method it takes care of all those tiny workarounds that needs to be drawn in consideration just call it instead of bootstrapping your angular application in a traditional way and then it's becoming easy then you can directly route to your web components for this you have a web component wrapper and this allows you to define the key data of your web components of your micro frontends of your module federation remotes using this data object which is always part of your router configuration and you see here we have done this before we are pointing to the remote entry we are pointing to the remote name to the exposed module and we are also pointing to the name of the exposed web component and that's it now you can route to the wrapper component and the wrapper component will load this very web component on demand and if you look through this readme you will find some additional helper methods which eases the pain of doing a scenario like this i even have a tutorial for this i think we will skip this tutorial for today because it's already quite late and the tutorial is quite easy it's about introducing several routes pointing to this or that web component deployed to the internet and then adding some hyperlinks for those routes but if you want to do this tutorial feel free to go through it of course i'm giving it to you please find it now in the chat bonus tutorial bonus tutorial feel free to go through it if you really need a scenario like this okay and that's it so thanks for coming i hope you enjoyed it have a nice day and perhaps who knows we will see each other again at this or that event
113 min
17 Jun, 2021

Watch more workshops on topic

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career