BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Presentations Lessons from DAZN: Scaling Your Project with Micro-Frontends

Lessons from DAZN: Scaling Your Project with Micro-Frontends

Bookmarks
51:36

Summary

Luca Mezzalira explains how to implement micro-frontends, enabling to scale up a project with tens of developers without reducing the throughput. Micro-frontends are a new architectural trend in the development of front-end applications. This style can provide several benefits to our projects, offering a level of decoupling never seen before in single-page applications or universal architectures.

Bio

Luca Mezzalira is the vice president of architecture at DAZN. In his 16-year career, he has worked on cutting-edge projects for mobile, desktop, web, TVs, set-top boxes, and embedded devices. He is a Google Developer Expert on web technologies, the author of Front-End Reactive Architectures (Apress) and manager of the London JavaScript community.

About the conference

Software is changing the world. QCon empowers software development by facilitating the spread of knowledge and innovation in the developer community. A practitioner-driven conference, QCon is designed for technical team leads, architects, engineering directors, and project managers who influence innovation in their teams.

Transcript

Mezzalira: My name is Luca. I'm VP of Architecture at DAZN. I'm a Google Developer Expert on web technologies. Finally, I'm the manager of the London JavaScript Community. If you are looking for a community on some talks on JavaScript, check it out on meetup.com.

Architecture Evolution

I think everyone, at least once, has approached architecture in this way. Imagine that you have a new Greenfield project or you're working for a startup, the first thing that you want to check is validating your business idea. You really want to make sure that your ideas start to fly. That means that sometimes you need to make some trade-offs on the technology stack, because that's the reality. Architecture is not creating the best architecture ever, but it's creating the one that could make your company successful. That includes trade-offs. Usually, what you have is trying to work with monoliths, because when you are trying to get something out to your audience, you try to figure out the best way to do that. A monolith is absolutely something that you need to take into consideration. A single-page application is not deprecated. A monolith layer with an application server is not deprecated at all. It's a quick way that you can gather information for the final user. Each line of code that you're writing nowadays, is not for you. Maybe it's also for some people in your team, and your organization. The reality is you are generating code for your users. You need to create value for them. That is the key thing. In this architecture, there is nothing wrong. It perfectly fits in a specific lifecycle inside your company. Here, you have an application server and maybe a database. It could be SQL, NoSQL.

Let's assume that suddenly, your business idea starts to fly and starts to have traction. You have people that are subscribing to your services or they are starting to use your application more. Then, the company usually starts to say, "It's successful. We have a bit of money that we can use. Let's start to reinvest on the idea." They start to augment the development team. They start to increase the company. It's becoming more complicated having everything just with this stack. Usually, it's where microservices come in, in this case. You start to say, "On the backend, there is the critical part of my architecture that maybe requires to scale differently than another service that can be highly cached behind the CDN." The developers on the backend start to pick the right programming language and the right database for the job. There is no more one-size-fits-all model, where you had in the monolith. You start to say, I want to use the right approach for a specific thing. That makes a lot of sense.

What about frontend? In my experience in frontend, you start with a single-page application. You continue with a single-page application until it's so complex that you start to rewrite. It's a monumental chunk of work that you need to rewrite, because maybe it's been running for several years. What about the developer experience? Developer experience, usually, there are a bunch of guys that start this application. Maybe they are still in the company, maybe they are not anymore. The reality is they are taking certain decisions that you have to live with forever. Otherwise, it will require a tremendous amount of work in order to change certain decisions. Maybe you can do that, but sometimes you cannot.

I was in this situation when my company started to scale. I have to figure out, from the backend, if I had a way to manage the complexity and the fact that we were blitz-scaling. On the frontend, I didn't have an answer to how I could make these single-page applications scalable to multiple teams in distributed offices, maybe having all sorts of people that are working remotely. After a while, what I came up with was the possibility to take this single-page application and divide that in micro-frontends. That's basically why I'm talking about them.

Definition of Micro-Frontends

Let's try to figure out how we would define micro-frontends. Currently, on the web, there aren't too many definitions. This is the definition that I came up with. I think it provides a decent understanding of what they are. For me, micro-frontends are a representation of a business sub-domain. This is coming from domain-driven design. They allow independent implementation. They can share the technology or can have different technologies. They should avoid sharing logic. They are owned by a single team. The ownership part is very important. Because if you don't have a strong governance, you risk, honestly, having a project that doesn't work properly. Not because of an architectural issue, but due to a governance issue. That is very important to think about.

