GOTO - Today, Tomorrow and the Future

Building Micro-Frontends • Luca Mezzalira & Lucas Dohmen

May 06, 2022 Luca Mezzalira, Lucas Dohmen & GOTO Season 2 Episode 17
GOTO - Today, Tomorrow and the Future
Building Micro-Frontends • Luca Mezzalira & Lucas Dohmen
Show Notes Transcript Chapter Markers

This interview was recorded for the GOTO Book Club.
gotopia.tech/bookclub

Luca Mezzalira - Author of "Building Micro-Frontends" and Principal Solutions Architect at AWS
Lucas Dohmen - Senior Consultant at INNOQ and Podcast Host at CaSE

You can find more details about Luca here:
buildingmicrofrontends.com

DESCRIPTION
Micro-frontends are more than just a buzzword. In this GOTO podcast episode, Luca Mezzalira, author of “Building Micro-Frontends,” shares best practices around how to use them. The conversation also sheds light on some key terms like SSI and ESI. 
The interview is based on Luca's book "Building Micro-Frontends"

Read the full transcription of the interview here

RECOMMENDED BOOKS
Luca Mezzalira • Building Micro-Frontends
Luca Mezzalira • Front-End Reactive Architectures
Noel Rappin • Modern Front-End Development for Rails
Michael Geers • Micro Frontends in Action
Jeremy Fairbank • Programming Elm

Twitter
LinkedIn
Facebook

Looking for a unique learning experience?
Attend the next GOTO conference near you! Get your ticket at gotopia.tech

SUBSCRIBE TO OUR YOUTUBE CHANNEL - new videos posted almost daily.

Twitter
Instagram
LinkedIn
Facebook

Looking for a unique learning experience?
Attend the next GOTO conference near you! Get your ticket: gotopia.tech

SUBSCRIBE TO OUR YOUTUBE CHANNEL - new videos posted daily!

Intro

Lucas Dohmen: Hello, and welcome to the GOTO Book Club. Today, we will talk about “Building Micro-Frontends” and I've invited Luca the author here. Welcome to the show, Luca.

Luca Mezzalira: All right. Thank you for having me.

Lucas Dohmen: My name is Lucas Dohmen. I'm a senior consultant from INNOQ, and I'm also very interested in frontends and how to build good architectures for web applications, especially big ones. Luca, maybe you can introduce yourself.

Luca Mezzalira: Sure. My name is Luca Mezzalira. I'm Italian but based in London. I'm a principal solution architect in AWS and in the last seven years, I have explored the micro-frontends topic quite extensively, not only writing a book but also building multiple applications and helping customers to implement these architecture patterns across different industries, seeing different challenges and figuring out how to overcome them. So it has been a very interesting journey so far. So I hope that will continue.

What problems do micro-frontends solve?

Lucas Dohmen: Awesome. Micro-frontends is already a word where different people have different ideas of what it means. But before we get into that, I want to start with your motivation. Why are you looking into this topic? What are the problems that you've seen that led you to explore micro-frontends?

Luca Mezzalira: Seven years ago, I was working for a company called DAZN, which is an OTT streaming platform. Imagine Netflix for sport. Our focus was mainly live content. It was available in multiple countries that have different needs. We started to grow significantly moving from tens to hundreds of people in the tech department. The challenge we had is that we don't only develop content for web, but also for living room devices, so set-top boxes, consoles, smart TVs. And therefore, we need a variety of teams that could handle all the challenges. And moreover, the team was distributed.

On frontend, I have worked in the past on several projects that require tens, if not even hundreds of people working together for delivering a project. But when you have a monolithic codebase, sometimes you have some challenges that are mainly due to the fact that there are some decisions that are pertinent and made at some point in time, but then reverting or changing those decisions across the entire code base is a challenge. And instead, what we were seeing on the back end is that there was distributed architecture that allowed different people to have certain modularity and flexibility that we didn't have on the frontend. So back in the days, I was asking myself, "Can we figure out a way to have the same flexibility of microservices, for instance, on the frontend?"

Independent deployability

Lucas Dohmen: I think one of the things that's the most important part of both microservices and micro-frontends is the idea of independent deployments. So the idea is that a team can decide for themselves, that they can deploy something without the other teams needing to do something about it. Maybe you can talk a bit about that topic as well because I think it's very important for our conversation today.

Luca Mezzalira: Sure. One of the characteristics of a distributed system, like if we think about microservices, as well as micro-frontends, is exactly what you described. So the fact that our independent artifacts, and that is not only helpful from a technology perspective, but also from an organizational perspective. One thing that I have noticed in the past is that very often, people forget that architecture is tightly linked with the organization structure. And that is something we cannot forget.

Conway's Law, for instance, states that we usually design our architecture, or our system based on how our company is structured. That is something that was stated in the '70s and is still very actual, in my opinion, and we need to take that into account. The fact that we want to have a distributed system, means that we need to reduce the external dependencies for the team. Because otherwise, it's creating more overhead in coordinating the effort. That is not only for independent deployments, but it's also for sharing libraries and many other things that we used to do when we deal with certain types of projects.