What Is a Micro-Frontend?

Technically speaking, what is it? For me, currently, imagine that you have this single-page application that contains different business logic and different domains. It could be either an authentication domain that is composed by the sign in or sign up. It could be the core domain that is the catalog here, and so on. Then we take those domains and we start to split them. How do we split them? It's very important that we look at the value that we can provide to the user and not the value that we provide to the developers. What it means is, in domain-driven design, one of the things that you usually do apart from defining the ubiquitous language is also identifying some sub-domains. Working with sub-domains could also work on the frontend. I don't understand why for so long, we always kept domain-driven design away from the frontend where it's part of the domain as well. Why not? That is the key thing for understanding micro-frontends.

Principles of Micro-Frontends

When I started this journey, the problem I had is, how can I scale a single-page application with hundreds of developers distributed in Europe? Unfortunately, I didn't have an answer. What I thought is, let's start to look at a successful architecture pattern that allows me to do that. Obviously, microservices was the first one that came to mind. At the same time, I cannot apply the same technicalities that you find in microservices. I took a step back and looked at the principles.

Model around Business Domain

The first principle is model around business domain. I can do that on the frontend as well, because, in particular, if I have an existing application, I can see how my users are interacting with my application. I can understand how they are behaving inside the application. I can understand, what are the flows that they're following? What are the funnels that they're following? What is the most used payment method? How are they interacting with a specific carousel? That is something that I can do. Also, business domain, it means that I can speak internally with my product team, and try to figure out how I can create something that is frictionless. Not only for the users but also for the internal communication that you have inside the company.

Decentralization

Then the other thing is the concept of decentralization. Up to now we always work with architects and tech leads that were basically defining the best practices. Now I think we are heading towards a new way of working with the teams where we create guardrails. Inside these guardrails, they can take their own decisions. It is very important that whoever is close to the business domains can take the best decision for the business domain. Honestly, in particular in large organizations, it is impossible to know everything about any domain. It is really impossible. You need to trust your developers that are closer to the action. They live and breathe on the specific domain.

Culture of Automation

The other thing is the culture of automation. In microservices, to a certain extent, writing the business logic on microservices is pretty easy. There is no rocket science there. On micro-frontends, it's potentially the same. The thing is, if you don't have a strong automation pipeline that allows you to generate a feedback loop for your developers that is quick and can deploy quickly in production, and learn from your users. Then you could blame the architecture, but in reality, it is the automation that you need to look at.

Deploy Independently

One thing that is important for me to stress is the fact that when we work with microservices, ideally, we want to deploy independent microservices. At the same time, on micro-frontends, I want to do the same. I don't want to, every time that I make a change, deploy 2, 3, 4, 45 micro-frontends at the same time, because that will be a fail. That is basically distributing the complexity across multiple teams. If you have teams that are not even co-located, good luck.

Hide Implementation Details

Then we have, hide implementation details. You need to work with API contract. It is very important when you manage that. Because if you start to have some APIs that are tightly coupled between different bounded contexts, you risk having a lot of complexity that you need to manage. You really need to do a lot of work on defining APIs that allow you to communicate between micro-frontends, if needed, as well as with components.

Isolate Failure

Then, we need to isolate a failure. We have a new dimension on the frontend. Before, we had a single-page application or a server-side rendering one, where we downloaded the entire application. We have everything there. All the code is there. You just jump from one view to another one, no problem at all. Here, we start to have a different dimension where part of the application could fail due to a network condition, or someone is looking at your application with a mobile device and lost the connection, and so on. There are new things that we need to think, what could go wrong and how do I mitigate that? The important thing is not thinking, I need to prevent something going wrong. The important thing is understanding how you can mitigate that failure because that is the important thing, without affecting the entire application.

Decisions Framework of Micro-Frontends

When I was in this journey then, I was doing a workshop in Norway and I was talking about this thing. Suddenly, I have a spark in my brain. I was talking, literally. I couldn't stop. I had this idea of four key things that, in my opinion, when you nail those four key decisions, will cascade all the other decisions inside your micro-frontend architecture without any problem.