Now, specifically on independent deployments, I think is quite key that we are trying to use best practices that we have learned in the microservices world and apply, if possible, on the frontend for defining some boundaries around the micro-frontend input and output, for instance, and then having the possibility to independently deploy at our own pace without the need of massive coordination. That doesn't mean it is always possible. It means that for the vast majority of the time, a team’s day-to-day is that I'm independent, I can make my decision, I can go ahead and deploy multiple times per day or every day, whatever is the cadence that they prefer.

Whether there are certain situations, for instance, when there are features that are across multiple domains of our application, or we have a massive change like a design system that is changing drastically, we need to do some coordination across teams. That is inevitable and is going to happen. But if we can reduce it to less than 5 or 10 times per year, then we will have a big success. Because every team is independent for a vast majority of the year, and then will be able to take their own decision and move forward with what really matters at the end for a company. So generating value for the users. Because we are often forgetting that we are here not only to write amazing code, but also for generating value for our customers. And that, for me, is the key thing that we need to focus on, especially nowadays where applications are becoming more complex and users are requiring, let's say, specific features and richer features if you want. And in this case, I think modularizing our architecture is a key characteristic for any application for the future.

Where do distributed systems fit in?

Lucas Dohmen: One thing that you also outlined in your book is that, of course, splitting up your system into a lot of different systems comes at a cost. It's not free to split up your system. You need to integrate it, and we will talk about that at length in this conversation. So you have to do a lot of decisions. Where do you come down on the decision on when to do the split? Do you think you should always start with a distributed system like a micro-frontend microservices system? Or do you think there is a good reason to start with a monolith and split it up later? Where do you think you come down on that?

Luca Mezzalira: Yeah, let's start stating that I don't believe distributed systems are a silver bullet at all. I would say that that is more a way to solve an organizational challenge and not only a technical challenge. Probably mainly an organizational challenge than a technical one. I think there is a lot of value nowadays working with monolithic architectures, or a modular monolith even better, as was described several times by Sam Newman in one of the books that he wrote, "Building Microservices" and "Monolith to Microservices."

I think in general, the idea is you need to really think about your context and find which is the right architecture pattern for what you need to achieve. Sometimes for a startup, having a quick turnaround makes way more sense than starting with microservices or micro-frontends. Because you don't even know if your product will reach prime time. And therefore, it is very, very important that when we think about the architecture part and implement it, we need to think about our priorities. And often for startups is validating their assumption or idea. How many customers are interested in that? And then set up the code and the architecture in a way that the modularity characteristics that we discussed before could be leveraged at scale.

In reality, if we think about architecture characteristics, I think modularity is one topic that we talked about for several years. And I created the mental model in my head that you can reach a different level, you can have the code level, you can have the infrastructure level, you can have at the architectural level and organization level. And those three things don't have to be achieved in the first iteration. You can start with a monolithic architecture with a modular code base, and slowly but steadily move into more granular modularity on the infrastructure. And then slowly but steadily you move towards the architecture that I believe is the last phase where you have modularity on the code, on the infrastructure, on the architecture for the organization based on the assumption that we design our architecture based on organizational structure?

If that is true, it means that you can achieve some of the benefits that you can have with distributed systems also with a monolithic codebase, or whether require more discipline or require certain coordination that you might not have to think about when you go further about the abstraction that you are talking about. I think also with a monolithic codebase, you can achieve some benefit of modularity at the codebase level, and then that will be your foundation for moving towards.

Splitting your application: horizontal vs vertical split

Lucas Dohmen: Very nice. After talking about the motivation, I think we should shift our focus a bit to micro-frontends and away from microservices. In your book, you wrote that there are basically two big categories of doing micro-frontends. And this also will lead us to what exactly that means. I prepared this visualization, which I stole from your book, which shows the horizontal split versus the vertical split, right? Can you explain to us how you can split your application into either horizontal or vertical splits, and what are the benefits of those two approaches?

Luca Mezzalira: Sure. When I started my journey in this world of micro-frontends, obviously, I struggled to find some content available online. And there weren't many companies doing that. So I had to figure out and create some mental models that would help me and other developers working with me to visualize what we were talking about. And I created what I call the decision's framework in 2019. Basically, one of the decisions is the one that you mentioned. So the vertical versus horizontal split. And that is the first decision. So when you are approaching micro-frontends, you need to understand how granular you want to go. That doesn't mean you cannot mix and match both, you can definitely mix and match both. But there are certain situations where one makes more sense than the other one.

A horizontal split is when you have multiple teams working together inside the same view. So if you reach a homepage of a website and you realize that there are multiple teams working on that, because your organization is quite large, and you have multiple domains that the homepage is covering, or you have, by the type of system, a certain level of granularity and reusability on micro-frontends, that is definitely an architecture that I encourage. A classic example, for instance, think about an observability dashboard where you have multiple elements that can, in a homepage, correspond to the throughputs that you have, or the error rates and other things. So those are multiple domains that are contributing to providing a final view. And those domains, obviously, are very likely to be handled by different teams. So in order to aggregate that, you can use a horizontal split where you collect different metrics to provide a view to the final user.

On the other side, we have a vertical split, where instead, a team is responsible for one view or multiple views. Depends on the type of application that you have. So, for instance, if we continue with the example of the observability dashboard, maybe you go to the homepage and select error rates as a metric that you want to deep dive and understand better how to fix. So at that level there, you can open it and you see that it’s owned by the team responsible for the error rates that goes more in-depth about the visualization, the charts that you want to display, and maybe you can do some queries, and search, and other things. So in that case, you can have a mixed approach where you have a team that is responsible for the vertical split of the application and the same team responsible for the smaller view or the snapshot of the view of the error rate that contributes to the homepage. So as you can see, you can have both.