Key Micro-Frontend Decisions

I think there are those four decisions. Define what a micro-frontend is. Compose a micro-frontend. How do you compose them? How you route micro-frontends, and how you communicate micro-frontends. Those four decisions, will allow you then to take all the other decisions, like how I manage my design system. How do I create my design system? How I'm going to share the code between micro-frontends. Those four decisions are the key ones that define your architecture. I define them as decisions framework, because I think this is very powerful. These four will allow you to define your architecture, and then you can move along without any problem.

Define Micro-Frontends

The first one is how you want to define a micro-frontend. There are people that have multiple micro-frontends inside the same view. They represent, basically, a micro-frontend as a part of the view. That is not wrong. It is one of the options. There are strong use cases for this approach. For instance, SAP released a framework called Luigi framework. What they do is having IFrames that are coordinating that. There are other options, you can do a composition on the server side. The reality is, you will have multiple micro-frontends inside the same view. It creates some complexities. How do they communicate together? How do I manage the dependency and the dependency clash between those? There are some challenges there, but you need to ask a few questions around this.

Slicing Micro-Frontends Vertically

The other thing is thinking about slicing them vertically. I represent a micro-frontend as a single HTML page or a single-page application. Those are the two options that are currently available. Those are going to drive all the other decisions.

Horizontal Split

On the horizontal split, there are a few things that you need to take into consideration. You have an upfront investment on creating a smooth experience for testing everything. Because if you test in isolation, one part of the view, how do I make sure that this part is not clashing with the rest of the view? Therefore, you need to figure out what is the best way to do that. New Relic, for instance, invested heavily on the developer experience. They started to do talks on explaining how they work. Basically, they have an app shell that contains some templates. They load micro-frontends inside these templates. They didn't go very granular. They have a fairly simple template system. You have two or three micro-frontends at the same time. That is an investment that you need to think about. In the case of New Relic, it makes sense because they have dashboards that are self-contained. If you have elements that have to jump from one side of the screen to another one, and you have strong animation and a lot of interaction, then it could be complicated to manage that. Then you have to think about the team structure. How your teams are structuring in the horizontal thing. If there is a team that is owning a carousel inside your application, then that team is owning that. Develop those. Then, what other elements is it going to use, because you need to figure out how you assign tasks and micro-frontends to each single team. It is a great thing for SEO. If SEO is one thing, you can do server-side rendering and compose everything on the server side. Then you serve that to the crawlers. There are other options you can also do for vertical split. That is a good thing that you can do.

There are testing challenges. Because if I test my micro-frontend in isolation, in this case, then how can I test everything, end-to-end? Who is owning the end-to-end test? Is there a team that is owning the final look and feel of the page? How can I create a smooth experience for our users? There are scalability challenges. If I'm working on assembling those micro-frontends at runtime, and I have a lot of personalization in my page, I need to think twice when I start to assemble that on the server. Because if I have a spike in traffic, at some point, I need to scale that layer, horizontally. Is it something that is doable? What complexity is it bringing inside my organization? You need to think about those questions.

Finally, the dependency management. Because if you have an IFrame that is behind the sandbox where you can throw whatever dependency you want inside there, the moment that you don't have that and you have to assemble, you also have to think about the final bundle size. You have to think about whether you can manage having different versions of the same framework, or you need to coordinate certain things. Those are a few questions that you need to answer before you decide to go with the horizontal one.

Vertical Split

For the vertical split, though, it is easier. We have a more traditional development. Because if you think about that, it's just having an HTML page or a single-page application. That is basically what we are doing in frontend, the last 5, 10 years. You don't create custom solutions for your business, you use the solutions that are out there. You can use Webpack the way it is. You can use Rollup. You can use whatever you want. Whatever tool is out there, you can still embrace. Despite it being currently available, or it would be in the future, you can just embrace because it's normal development for your frontend developers. Finally, for mitigating the problem with SEO, you can use dynamic rendering. Google released last May, the possibility to instruct and provide and serve to the crawler, a specific version of your page that is basically optimized heavily for indexing the content. It's something that I encourage you to check on the Google website, if you search for dynamic rendering. It's pretty powerful. For instance, we are using it heavily, on our application.

Compose Micro-Frontends

The second step is, how do I compose the micro-frontend? I basically have three options here. One is composition on the client side. In the case of this approach, you have an app shell that is always available inside your application. You start to load different micro-frontends in the same view, or you load just one single-page application, for instance. In this case, usually, you have all your micro-frontends on origin that could either be an application server, or it could be simple like an S3 bucket. You have a CDN that may or may not be there. I would recommend having that. The CDN, basically, is serving the content instead of going to origin every time. That is definitely a good option.

The second option to compose micro-frontends is using an Edge Side Include. That is a markup language that is proposed by Akamai and is implemented in several CDNs. Basically, at the CDN layer, they fetch all the different pieces of micro-frontends and they put everything together in a unique page. For the user, it is like downloading a single application. It fades out completely out of your hands. The possibility to scale that part is taken care by the CDN. If you have static pages, great for that. It's becoming more complicated as a developer experience, though, because testing on the CDN means that you always have to try over there. The other thing is you need to pay attention because if you decide to go with Akamai, the entire exciting, good spec is there. When you move to another CDN, you don't have all the APIs available. Therefore, certain things that can be done on Akamai, cannot be done in another place, or it cannot be done at all because there are some CDNs that are not allowing you to do that. Finally, you can compose at origin. For instance, a famous one is Zalando. Zalando is a fashion e-commerce. It allows you to compose everything. Before, they had Mosaic 9 that is an open source project, with TailorJS that was basically taking different HTML fragments and composed everything together. Now they moved away from that, and they decided to go with React and GraphQL, always with the same approach. They compose at the server-side level. Then they serve through a CDN. Other options like OpenTable and Skyscanner are using OpenComponents. It is another framework that allows you to compose everything at the origin and then serve at the CDN because they have less data that are personalized to a certain extent, compared to other approaches. Those are the three ways that you can compose. After that, you decide if you want to go horizontal or vertical.

Route Micro-Frontends

The first step is, I decide to go with the first implementation. I compose at the client side. How can I move from one micro-frontend to another one, one view to another one? In that case, you have two options. One is doing at the CDN level, using Lambda@Edge. Or if you use CloudFront, you can also instruct CloudFront to load different parts of the application from different places. The other one is routing on the client side. In the app shell, basically you have the macro-routing. In the macro-routing, you say, I have those views. Then it could be either that you load an HTML page on a JavaScript file. It depends on how you structure your application. That is another option. Then you have Edge Side Include with CDN. What you can do instead is routing on the CDN level. It's the only way. Because, obviously, all the logic is happening there. You basically hit a URL. The CDN is just saying, now I assemble these components together, and I serve the page. That is the way you route. Then at the server side, in that case, you can also do on the client. My suggestion would be, do on the origin, because you have all the application logic that is running there. You are assembling your pages there. You can do quite a lot of logic also for the routing bit of micro-frontends.

Micro-frontends Communication

The last bit is, how do they communicate together? If I have a horizontal split, I can have something like that where I need to decide how one component is communicating with another one. The challenge here is that you need to find a way to not couple them. There are two options here. You have an event emitter that is owned by the parent that contains everything and injecting each single micro-frontend, or you use custom events. Custom events are a standard in JavaScript. You can use that. You're just bubbling up events and everyone is listening to them. Those are the two options that you have.

Then the other thing is, how do I communicate from one view to another one in order to pass data across that? We have two options there. We can use web storage. For instance, imagine that I have the authentication part and the user is signing in. Then you use a micro-frontend to receive the JWT token. It can store inside the web storage, locker storage, for instance. Then when the authenticated part is loaded, it can fetch the JWT token from the web storage and double check if the user is entitled to watch that part, or whatever. The communication here could be either from web storage, or through normal query strings. I can parse some data that maybe are not sensitive, possibly, on the query string and picked up on another micro-frontend. Then consuming an API with that specific ID or that specific information that is in query string, towards my APIs. Those are the four things.

There Is No Right or Wrong, Only the Right Approach Based On the Context

In all of that, there is no right or wrong. The context here is driving those decisions. One thing that is very important to highlight is not the technical context only. It's also how you structure your company. How you're going to grow inside your company. How you want to manage certain communication, because the complexity of scaling a frontend application is not on the technical side. Here, we are not talking about vertical versus horizontal scaling, stuff like that. It is the communication overhead. How you manage different teams. How you don't create external dependencies. Those are the killer for scaling frontend teams. Bear this in mind when you pick one of those decisions.