Obviously, those approaches have different pros and cons. In horizontal split, I have seen more and more people that are investing in tooling for providing capabilities for the team that is, let's say, rendering just a portion of the view, some traction to understand if the micro-frontend that they develop is working. And also there are some challenges around the organizational structure. Because imagine that you're responsible for just one micro-frontend in the view that is composed of, I don't know, five micro-frontends. Let's assume this example. How do you ensure that your micro-frontend is working? I have seen companies that are decoupling the teams and having a Q&A session that is owned by the Q&A team for making sure that the application is working as a whole.

And on the other side, I've seen teams that instead are creating tools for making sure that the application or the micro-frontend is working in conjunction with other micro-frontends. And therefore, the investment around ephemeral environments, where in that case, basically, you spin up a snapshot of the system, maybe retrieving other micro-frontends from a more stable version, like staging environment, for instance, and your micro-frontend is working alongside. But as you can see, there are more considerations to take into account when we work in a horizontal split.

In a vertical split, it is more likely that you feel comfortable if you have developed single-page applications, for instance. That is usually the scenario where if you're capable of developing or you have experience in developing a single-page application, that is potentially easier to pick up micro-frontends. Because in the end, you're going to be responsible for a portion of the system. You can use the whole JavaScript ecosystem without any problem. And moreover, I think it is a nice way to ramp up with this idea of micro-frontends. Obviously, it depends on your maturity level inside the team and what you're trying to achieve, but I have seen successful implementations of vertical split as well as horizontal split.

Single-page app vs Rich page app

Lucas Dohmen: So one thing that I noticed in a lot of conversations is that there are people that have different ideas about what a single-page application means. So I think what you're referring to here is a rich client application with a lot of JavaScript maybe written in React, or Angular, or something like that.

Luca Mezzalira: That's correct. So for me, a single-page application is an application that loads just once all the package that needs, and then the only round trip that is done to the server is for consuming some APIs. Then, obviously, you can argue that nowadays, there are people using lazy loading different chunks of JavaScript. But in the original concept of the single-page application, the idea was we were moving away from the fact that every time that the user was changing the endpoint was refreshing the entire page, we were downloading in a single-page application, the entire package, and then at the end, you're going to have the entire application sitting on your browser.

Lucas Dohmen: But I think there's still a lot of movement there, especially with things like hot wire and technologies like that, where the line is not so easy to draw between server-side rendering and single-page applications. But I think we have a rough picture of what we mean. It's a very client-driven application, probably with routing on the client-side and not on the edge or on the server.

Luca Mezzalira: It could be either way. So the interesting part of the vertical split, for instance, if we want to go ahead with that topic, is that usually the routing part could happen on the client-side or it could happen at the edge side. I have seen it working both ways. If I think architecturally speaking, having it at the edge side provides a nice decoupling between the container of these micro-frontends, usually, it's called application shell, and the routing mechanism. If it is on the client-side, it might be non-trivial. Because there are a few things that you need to take into account. In the end, you are consuming, the vast majority of time, an endpoint for retrieving the catalog or micro-frontends that are available, and then you use some logic that you add in your code for the application shell.

The challenge you have if you do on the client-side, is you're not paying too much attention to decoupling in a nice way, this catalog of elements with the mechanism that does the routing is the fact that you need to deploy the application shell alongside some micro-frontends every time. That I have seen as an anti-pattern because basically, you're creating a coupling between the container of your micro-frontends and the micro-frontends itself that represent a piece of the domain. If you do it on the edge side instead or server-side, depends if you want to go up to the origin, the nice thing is that when there is a request from the client, the logic running on the server is just retrieving the right artifacts. And that opens up two possible solutions like kind of releases, blue-green deployment, or even strangler pattern, if you want, for migrating an existing legacy application or monolithic codebase towards a new micro-frontend.

What is an app shell?

Lucas Dohmen: Awesome. Micro-frontends is already a word where different people have different ideas of what it means. But before we get into that, I would say let's first look into the app shell, I think most people are not aware of what exactly that means. Can you explain what an app shell is?

Luca Mezzalira: Sure. The application shell is the first thing in a fat client, as it is called, or rich client-side application that you usually download. So when you type your mysite.com, the first thing that you download in that case is an application shell. The application shell is unaware of the business domain, or it should be as much as possible because that is a common part. And what it is responsible for is loading the micro-frontends and routing the URL basically on where the user wants to go, and then loading the right micro-frontends associated with that. It's also responsible for delinking, that usually comes for free out of the box when we are implementing the application shell and is usually used either within the horizontal or vertical split. So the application shell, when we do on the client-side, we can decide to go with horizontal or vertical or both, as we said before.

The interesting part is that with the very light implementation, there are several technologies that you can handle. In my humble opinion, usually, if you are capable of keeping that as framework-agnostic as possible, it opens up also the possibility in the future to evolve the micro-frontends without the coordination of having the application shell that is holding you to experiment.

What framework to use for micro-frontends?