Micro-Frontends at DAZN

Let's try to figure out some experience, directly from the trenches. I work for DAZN. We are an OTT platform similar to Netflix but for sports. We mainly do live sports. We also have video on demand. We are currently available in nine countries. We announced that we are going to be available in 200 by this spring. It will be quite an exciting time right now, being in DAZN. The complexity we have on the frontend is quite unusual. We don't have only the web and mobile. We also have quite a few TV targets. We have FAR TV, Android TV, Tyson, Sony, CB, and many others. We have a lot of clients that we have to serve. Micro-frontends is helping to improve the experience for our users.

What is a micro-frontend for DAZN? Technically speaking, a micro-frontend is this, the holy trinity of JavaScript: HTML, JavaScript, CSS. It's pretty simple. It's how a single-page application is composed. It is nothing different from what you do nowadays. That, for us, was very important because we didn't want to create something that was dedicated only to DAZN. Every time that we have a new joiner, they have to learn how to do their job. They know perfectly how to do their job. The thing is, we need to find a way that we can use the holy trinity of frontend for doing micro-frontends. The artifacts that we have, that we serve are exactly these.

DAZN Implementation

Then we need something that orchestrates everything. For us, the decision was we go client side. Also, because TV is quite complicated sometimes. In this case, what we have done is creating the bootstrap, nothing has to do with the CSS framework. It's just the first thing that we load. When you type dazn.com, we load the bootstrap. It's basically a simple HTML page that contains some logic for loading micro-frontends. What it does is this. The first thing that it does, is you type dazn.com, and you have the bootstrap there. It's loading the application startup that is retrieving the configuration, so it knows which device you're using. It knows which country you are, and feature flags, and other information. It abstracts the input/output operation mainly for TV. We cannot rely on web storage for TVs, because not all of them allows us to work with that. We decided to abstract that part. If you want to store a JWT token, you always pass through the bootstrap APIs. It triggers some callbacks on some lifecycles, so the micro-frontend knows when it's finished, to be mounted, and when it is going to be unmount. Those are the two things that we have to notify the micro-frontend. In particular, for the unmount part, it is important because they have to remove all the listing and anything else that is in memory, because we are going to wipe out all the dependency the micro-frontend has. Finally, is taking care of the communication between micro-frontends. For us, the decision was going to the local storage. Every time that someone wants to store something, it is communicating through the bootstrap APIs, and stores everything to the local storage. That is the decision that we made.

After that, we define a few other things on our micro-frontends. Those are some principle that we have internally. Each micro-frontend represents a business domain. For instance, one business domain is the discovery part, for us. Discovery means the catalog with the video player and so on. That part is a business domain. We have our product team. We have multiple teams. Some of them are working on TV. Some of them are working on web. Some of them are working on mobile. The logic is, we have that domain that is mapped with teams. A micro-frontend is autonomous. If I want to release discovery, I don't have to ask permission to anyone. I just deliver that. Each team is responsible for that. They build it, and they own it, and they deploy it. We always load inside the bootstrap, one micro-frontend per time. For us, it was very important to create something that was easy to understand and debug. When we started to look at the complexity that we are dealing with, having multiple micro-frontends in the same view, led us to build a lot. In particular, because we are a large organization working on something very complex. Instead, if we're working in a vertical way, we create some single-page applications. It became very natural to manage everything because all the JavaScript developers will always work in that way.

There is no sharing between micro-frontends, with some caveats. Recently, we introduced the first shared library that is the payments shared library. There is a good reason why we did that. Because if we have a new SDK for the credit card, we cannot afford to have one micro-frontend that has the latest SDK of the credit card and the other one it doesn't. Because otherwise, it could blow up the payment. We forced the coupling between two micro-frontends that is having this shared library, where we have all the payment methods, and are used and consumed by two micro-frontends. Every time that we change the shared library, those two micro-frontends have to update and deploy simultaneously. That's the only dependency we have. We duplicate certain parts. For instance, the header and the footer are duplicated in each single micro-frontend. We are not abstracting that because the abstraction could lead to coupling. We don't want coupling. The other thing that is most important is being technology agnostic. Each micro-frontend can take their own path. When we proposed that, we found that the teams wanted to follow what the first team was doing. In the end, we ended up with React and MobX.

How Bootstrap Works

When we load the web application, first, it calls startup service. A consume API retrieves a JSON with a lot of information. Then the bootstrap understands the user status. It understands if you have a JWT token stored in the local storage or not. If there is, it is validated that you are entitled, your JWT token is still valid. When it does that, and let's assume that you are entitled, we load the HTML. How do we load that? The logic was, we can start having as an entry point, a JavaScript file. There is a famous framework, single-spa, that starts from that assumption. For us, it wasn't enough because we wanted to do server-side rendering at compile time. We want to provide the user the best experience possible. We don't want to wait until all the JavaScript is loaded, parsed, and available to the engine. What we have done is create an HTML page that is server-side render, if the team feels that is needed, and provide the skeleton of the page. Then loading everything slowly but steadily. HTML at the end, is an XML file with a specific schema. We basically have this entry point. It is an HTML. The bootstrap is loading this HTML, and appending inside itself, the DOM elements that are needed in order to compose the page. When it arrives to the script, and CSS, the browser will automatically load vendor, application, and CSS. We didn't invent something that is not available. It's already there. We just used what is a standard.

Example

I did a screen recording of this. Please keep an eye on the flashing outside the DevTools. There's quite a few flashing. The loading was in the bootstrap. I'm obviously throttling the speed of my connection, slowly but steady, we arrive at our application. My main aim here was showing that basically we're not doing anything custom that we created ourselves. It is, literally, I parse an HTML. I append the DOM nodes inside the bootstrap. There isn't a specific framework that we're using. There isn't anything. It is vanilla JavaScript. That's it.

DAZN Routing

How do we route our application? There are a few things that we were thinking. First of all, our infrastructure is really simple. We didn't want to do anything smart. One situation that we have to deal with often on the backend, is imagine that you have a Champions League Final. You have all the people, 5 minutes before the kickoff, that are joining the platform. You have millions of users, literally, that are hitting your platform heavily. If we have smartness on composing everything on the server side, it could be complicated, because you have to take care of a new layer to compose everything. In this case, we went with a very basic thing: CloudFront and S3. We basically generated and do everything at compile time. Then we store our artifacts on S3. CloudFront is our CDN. CloudFront has another thing that is pretty cool that is Lambda@Edge. Basically, it's running some Lambdas in the closest Edge node that CloudFront has. The beauty of this is it can add some logic. For instance, for us, this allowed us to do canary releases. We can say, if you are in Canada and you're using Internet Explorer, we serve the old version of the application. If you are using Chrome, 20% of the traffic goes to the new version. That is very simple because you can also do in other ways. We decided to go with Lambda@Edge because it's also providing quite a lot of flexibility for us. Overall, it is a fairly cheap strategy for doing that. Because you are basically abstracting all the problem of canary, so the client is the same way, 100% or 20%, whatever it is. The client is completely unaware of what's happening. It's just being served or not to a specific client. Then the Lambda@Edge is loading the bootstrap. The bootstrap is loading the micro-frontend. It could be the authenticated one, or not authenticated. It could be deep link, whatever it is. Those are the layers that we have. Again, because cache is very quick also, as a response.

Components

Obviously, we don't have only micro-frontends that are vertical, we also have components. One classic example for us is the video player. Having a video player optimized for live, I can guarantee, is very complex, because it's not the same. I take Shaka Player and I squeeze my page down. Unfortunately, all those video players are obviously optimized for VOD. We had to change the heuristics. We have a team that for each single video player for each single device, if possible, is creating new heuristics that allows us to have the best performances for live, and provide the best experience for our users. Obviously, it's a very complex thing, because not many have done that before. We have a pretty big team at the moment that are working just on the video player. The video player for us is a component. This is not loaded at runtime or anything. It's treated like you load any other components. You just import your components. The team that's working on the video player is basically creating a package on the private npm. It's picked by any micro-frontend that is needed. It is just importing this video player. This is another way that you need to identify inside your micro-frontend, some areas that could be complex video player, or the payment API, and understand when you want to abstract them. A vast majority of the time is just a simple team that is working, or five people that is working on the micro-frontend only on the frontend side, not backend. They're working on the entire user experience for that micro-frontend.