Lucas Dohmen: Let's dive into that topic as well. Because I think one thing that I've come across a lot is the question, "Can I use different frameworks for different parts of my micro-frontend? Or should I use the same one for everyone? Should I use Angular and React mixed, and some use Angular and some React? Or should I always use React for all of them?" Not looking at Angular or React right now. But where do you see both in the vertical and in the horizontal split with the app shell, how do you see how you can handle that?

Luca Mezzalira: Despite the architectural pattern that you're going to take on micro-frontends, I would say that is a misconception that was raised by the community for several years now. The challenge I see, and usually is this example that I made. So imagine that you have a single-page application. Technically speaking, you can mix React and Angular. And I remember back in the days when React came out, there were some experiments on that using the React library as UI and the rest of the Angular framework for the state management and the rest of the application.

Now, despite that you can, it doesn't mean that you have to. Therefore, in my opinion, there are certain situations where having multiple frameworks might help with micro-frontends. So when you are migrating, for instance, from a legacy application to a new one, or if you are migrating an application from an older version of the framework to a new one, what you want to do, instead of going stealth mode for a month and then deliver something in front of your customer, is taking a vertical of your application and start to iterate on that. Deploying production alongside the previous application and starting to generate value for the users because we are going back to the real scope of why we are developing code. And that's where I can see for a certain period of time because I know that is a migration, a mix and match of frameworks.

And another option could be when you are acquiring your company. So if your company's acquiring another one, and you want to immediately see the value of your investment inside your, let's say, brand, inside your umbrella, you can do that. And you can easily have another, let's say, technology that is developed by the other company that is living alongside your application. That is another situation. Overall, I discourage this practice unless there is strong reasoning behind that. Because, obviously, it’s going to have an impact on your users. And at the end of the day, we know that for a certain type of workload, think about, for instance, e-commerce, the first byte is quite important because it could generate more revenue or not. And I know that sometimes it could seem silly, what I'm saying, that one second could generate millions in revenue. But in reality, we have quite a lot of companies that prove that that is true. And therefore, it's very important that we optimize our application, despite the architecture we're using.

Lucas Dohmen: I agree. We should not go to a multi-framework solution because we are downloading way too much data. And it will also be complicated for things like server-side pre-rendering and stuff like that. I think that a lot of people that are doing architectures for the frontend are paying not enough attention to the fact that you already mentioned — migrations. If we are going into a situation where we need to do a framework update that is a breaking change, for example, and we are forced to do it across our entire application, then we are paying all the costs that we have for a monolith. Because then we need to update all at once. And that might be costly.

And the same goes for if you remember something like BackboneJS, that was the first framework I built a single-page application with, nobody would use it today, I would say. And sometimes those frameworks go out of style. And we cannot say which one will maybe be at end-of-life by the people behind it. It's not very likely that React and Angular will just disappear because there are so many applications written in it. But we should at least keep it in the back of our minds that this might happen. And that we have a strategy, if that occurs. And especially for the framework updates, the breaking changes will be something you should pay attention to, in my opinion.

Luca Mezzalira: Yeah, I fully agree. And also, because often we need to think about if, technically, architects and developers are keener to work with modularity, the business instead wants to work with agility. So they want to drift the direction in the way that they want. And if we link agility with modularity, basically, you have exactly what we are talking about here. You have the possibility to take the drift of the organization towards your architecture, and therefore your code, and shift the way how you're working today for having a better future tomorrow. That is, let's say, probably the key thing that an architect should do, try to map the business characteristics towards what we're trying to build. But yeah, I agree with you in your mindset.

Lucas Dohmen: App shell would be one integration method. One other thing you brought up on the client-side horizontal split was the Module Federation, which is a feature from webpack. Can you explain what it does on a high level, not on a code level, because it's always a bit tricky on a format like this one, what it means and what it does?

Luca Mezzalira: Indeed. Yeah, sure. So Module Federation is an official webpack plugin that is available from webpack 5. The interesting part that it does is not mainly for micro-frontends. The idea behind Module Federation is the fact that it’s a transport layer for bundles. And that opens up quite a lot of opportunities, not only on the client-side but also if you think about cloud or server-side, you can still use Module Federation, no problems. The interesting bit for micro-frontends is that if I talk about the transport layer for bundles, the bundles could be an independent piece of code. And therefore, it resides very well with also micro-frontends. And that means potentially, I can use Module Federation for composing my micro-frontends. What it does is exactly this. I take a bundle that is independent, and I, let's say, load it inside another application. That is exactly what we want to do with micro-frontends.

The other great thing that Module Federation does, is taking care of all the dependencies. So one of the challenges you have when you deal with, let's say, your own implementation of micro-frontends is how can I make sure that everyone is using React 15 or React 16, or the same version of a framework or library whatever? Module Federation obstructs that for you but goes even further, providing you the possibility to, let's say, create a scope around the same library with different versions. So if a team wants to use, I don't know, MomentJS with an older version, compared to the rest of the application, they can do that without crashing because the scope is different. And that is another great functionality of Module Federation.