What We Have Learnt

What have we learned so far? What are the takeaways for this journey? We understand that, first, we need to identify our micro-frontends. How we want to build them. We want to compose them. We want to orchestrate them. We want to understand how they communicate together. Those four actions are critical, and are your four pillars for creating a successful micro-frontend architecture. Then there are other things that we didn't discuss but are there. You need to invest time in automation. That is a nice investment. I saw, too often, projects failing just because the automation wasn't there. "It works on my laptop, but not in production." That is very important. Do that investment upfront. Observability, obviously more micro-frontends and more distributed parts that could fail. You need to understand why they're failing. When they're failing. Potentially, be proactive more than reactive. Tools like LogRocket, Sentry are definitely useful for you. How do I create a high consistency of user experiences in my micro-frontends? That's another thing. For us, for instance, we started with providing JSON to different teams. Now we are thinking about having a proper design system with some components that are not too complicated. We are not going to have a header or footer, or stuff like that, we are going to have a button, a link, or something that is very dumb, because then each single team can implement and compose their own stuff.

Team's impact is another important thing. When you design anything, when you take any decision, please don't take the technical part only. Think about the impact of your decision inside your organization. It's very important that you think about that, in particular, when you work with large teams, because your decisions will affect the communication between teams and how they're going to succeed or not. That part is very important. How do you manage the dependencies? If I have a horizontal split, how am I going to put everything together? How do I avoid having clashes around that?

Duplication over abstraction. The wrong abstraction is way more expensive than good duplication. They say in several books, "Don't repeat yourself." I totally agree with that. I'm not saying, "No, please duplicate." Try to ask a couple of questions before you immediately decide, let's abstract that. Let's have an external library. Because that will lead to some complexity. If you have distributed teams, with the core team in a completely different country, then you have external dependencies to manage that are way more horrifying for developers than having some duplication. Finally, performances. I heard on several socials that with micro-frontends, you cannot have performances. It's not true. Everything that you know already on how to optimize your application, is totally applicable on micro-frontends. You can have performances. You can have the less than 4 seconds load like Google is suggesting, or mobile and web, without any problem. The only thing is, you need to be smart in how you are taking a certain decision.

Conclusion

Micro-frontends are not a silver bullet. I'm not saying from now on, you all work with micro-frontends. Absolutely, single-page applications, server-side rendering are still and will remain a valid option. Finally, we have a third option on top of that, that will allow us to deal with certain problems when we operate at scale. Before, it was, we need to figure out a way to manage that. We saw several options for doing that. Obviously, micro-frontends are not going to solve all the problems of the frontend world. It is just an approach that in certain contexts, and applied in a certain way, could make a lot of sense.

Resources

Obviously, this journey is still under discovery. Micro-frontends is not a super consolidated architecture. What I'm trying to do is create interest. There are tons of other resources that you can find. Here, you can find on the left, a link to a page where I collected all the articles, all the things they have done so far on micro-frontends. Next week, I have a webinar that is 3-and-a-half hours long, on O'Reilly. If you are interested, there is still space. Basically, I'm going to deep dive in each single part that we have seen, including the impact on the teams. I'm writing this book for O'Reilly. There's an early release in the UK. If you have access to Safari Books Online, you can read the first three chapters. Feel free to provide feedback.

Questions and Answers

Participant 1: When you started, you said that you try to split your frontend following domain areas. You said that because the backend was already doing that, and they already had microservices, and maybe organized their microservices around domain areas. You are trying to do the same in the frontend. In your experience, do you find that there is any alignment between the domain areas in the frontend and in the backend? If you do, how do you align the teams? Do you have one team with shared resources, frontend and backend, or you have two parallel teams?

Mezzalira: What we have done is first identify the three types of domains that we have: generic, supporting, and core. Those are the three DDD domains that you can find. Then we mapped inside the application. We mapped, in a context, what the different areas are. We did a step further. We reorganized the company. We currently have four dev center specific domains. For instance, if you go to Amsterdam, you have the entire acquisition, retention that is a generic domain for us. We have the frontend for web and for TV. We have all the backend for all the payments, subscription, management, and everything that is inside that building. If you go to London, we have the core domains. We have the playback, the discovery part, and the catalog, and metadata, and so on. We identify the technical part, but we try to understand how the communication between teams could work. Because at the beginning when we started this journey, we said, we can have a bit of sign in, sign up in London, a bit of discovery in Amsterdam. We saw, even before starting this discussion that something was clashing. This approach provides a lot of flexibility, but you need to, to a certain extent, understand what is the social impact inside your organization with this?