I think the interesting bit is very foundational, going to a level where yes, you are tight with webpack 5, there is a version for webpack 4 that doesn't have the full fledge of features that I described. There is another one on ESPNode that I know that is about to be built or in progress. And there are other open-source frameworks that are doing something similar without using webpack. But the overall idea of creating a way that abstracts the loading of a specific module or a specific bundle is pretty good. Because the other thing we often forgot to say about Module Federation is because it's a webpack plugin, we can mix and match with the rest of the plugins available in the ecosystem. Therefore, if Module Federation generates too many chunks of your JavaScript, you can configure it in a way that instead you have just, let's say, one chunk or a smaller chunk, or you want to set a certain budget per chunk, every chunk has to be at least 15 kilobytes, stuff like that. So you have great flexibility and you’re solving quite a peculiar problem of micro-frontends.

Lucas Dohmen: But doesn't that mean that the build of those different subsystems or different micro-frontends are bound to each other, or are they still independently buildable? So can I just build the component that does the product view, for example? Or is that not possible anymore?

Luca Mezzalira: It's totally possible. Because the whole point of Module Federation is moving away from the concept that I need to centralize the build of my elements. I can use Module Federation. There are two concepts that are key, the host, that is the container of, let's say, another bundle, and the remote that is what is loaded inside the host. The beauty of this approach is that you can create multiple applications completely independent that are living inside their own work, and then load them together at runtime. And all the loading parts are handled by Module Federation. Let's assume that you're using an S3 bucket with a CloudFront distribution in front of it, you deploy your micro-frontends tens of times per day in S3, and every time that application shell is loading with Module Federation, is going to go to the latest version of your micro-frontend. The beauty of this is you're completely independent despite you're using webpack.

Lucas Dohmen: All we need to align on is the build tool that we are using. Right? So we are bound to the same version of webpack for all micro-frontends and not webpack 5.110 or something, right? Okay, cool.

Luca Mezzalira: No, no, it's just webpack 5. It should be retro-compatible. I didn't try that, but to be honest, I never heard anything like that.

Use iFrames for a horizontal split?

Lucas Dohmen: Okay. So the second approach that you outlined in the book is using iFrames for a horizontal split. So I think a lot of people when they hear iFrames, think this is like an outdated technology that nobody uses today. What's your take on that?

Luca Mezzalira: I agree in certain contexts. In others, they are solving quite a few problems. So iFrames, yes, it’s true they are not new but they are the safest option that we have to create strong boundaries and have better control for our code. So if, for instance, you acquire a company and the code that maybe is an old jQuery with other things that you want to put inside your brand new micro-frontends application, but you don't want to have the headache to go through the code and build anything, you take the build that is available currently in the application, you move in your server, and you use an iFrame to basically protect the rest of your application. That is a nice way.

There is a micro-frontends framework called Luigi framework that was created by SAP that is using iFrames for isolating the different contexts. And usually, what they do is use this iFrame, there are not many per view, there are three or four. And they're using that because they are sure that whoever is developing the code is not going to clash with the rest. And they provide a set of facilities in this Luigi framework for the communication part, retrieving the authentication and stuff like that. The other option that I've seen iFrames very useful is on the security side. Because there are certain, let's say, industries that require a strong set of securities. And with iFrame, you can fulfill them very easily. And therefore, I agree there are several drawbacks on iFrame. They're quite heavy and so on and so forth, but there are situations where iFrames can be a valuable option.

The other thing is currently, the TC39 is exploring a proposal that's called ShadowRealms. They changed the name recently. And the idea is basically having a glorified iFrame but without all the burden and the problems that we have with the iFrame. So you don't have to have the full structure of the wind of the object and so on. But you have, like, a light iFrame with all the benefits of creating boundaries around your code.

Lucas Dohmen: I've never seen anyone use iFrames to integrate within one company. But I think, especially using it between companies if you are integrating something from a different company, then iFrames are a very good solution. I think a good example of that is Google Maps. Everyone uses iFrames to integrate Google Maps, right? They don't realize it because they put in a script source. But it's a good way, for security reasons, to split out this application, and don't give it access to the entire DOM that you have. But I would say it's rather an unintuitive way to integrate within your company where you have higher levels of trust, I would say.

Luca Mezzalira: Yes, it's true. But very often, in large organizations, I've seen used iFrames despite there being now these other options for security reasons.

Lucas Dohmen: So one other thing is web components. And I think this is also one of those words where people are not entirely sure what it means. Can you explain what the pillars of web components are?

Luca Mezzalira: Web components are an API standard that is available for developers to create components that are compatible with nowadays all the browsers, if not, the majority of them, and is providing some utilities like custom components and Shadow DOM. And those two things are quite key for micro-frontends. So the custom component is the possibility to create your own components that contain your code. And the interesting bit is you can have it in Light DOM or in Shadow DOM, depends on which path you're going to take. And the main difference is the Shadow DOM basically is completely decoupled from the main DOM element. And that means, basically, that you can, for instance, have also some duplication on, classic example, CSS style. They are not going to override the one that is available or be overridden by what the application is using. The Shadow DOM is basically like a nice black box that prevents someone from accessing it.

I think the interesting part of web components is that nowadays if you're using them they are compatible with any framework. So if you're making an effort for building some elements with web components, then you know that if today it's React and tomorrow will be VueJS, you will be able to reuse that part of the code. That is great. In fact, also in my book, I warmly recommend them if it's possible to use them for creating design systems. Because that basically will allow you to reuse a portion of your code and your application despite the underlying UI framework could change. That for me, in my head, creates also this concept of evolutionary architecture the web components can provide.

The only challenge I have seen so far with web components for micro-frontends is that often developers are overlapping this concept of components and micro-frontends. And that is a risk because sometimes you might hear people say, "Oh, yes, we are using micro-frontends. And we have a gazillion of micro-frontends in the same view." But in reality, they're using components. And a rule of thumb that I try to rationalize lately probably was towards the end of the book, I was trying to figure out how I can explain easily the difference between components and micro-frontends. And the idea is, I think the key here is the extensibility factor. If you think about the component, usually the container is providing some context for the component to behave. In that case, you pass maybe, I don't know, the labels, the localization, the type of behavior you expect from the component, and so on, so forth.

In micro-frontends, you define the input and output. Worst-case scenario, you can inject an event emitter or, let's say, something that's very not domain related, but the micro-frontend owns his narrative. He knows how to build, he knows how to render, he knows everything, how to behave. But it doesn't have to be instructed from the container. Therefore, there is no leak of domain outside the micro-frontend. And that is, for me, the key difference for understanding if you're dealing with components or micro-frontend.

Lucas Dohmen: But you already mentioned the problem with CSS, right? So if I take a solution with a client-side approach with an app shell, then I might have collisions in CSS class names, for example. So one approach would be to use Shadow DOM, as you already outlined. Are there any other approaches that we can use for, yeah, getting out of trouble there?

Luca Mezzalira: Yeah. Another idea that is quite simple to implement, whether you're using web components or not, is using prefix. So if you divide the work that you're doing in multiple teams, every team might have a name or a domain that they’re representing. So if they prepend the class name that they want to use with their own unique name, they can then have uniqueness on their class names, that decreases the risk that there would be a clash.

One thing, for instance, I was using material UI for a demo and I was using micro-frontends, and I have to make sure that they didn't clash together because it was material UI across the entire application but with different parameters and everything. So I don't want the same style or class name that was rendering one way in one part of the application to clash with another one. Therefore, in that case, using prefixes, I was able to decouple, basically, the two styles. And although I might have a style that by default would clash together in the application shell, they weren't clashing in reality because there was a prefix that was creating uniqueness to all those.

Pattern libraries

Lucas Dohmen: One thing I wondered when I read the book, and I wonder again now is how do you handle things like pattern library? So if I want to do something like a shared style between all the applications they have a certain amount of styling that is shared between everyone and a certain amount that is very specific to one part of the application. Right? So how do you handle that both in Shadow DOM and in something like BEM that you just mentioned? How do you do that?

Luca Mezzalira: Usually, in Shadow DOM, you have fewer problems because they are encapsulated. So in that case, you can make your own decisions and every component won't clash with each other because it's not even in the same if you want scope of the application. On the other side, BEM basically provides you with a more granular way to describe the element that you're styling and it goes up to a certain granularity that makes it more difficult to have, let's say, redundancy on names. But if that happens, the prefix strategy helps a lot. Because then at the end of the day, if you're using the same button but with different, I'm gonna say, styles or parameters that you want to use, you can do that without any clash. Because you are going to basically create your way to handle one thing. And then you can supplement, as you described before, with your own style for that specific portion of the application that requires different styles. So you can really do mix and match with just this simple implementation.

Lucas Dohmen: So maybe this is a bit too deep into the technical part. But if I imagine that you have a page that consists of the app shell, and then three sub-parts that are split, that are different parts of the application. And each of them has its own Shadow DOM. So do you include the basic styling as one CSS file in each of the Shadow DOMs and the app shell, and then add specific styling to the sub-parts? Or how do you do that in practice?

Luca Mezzalira: If you go in the part that you include all the base elements, obviously, it's going to have more kilobytes per download for the users, but you guarantee that they are completely independent. So potentially, if tomorrow one of the team is using an older version of the styles you will still provide an independent element that can be updated at some point. The moment that you start to share anything, style, code, whatever across those three independent parts, then you have a risk that there is a runtime error that you didn't pick up in your environment or in that environment because it potentially could be different. So ideally, you need to understand if that is the trade-off that you want to take, or if you spend more energy and more effort making sure that everything is still there on time, it's not going to be locked from the customer.

Lucas Dohmen: Okay, good.

Luca Mezzalira: It's up to you to decide where you want to spend your effort. It could be a simple, “Okay, I have a bit of duplication, is 10 kilobytes more. That's not the end of the world”. Or “No, that 10 kilobytes, I really need them. And therefore, I need to find a way to optimize my pipeline to make sure that I'm not going to create issues at runtime”.

Lucas Dohmen: Very good. So one thing that we did in one project was that the base style was available versioned at a URL, and you could use it in the different parts of the application. And then you can take advantage of caching. So at least if everyone is on the same version at that point in time, you only download it once. But yeah, that's also an approach where you can maybe find a middle ground between those two approaches.

Luca Mezzalira: Oh, yeah. Of course.

The difference between SSI and ESI

Lucas Dohmen: So talking about the server-side part, I think one of the things that a lot of people have never heard about is SSI and ESI. Maybe some people heard about SSI a lot of years ago. Can you first explain what ESI and SSI are?