Participant 2: You mentioned when you were talking about duplication over abstraction that things like the footer are duplicated. How do you handle when you need to make a change to that across your whole application?

Mezzalira: In this case, we have these two things, footer and header that are duplicated in five different micro-frontends. When I decided to do that, I was thinking, let's see indeed, in the last three years, how often we change. We change two times. We had to change again the third time when we implemented this micro-frontend thing. It was five tickets created in Jira, and roughly, a turnaround of 5 minutes per team. Obviously, I'm not saying duplicate everything because that is not my aim. Be wise. That's why I'm saying, ask yourself some questions before you start to say, "Go with an abstraction." If I decided at that time, saying, let's go with a shared library that I include footer and other stuff. Then the question starts to be, now I have teams distributed around Europe. One team maybe has a specific request, and the other team doesn't have the context, why do they have to do certain things. Then they do that, and maybe they create some bugs. Then in order to test, it became a nightmare because the ownership is in different places. It's true, I have to write five times, but it's way less error-prone than having, in this case, a component library that contains 2 things that are changed 3 times in 5 years.

Participant 3: A question related to sharing the libraries from bootstrap to the components. You said that the first team went with React and MobX, and the subsequent teams followed the trend. In your opinion, would it be wise to share the React version from the bootstrap to the components?

Mezzalira: Yes. For instance, if you use single-spa, that is a framework that is working with vertical split, and as entry point JavaScript, you have the option to do that. There was a new plugin of Webpack 5 that basically allows you to lazy-load different parts of the application, so different micro-frontends, and share the dependencies. There is definitely a trend around that. We didn't take that thing. If you go a step further and say, everyone has to use that specific version of React, or that specific version of Angular, you can definitely do that. In our case, we wanted to provide a bit of flexibility. What brought us this approach was, for instance, when there was the React hooks that were out, the landing page team decided to implement the alpha in production. They tried that. They learned. Then they shared back with all the other teams that didn't even know that that experiment was there, because it was completely independent. They can take this decision. You don't have to coordinate 11 teams across different places to manage that. You always need to understand the trade-off of your decision, the moment that you share, you need to also think about communication overhead, coordination, deployment, and so on.

Participant 4: I assume you started with a single-page app and then went to the micro-frontends. Was there any trade-off actually, in your experience, going from SPA to a micro-frontend?

Mezzalira: The trade-off that we have is, currently, a new user is downloading React multiple times. That, for us, was an acceptable trade-off for several reasons. We are a streaming platform. We expect, obviously, to be very fast on providing feedback to the user. You need to have a decent connection. Otherwise, it is very difficult that you are going to have the video. The other thing that we have is we try to push the user from the mobile web experience to the mobile native experience, because obviously, the possibility to tweak the video player for live on native, is completely different from a mobile browser. For instance, iOS, last year released Low-Latency HLS. We implemented it straight away. We were one of the first companies that did that. It's something that you cannot have in other places. In our context, the trade-off was downloading slightly more kilobytes. The other nice thing is, what we discovered is when a user is in, it is very unlikely that he's signing out. They remain inside that area. Therefore, we have a huge plus there, because the moment that you load our authenticated area, that is a single-page application, you never downloaded the SDK for credit card. You never downloaded the logic for the regex for checking certain fields, because you don't have it. In reality, yes. The first subscription phase takes slightly more. Then after that, it is very smooth, because at the end, that user is downloading only the experience that he needs to perform, either my account, or the authenticate error, or help, but always in a single way. For instance, on TV, we have an amazing impact. The first few tests that we have done, we saw that there was a drop of the startup of the application, half of the time, in particular low-end devices. For me, that was a big success because, yes, the first time it would take slightly longer, but considering the other parts are not that big, so it won't take too long. When you are in, you have a really nice experience. For me, that was one of the key drivers for this.

 

See more presentations with transcripts

 

Recorded at:

Sep 04, 2020

BT