Luca Mezzalira: Yeah. So SSI and the ESI are server-side includes and edge-side includes, those are markup languages that are basically using a mechanism called transclusion. Transclusion is an algorithm. What it does is very simple. You have a placeholder element that when it’s parsed by the engine is going to be replaced by more concrete elements. For instance, imagine that you have a cart element that you are describing with that placeholder. And then when the engine is going to parse that markup element it’s going to replace that with a div element, maybe, I don't know, an image that represents the icon and some things. That is basically what it means.

Those are running either edge-side include on the CDN level, or server-side include on the server level. I think server-side inclusion is quite an old technology. So it's not anything revolutionary but allows you to work at the server-side and compose multiple elements because there are servers like NGINX, for instance, that are allowing you to use this technique for composing your page and provide a final output for the user. The edge-side include instead is a markup language that was created back in the days by Akamai and Oracle and a few other companies, that is available in certain CDN providers, not all of them. And the other problem is the ones that are supporting ESI sometimes are not even supporting the full implementation. Therefore, if you think about migrating from one CDN that has the full fledge of specification implemented to another one, you might think twice because you don't know if everything is supported.

The other problem of ESI more than SSI is the developer experience. That I think is quite important. In ESI, there aren't many companies that are providing a smooth developer experience. So you need to test against your CDN. Akamai, for instance, is providing a Docker container that basically allows you to test on your laptop how the CDN node in Akamai would render your application, but it's the only company currently that I am aware that is doing something like that. Usually these techniques I have seen use where you have, let's say, more static content on the UI.

So, for instance, IKEA used edge-side include back in the days for their catalog. And everything was extremely static. But when they started to have dynamicity or they wanted dynamic content or interactivity, they supplement edge-side include with client-side include. There are some libraries available in JavaScript, but basically, they do exactly the same thing using transclusion but on the client-side. So you can mix and match those and provide interactivity as well, high shareability, and also span the load across multiple edge nodes instead of going every time to the origin, usually the original weighs less than all the CDN nodes that are available in any cloud provider.

Self-contained Systems

Lucas Dohmen: Very cool. One thing I noticed when I read the book is that one approach that we at INNOQ use a lot that is very focused on vertical splits is not really described in the book. I wanted to get your opinion on where you fit it into your frame of mind. I added it to this as well.

A few years ago, we wrote this guideline called "Self-contained Systems," where we describe how you can integrate multiple vertical splits into one system. And there we focus a lot on server-side routing, or edge-side routing, I don't make a big distinction between those two.

Each of those systems delivers its own frontend, its entire stack from the database to frontend, and we integrate those systems with things that we already mentioned, like transclusions or also components in the other system, both transclusions with ESI or also client-side transclusions that you just mentioned. And also simple things like links between those two systems, right? So they are really independently deployable. They are manageable by one team. Why would you put that into your frame of reference?

Luca Mezzalira: In that case, I would say are we in front of micro-frontends? Because the reality is, yes, you divide by system, but if we say that everything that is divided by multiple systems renders on the client-side might represent micro-frontend, then where is the distinction between a single-page application or micro-frontends, whatever it is? So I think the distinction on micro-frontends is the fact that you have a map with a business domain, and then you can go more granular. The fact that you have an application shell that I think is one key distinction for micro-frontends and other implementations. In your case, you are dividing more in a modular monolith fashion, where you divide some verticals that might or not represent a domain, and then you are basically linking and using web standards for moving through between the shoes. I think I'm not even sure that I would categorize it as micro-frontends.

Lucas Dohmen: That's good to know because I just wanted to understand where you see the distinction. Because in the past, I saw descriptions of micro-frontends where this would be part of it and other parts where this would not be part of it. So it's always good to get a good frame of reference. Because it's a different approach to it that solves some of the same problems because you still can deploy independently and so on. That helps me a lot. Okay, so one thing that you also described in your book is the whole topic of the back-end structure.How is the back end structured to allow access from the frontend? You outline three ways, API gateways, BFFs, and service dictionaries. Can you just give us a brief overview of those three approaches and where you see them fit?

Luca Mezzalira: Sure, I will start with the service dictionary that probably is nothing new, but I think is very helpful. So a way that I found helpful to decouple micro-frontends from the endpoints that allow basically to run independently, the back-end parts with the front-end part, is injecting basically to the micro-frontends the list of endpoints to use with a dictionary, basically, of services that are available. That allows a micro-frontend to pick the service that they need. And also, potentially, you can do something even smarter. And every time the micro-frontend is questing a specific service dictionary, you serve a list of endpoints that are useful for that version of the micro-frontends.

That allows, for instance, if you work with API control first, where you maintain the same code and at the same contract between the API, then you can evolve the application independently. You don't even have to care about injecting them in CI/CD, as I've seen before, or even create a library that is loading inside of micro-frontends for handling this part. Because when you have these kinds of information that are loaded, instead of having all the micro-frontends using a specific version of the APIs, you might wish that you have some of the micro-frontends using a version, some others another version, and then you need to coordinate the redeployment of these shared libraries that is a pain, especially in large organizations. So this one is just a runtime way for handling something as simple as a JSON Schema or a JSON that is providing a list of endpoints that are specific for a micro-frontend.

API gateway is a well-known way of exposing APIs, especially in the microservices world. And in that case, you have your endpoints behind that API gateway wherever you want, a portion of a monolith, and the rest of microservices or microservices completely, up to you. But for the client, it's not a big deal. And that basically also might help where people are saying, "Oh, if we work with microservices and micro-frontend, we need to have cross-functional teams." It's not true. At the end of the day, you can use APIs as a nice way for communicating between teams, front and back end, and maintain the independence of these two layers. Then there are people that argue, but I want to have some work that is handled by one team only, can deliver a feature independently, and create value for the customers.

And I agree, that is the favorite way also for me. But there are certain situations where you cannot do that. For instance, in my previous company, we had roughly 40 different targets on the frontend we needed to manage and with different technologies. So if I have to create a team that can deliver a future end-to-end without creating first-class citizenship inside my teams, I need to have a team of 15, 20 people. That is not manageable at all, it doesn't perform. So instead, if we divide, in that case, front and back end, you can have, let's say, more interaction between front-end and back-end teams. But in the end, if you work well with the API contract first, you reduce basically this type of interaction that can work independently against the same API contract that is not changing every day.

And the last one is backend for frontend, where backend for frontend is a pattern that usually allows a front-end team to create a layer that is in between the APIs and the frontend. And they are able to use the APIs that they want, put them together and serve to their client. Usually they are divided by device. So you maybe have a mobile backend for frontend, one web, etc. But I have seen also people that are dividing per domain. So instead of having one per device, you have one for, I don't know, the catalog, and one for authentication. And you handle it in that way.

It really depends on how complex the application is, how large is the application of your structure. But backend for frontend provides a benefit where also if someone is updating, let's say, an API and you're not aware of that, for any given reason, is not going to cause a cascade effect on the client-side. So you can slowly but steadily migrate your API on the backend for the frontend layer and also aggregate them in a way that the client can do less round trips and provide a better experience for the users. And sometimes I have seen use REST, sometimes I've seen use GraphQL for this implementation. It's completely up to the team. And often, I have also seen one backend for frontend written with GraphQL and using Schema Federation for stitching together the different APIs in a unique layer that the clients can consume either mobile, web, micro-frontend or monolithic application.

Who is responsible for BFF?

Lucas Dohmen: Where do you see the responsibility for the BFF? Is it the responsibility of the front-end team that uses it? Or is it the responsibility of a different team?

Luca Mezzalira: For me, it would be the responsibility of the front-end team. Because they are the ones that are using that layer and consuming that layer. So they need to know how to handle that. But I would recommend having some help from the back-end teams because there are certain, let's say, topics like scalability, observability, stuff like that, that they have definitely more experience with, especially when we are talking about building microservices.

Lucas Dohmen: Very cool. So one thing you already brought up, but I wanted to have as our last topic for the day is the question of feature teams with component teams. So how do you see team structure and where do you see it in regards to all the things that we talked about until this point?

Luca Mezzalira: I don't have a strong preference. I mean, I work in both and I've seen both systems working. I think that the context should drive that decision. As I said before, I had the possibility to work in companies where it was impossible to work with features teams, and others that I worked in features teams and it was working extremely well. The problem there is when you have a limited amount of resources, but the number of features is growing. And then you have a feature team that suddenly became a features team. And therefore, you have to handle multiple events that are touching multiple points and the complexity starts to rise.

For me, it's very important as a tech leadership, in general, architects, principal engineers, whatever, that we take regular steps back to understand if our model is still valid. Because often, companies start in one way and they stick with that despite it not working. And then the problem is, “Oh, but we don't have enough developers”, or “We don't hire the right developers”. And in reality, maybe the problem is only how the communication structure works, how the company is organized.

So either that should be a component of the feature team, my suggestion is to check regularly if that is the model that is still working for your company or if the way how you sliced your domains is correct. Because as the company's evolving, your architecture has to evolve with the company. It's not something static. It's completely fluid and organic. We need to bear in mind that the moment that we are designing an application today, in one year's time, the things could be completely different with the same people or not. Because the business drifts towards a different direction from the beginning.

So the assumptions and the characteristics of our architecture and organizational structure that we made a year ago, are now obsolete and we need to replace them with another way. So don't be afraid to change and switch models if you see that there are some challenges. And question yourself, it might be that you create a genuine discussion, but questioning the decision that we were making, like, 6 months ago, or 4 months usually is a good time, but 6 to 12 months ago is definitely one thing that I encourage any company to do.

Lucas Dohmen: I think those are very good closing words because I think it's very true for a lot of parts of our system. We should question our decisions and also think about which architecture really fits our needs. And maybe at the beginning where we are more flexible of changing things, especially changing things up when we notice that it doesn't fit us even though we thought that it would.

Outro

Lucas Dohmen: So thank you so much, Luca, for your time and for your insights. And I wish you a nice day and all our readers the same.

Luca Mezzalira: Thank you very much for having me. It was a pleasure sharing my perception of micro-frontends with you. I hope that you will enjoy the book.

Lucas Dohmen: Bye.

Luca Mezzalira: And thank you again for having me.

Intro
What problems do micro-frontends solve?
Independent deployability
Where do distributed systems fit in?
Splitting your application: Horizontal vs vertical split
Single-page app vs rich page app
What is an appshell?
What framework to use for micro-frontends?
Use iFrames for a horizontal split?
Pattern libraries
The difference between SSI and ESI
Self-contained Systems
Who is responsible for BFF?
Outro