Transcript文字记录
Luca Mezzalira: Let me start with a quick survey. How many of you have heard about micro-frontends? How many of you are implementing micro-frontends right now? In the last more or less 10 years, I spent a lot of time implementing micro-frontends first. Then in the last 5 when I joined AWS, helping internal teams from Amazon, AWS, plus many of the customers that I work with from New Zealand to Silicon Valley, moving whatever they had to micro-frontends. The reason being is because they have seen that the complexity that they were dealing with day-to-day with the monolith codebase couldn't help them to achieve what really people want when they move to distributed system. We will see later on what this is. Let's start from here. Vast majority of the time when I am called to review an architecture that either is already micro-frontends or they are thinking about moving towards micro-frontends, the situation is more or less this one.Luca Mezzalira:我先做一个快速调查。在座有多少人听说过微前端?有多少人正在实施微前端?过去大约 10 年里,我花了很多时间先实施微前端。然后在过去 5 年加入 AWS 后,帮助 Amazon、AWS 的内部团队,以及我从新西兰到硅谷合作的许多客户,将他们已有的系统迁移到微前端。原因是他们看到日常处理单体代码库的复杂性无法帮助他们实现人们迁移到分布式系统时真正想要的东西。我们稍后会谈到这个。让我们从这里开始。绝大多数时候,当我被邀请审查一个已经是微前端或正在考虑迁移到微前端的架构时,情况大致是这样的。
They take whatever system they have, let's assume this nice e-commerce. Think that this is an e-commerce. Then they said, ok, so we take what we have today and we just move and load everything at runtime. All the red spots are loaded at runtime. This is more or less how many companies that I work with, they start thinking about micro-frontends. Unfortunately, it doesn't work in that way. Because here we start to go back to the roots of how everything started in frontend, backend or whatever. Even in frontend, back in the days, it was Flash developer, work with Flex, AIR, Flash Lite, all the suites of Flash. You had to design architecture. It's not just taking a bunch of libraries, stitch them together, thinking that you are designing a system. You're just implementing decisions made by others on specific solutions. That doesn't work in a distributed system because there is way more that you have to think about, and that's what we're going to talk about today.他们拿着现有的系统,假设这是一个不错的电商网站。然后他们说,好吧,我们把现有的东西直接搬过去,在运行时全部加载。所有红色标记都在运行时加载。这大致是我合作过的许多公司开始考虑微前端的方式。不幸的是,这样行不通。因为这里我们要回到一切开始的地方,前端、后端都一样。即使在前端,过去 Flash 开发者使用 Flex、AIR、Flash Lite 等 Flash 套件时,也需要设计架构。不是只拿一堆库拼在一起,就以为在设计系统。你只是在实现别人在特定解决方案上的决策。这在分布式系统中行不通,因为你需要考虑更多东西,这就是今天要讲的。
What Is a Micro-Frontend?什么是微前端?
Let's start with the basics first because this is probably the most misunderstood part of micro-frontends. What is a micro-frontend in reality? How micro is a micro-frontend, or how micro is a microservice? A vast majority of the time there is the confusion that micro-frontends are just components. Let's try to analyze this nice button. This is a button inside a larger application. What this button does a vast majority of the time is that you want to have a button inside your design system that has the possibility to set a specific label, to set a specific action, to have an ID, maybe also have some capabilities that you set a larger label because your website is translating multiple languages, it will adapt itself based on that. You have a bunch of capabilities and you want to design this capability in a way that the container of this button will take care of setting the button inside a specific context.先从基础开始,因为这可能是微前端中最被误解的部分。微前端到底是什么?一个微前端有多微?或者一个微服务有多微?绝大多数时候,人们混淆了微前端和组件。让我们分析这个漂亮的按钮。这是一个更大应用中的按钮。这个按钮在绝大多数情况下,你希望设计系统中的按钮能够设置特定标签、特定动作、有一个 ID,可能还有一些能力,比如设置更长的标签,因为你的网站要翻译多种语言,它会根据情况自适应。你有一堆能力,你希望设计这些能力时,让按钮的容器负责在特定上下文中放置按钮。
If this button is used for a form and you want to activate this button or enable this button only when the mandatory fields are filled, then you need to set specific capabilities of this button. Similarly, if you want to use exactly the same button in another part of your system, you will be able to do that and instruct how the button should behave. As you have seen in this case, the context is not owned by the button. The button is there trying to abstract capabilities and UI, in this case, also UX. In micro-frontends, we have something different. It's not anymore the container that is instructing someone else how the thing should work. In micro-frontends, when you load that, first of all, usually they are more coarse-grained. They are bigger. It's not just a single button. There is a reason for that. We will see that.如果这个按钮用于表单,并且只有填完必填字段时才激活或启用,那么你需要设置按钮的特定能力。同样,如果你想在系统的其他部分使用完全相同的按钮,你也可以做到,并指示按钮应该如何行为。正如你在这个例子中看到的,上下文并不归按钮所有。按钮在那里试图抽象能力和 UI,这里还包括 UX。在微前端中,情况不同。不再是容器指示其他东西如何工作。在微前端中,当你加载它时,首先,它们通常更粗粒度。它们更大。这有原因。我们稍后会看到。实际上,微前端本身知道事情如何运作。这使微前端能够完全独立。正如你所见,我用了几个关键词来描述两者。当我们考虑按钮时,我们考虑可重用性。我们想使用一个设计系统或共享组件库,随处可见。我们想设计它并使其可重用。我们想在整个应用中拥有相同的外观和感觉。问题是,当你这样做时,领域会泄漏,因为它必须在系统的多个领域、多个区域中对容器可用。容器直接知道如何指示组件以某种方式行为。我们这样做是为了减少重复。这是组件的主要目标。
In reality, the micro-frontend itself is aware of how the things are working. That enables us to have a micro-frontend that is completely independent. Now, as you have seen, I've used some keywords here to describe one and the other. When we think about the button, we think about reusability. We want to use a design system or a shared component library that is available everywhere. We want to design that and have that for reusability. We want to have the same look and feel inside the application. The problem is, when you have that, you have a leak of the domain, because it has to be available in multiple domains, multiple areas of your system, to the container. The container has a direct knowledge of how to instruct the component to behave in a certain way. We want to do that because we want to reduce duplication. That's the main goal of a component.我们这么做也是为了 UI 一致性。微前端不能这样。它不能是一个在运行时加载的组件,因为我们想要的是为独立性优化。我们想减少团队的外部依赖,并实现快速流动。当我们这样想时,我们需要减少外部依赖。因此,我们不希望不惜代价减少重复或可重用性。因此,思考微前端的方式必须不同。微前端最终与组件差别很大。它们在一定程度上分叉。因为当你为可重用性优化时,你会在容器和组件之间创建某种耦合。现在,每个人都知道彼此,组件知道得少一些,容器知道得多一些。微前端为了能够独立部署,必须自包含。因此,我们需要以不同的方式行动和设计。
We want also to do that because we want to have UI consistency. Micro-frontend cannot be like that. It cannot be a component loaded at runtime, because what we want instead is optimized for independence. We want to reduce the external dependencies of our teams, and we want to enable fast flow. When we think about that, we need to reduce external dependencies. Therefore, we want not to have the reduction of duplication at any cost, or reusability at any cost. Therefore, the way we should think about micro-frontends must be different. Micro-frontends, at the end, are way different from components. They diverge to a certain extent. Because when you optimize for reusability, you create a form of potentially coupling between the container and the components. Now, everyone knows about each other, the components less than the container. Micro-frontends is that, in order to be independently deployable, it has to be self-contained. Therefore, we need to act and design things differently.正如我所说,这次演讲涵盖微前端的迁移部分。我花了无数天和年观察不同的实现,我基本上在这次演讲中收集了哪些有效、哪些无效以及原因。绝大多数时候,在看到那个漂亮的电商网站一切在运行时加载的图表之后,对话的第二步是,好吧,Luca,我们决定使用 Single-SPA、Next.js multi-zone、Web Components、Module Federation 等一堆库/技术实现。在流程的这个阶段,这毫无意义。这部分相关,但不是在对话的这个阶段。在这个阶段,我们甚至不知道如何拆分这个系统。因为如果你考虑微前端,我们想要实现的是独立团队以他们自己的节奏部署,他们想要拥有系统的一部分,只是一部分。
The Migration Journey - Learnings from Hundreds of Teams我们需要问自己的真正问题是,我们想要实现什么?你以为你知道,但相信我,并非所有人都知道。因为分布式系统不同。这里不再是全员参与,开始写代码,然后创建这个有 25000 个视图的怪物,一切耦合,难以部署,我们需要一起部署所有东西等等。最近,例如,我在意大利和一个客户合作,他们有大约 900 个 Web 应用视图需要从 Vaadin 迁移出来,Vaadin 是一个旧的 Java 和 HTML 框架,但是一个 Java 框架,没有任何 API。他们只需要迁移离开 Vaadin,我想是版本 3,现在他们用的是版本 22。你可以想象,现实中存在大量分歧。真正的问题是,他们从未问过自己,我们想要实现什么?
As I said, this talk will cover the migration part of micro-frontends. I have spent countless days and years looking at different implementation, and I collected, basically, in this talk, what works, what doesn't work, and why. A vast majority of the time, after seeing that diagram of the nice e-commerce, that everything will load at runtime, the second step in the conversation is, ok, so Luca, we decided to go with Single-SPA, or Next.js multi-zone, or we are using Web Components, or we are using Module Federation, a bunch of libraries/technical implementation, that at this stage of the process, doesn't make any sense. This part is relevant, but not at this stage of the conversation. At this stage, we don't even know how we are going to split this system. Because if you think about micro-frontends, what we want to achieve is independent teams that are deploying at their own pace, and they want to have their own part of the system, just a part of the system.他们只是继续在这个系统上添加功能,很快变得非常复杂。第一件事是,当我们考虑分布式系统时,人们真正想要的是,尤其是高管层,团队的自主权,因为他们厌倦了无数的外部依赖,团队总是说,我们不能交付这个功能,因为我们需要等待另一个团队完成系统的特定部分等等。他们希望团队有自主权。他们希望有独立团队一起发布东西。他们希望有可扩展性。不要只想着可扩展性是我们要迁移到云,因此我们可以水平或垂直扩展,根据需要。我们需要组织可扩展性。这要难得多。这里涉及人。不是机器,是人。我们需要考虑的可扩展性是我可以在组织中像这样插入一个新团队来开发系统的特定区域,而不影响其他团队的吞吐量。然后我们需要考虑敏捷性。可能每个人都记得疫情期间发生的事情。一夜之间,企业在一周或更短的时间内不得不改变他们的商业模式。这不是一生只发生一次的事情。我们现在看到许多其他外部因素如何影响 AI 的到来或 AI 的普及。我们需要改变并重新审视我们对事情如何发生的看法。因此,如果你不在系统中应用模块化,那就会变得困难,因为你有一个非常僵化的系统,而外部世界正朝着不同方向快速变化。
The real question we need to ask ourselves is, what do we want to achieve? You think that you know that, but trust me, not everyone does. Because distributed systems are different. Here it's not anymore, we have all hands on deck, and start to write code, and create this monster of 25,000 views, that is everything coupled and difficult to deploy, we need to deploy everything together, and so on. Recently, for instance, I was in Italy with a customer that has something like 900 views of a web application that has to be migrated from Vaadin, that is an old Java and HTML framework, but a Java framework, that didn't have any API, anything. They just need to migrate away from Vaadin, I think it was version 3, now they are version 22. As you can imagine, there is a lot of divergence in reality. The real question is, they never ask themselves, what do we want to achieve?然后我们希望达到快速流动,因为现实是我们发现如果能更频繁地在生产环境中发布,我们可以从用户那里获得更多反馈。我们可以更好地设计东西。我们可以拥有一个更好的系统,符合我们在那里看到的所有其他东西。你注意到什么了吗?我在谈论使用 React 做某些事情,或者使用 Angular 之类的东西。没有什么是技术性的,因为实际上我们每天做的事情是使业务和我们的客户及用户能够成功完成他们的任务。因此,这些特性必须在设计任何东西时考虑进去,前端、后端,随便你。因为如果你不考虑,那就别怪六个月后,现在借助 GenAI,你得到的是意大利面条式代码。这不是 GenAI 的问题。是你不知道你想要解决什么问题。
They just continue to add features on this system that became quite complicated very quickly. The first thing is, when we think about distributed systems, what people really want from that, the C-suite especially, is teams' autonomy, because they are tired that there are countless external dependencies, teams that are saying, no, we cannot deliver this feature because we need to wait for the other team to finish a specific part of the system and stuff like that. They want to have teams' autonomy. They want to have independent teams that are shipping things together. They want to have scalability. Don't think about scalability on, we need to move to the cloud, and therefore we are going to have scalability because there we can scale horizontally, vertically, whatever is needed at some point. We need to have organizational scalability. That is by far more difficult. There are people involved here.因为社会技术系统有三个领域总是相互关联:架构、组织和工程文化。在某些情况下,我们以为我们只是在做一个设计决策。实际上,我们触及了所有三个领域。我给你举个例子。2017 年我开始谈论微前端时,我数了数,大约每隔一个会议,问题就落到:我们应该用 monorepo 还是 polyrepo?总是这个问题。我的回答是:看情况。每个架构师都会这么说。我说看情况有充分理由。因为你选择其中一种策略的那一刻,你基本上也在设计你的工程文化,甚至你的组织。想象一下,你进入一个没有多少高级工程师的环境或上下文,可能只有少数几个负责决策。
It's not machine, it's people. We need to think about scalability in terms that I can plug a new team like this inside my organization for developing a specific area of the system without affecting the throughput of the others. Then we need to think about agility. Probably everyone here remembers what happened during the pandemic. Overnight there were businesses that in a week or less had to change their business model. That is not one thing that happened once in a lifetime. We have seen now with many other things that are external to our business how these things are affecting the advent of AI, or the democratization of AI. We need to change and revisit how we thought about how the things are happening. Therefore, if you don't apply modularity inside your system, then it's becoming difficult because you have a very rigid system while outside the world is moving very fast in different directions.假设我们选择 monorepo。使用 monorepo,绝大多数时候你会采用主干开发。分支应该非常短暂,节奏要快。master 应该始终正常运行,没有崩溃,否则会影响所有人。你还依赖开发者的纪律。因此,如果你说,在这个共享文件夹中,我们只共享真正需要共享的代码和组件,你试图依赖团队做正确的事。然后现实来了。然后你可能有一个功能昨天就必须交付。因此,更容易的做法是把这段代码塞进共享库。问题在于,当你解决这个问题时,可能已是六个月后,幸运的话,或者一两年后。
Then we want to reach the fast flow, because the reality we discovered is that if we are capable of shipping more often in production, we can gain more feedback from the users. We can design things better. We can have a better system that complies with all the other things that we have seen over there. Do you notice anything here? I'm talking about, use React for doing certain things, or use Angular or stuff like that. There is nothing that is technical because in reality what we do day-to-day is enabling the business and our customers and users to succeed in their tasks. Therefore, these characteristics have to be taken into account when you are designing anything, frontend, backend, you name it. Because if you don't, then don't blame that in 6-months' time now instead of 3 years with GenAI you can reach spaghetti code. It's not a problem with GenAI. It's a problem that you don't know what you are trying to solve.因为然后你开始增长。你打开了一些门,变得非常复杂。Monorepo 可能非常有帮助,尤其是在你与有一定资历的开发者合作时。他们能够处理这种内在的复杂性。它继承在他们的工程实践中。一个看似只有技术方面的简单决策,比如 polyrepo 和 monorepo,却影响了所有这三个维度。你在一定程度上组织团队的方式。重要的是,当你做决定时,要记住这三件事是连在一起的。一个架构决策可能会影响另外两个。你说我们采用分布式系统,但如果组织 20 年来一直是单体,它就不能保持不变。架构应该支持你为组织预先做出的某些决策,因此,如果你决定采用分布式系统,你就决定去中心化。因此,不再有象牙塔。你需要慢慢但稳定地去中心化多件事情。决策上将采取更加联盟的方式。这些仍然重要。不要认为这只是技术决策。
Because there are these three areas of a socio-technical system that are always linked: architecture, organization, and engineering culture. There are certain situations where we think that we are just taking a design decision. The reality is we are touching all the three. I'll give you an example. At the beginning when I started to talk about micro-frontends in 2017, I counted roughly every other conference that I talk about them, the question lands into, should we use monorepo or polyrepo? That was always the question. My answer is it depends. Every architect would say that. There is a strong reason why I'm saying it depends. Because the moment that you select one of the two strategies, you are basically designing your engineering culture as well as potentially your organization. Imagine that you are going in an environment or in a context where you don't have many senior engineers, maybe you have a few of them that are taking the shot.实际上,上下文为王。你怎么做这个决定?然后,非常频繁地,我记得过去,人们急于采用 Spotify 模式。你听说过 Spotify 模式吗?我们需要有行会。我们需要每个团队独立去中心化,自己做决定,以及那里描述的所有实践。几年后,令人惊讶的是,有一篇文章说,我们在 Spotify 甚至没有实施这个 Spotify 模式。你是 Spotify 吗?你在 Spotify 工作吗?也许你是。那里有特定的工程文化。有特定的目标。有特定的目的。非常频繁地,我们忘记了像这次演讲中做出的某些技术决策可能对你的上下文没有用。你需要先理解你的上下文。即使现在当我们谈论 AI 时,我们在谈论上下文工程。不是只谈工程,不是只谈最佳实践。因为如果你不给生成式 AI、你的代码助手或任何东西提供上下文,那就别惊讶它没有生成你想要的东西。
Let's say we go with monorepo. With monorepo, a vast majority of the time you are going to use trunk-based development. The branches should be very short-living, and you should have a fast pace. You should have master always up and running without any crashes, because otherwise you are going to affect everyone. You also rely on the discipline of developers. Therefore, if you say, in this shared folder we are going to share only the code and components that are really needed to be shared, you try to rely on the teams to do the right things. Then reality strikes. Then you have maybe a feature that has to be delivered by yesterday. Therefore, it's easier if we shove this code inside of the shared library. The problem is that when you are going to solve this stuff, it probably will be six months, if you are lucky, or a couple of years later.我想强调的另一件事是,微前端绝对不是银弹。我的意思是,它只应该在需要分布式系统时使用。即使你在后端有分布式系统,你可能也不需要微前端。这一点非常重要。不要认为我们迄今为止在旧的、好的单体上所做的一切都应该被抛弃。这不是炒作。这是架构。架构表达某些特性。要么你提前把特性定好,否则很难选择特定的架构。
Because then you start to grow. You open up some doors that becomes very complicated. Monorepo could be extremely helpful, in particular, when you are working with developers that have a certain seniority. They are capable of handling this kind of complexity. It is inherited inside their engineering practices. A simple decision like polyrepo and monorepo that seems apparently only on the technical side is affecting all three of those dimensions. The way you all structure the teams to a certain extent. It's very important that you bear in mind when you take a decision that these three things are linked together. A decision that is an architectural decision could affect the other two. The way you say, we go with distributed systems, but then the organization cannot remain as it is if you have a monolith for 20 years. The architecture should enable certain decision that you make up front for the organization, therefore, if you decide to go distributed system, you decide to decentralize. Therefore, no more ivory tower. You need to decentralize, slowly but steadily, multiple things. There will be a more federated approach on decisions. Still, these things matter. Don't think that it's just a technical decision.让我们开始这个迁移之旅。早在 2019 年,我在挪威。我在举办一个关于微前端的研讨会。微前端这个术语出现在 2016 年。2019 年仍然早期。你需要想清楚如何为人们创建一些心智模型,以便正确地进行架构设计。我创建了微前端架构的词汇。然后我开始创建模式,或者更好的心智模型,来帮助人们更好地掌握它。2019 年,在这个研讨会期间,我灵光一现。我记得我在休息时把想法写在了一张餐巾纸上,因为它是突破性的。那就是决策框架。因为我当时在做几个关于微前端的演讲。有几件事反复发生,我在服务器端渲染、边缘端、客户端都测试过。总是同样的事情。通常,在微前端中,你有几样东西。
In reality, context is king. How can you take this decision? Then, very often, I remember back in the days, there was this rush towards the Spotify model. Have you ever heard about Spotify model? We need to have guilds. We need to have every team that is independently decentralized and taking their own decision, and all the practices that were described there. A few years later, surprise, there was an article that came out saying, we didn't even implement this Spotify model in Spotify. Are you Spotify? Are you working for Spotify? Maybe you do. There, there is specific engineering culture. There is specific goal. There is specific aim. Very often, we forget that certain technical decisions that were made in a talk like this might not be useful for your context. You need to understand your context first. Even when we talk now about AI, we talk about context engineering.首先,你要么有一个 application shell,要么有一个 UI composer。区别是什么?Application shell 通常是你如何描述客户端渲染解决方案中的容器微前端。而 UI composer 在服务器端渲染解决方案中可能存在,也可能不存在。因为 composer 只是组合服务器端渲染的片段,这些片段随后被组合并发送到浏览器。Application shell 有更多逻辑和事情要处理。绝大多数时候,在 application shell 层面,你处理配置、认证、组合和路由。这是你关心的四个方面。一个好的 application shell 是可以在你的系统之外复用的,在你当前的系统之外。它必须是领域无关的。它不必每次在微前端改变时都发布。否则,你会把领域泄漏到系统之外。
Context engineering, not just engineering, not just best practice. Because if you don't provide the context to generative AI, to your code assistant, to whatever it is, then don't be surprised if it doesn't generate what you are looking for. Another thing that I want to highlight is that micro-frontends are definitely not a silver bullet. What I mean by that is, it should be used only when you need distributed system. Even if you have distributed system on the backend, you might not need micro-frontends. It's very important, this one. Don't think that everything we have done so far with our old, good monolith should be trashed. This is not hype. This is architecture. Architecture is expressing certain characteristics. Either you have the characteristics nailed up front, but otherwise, it's difficult to choose a specific architecture.你通常有两种微前端风格。你有纵向和横向。你需要识别它们。一个微前端有多微?我们回到同样的问题。实际上,它们并非互斥。在系统初期,你可能会发现拥有更粗粒度的微前端是好的。而当你走向更成熟,领域和事物在变化时,你可能会更细粒度。这绝对是两种方法。纵向意味着一个视图或一组视图代表一个微前端。横向意味着一个微前端与其他微前端共存,就像我在这里强调的,在同一个视图中。这是两者之间的区别。一个更粗粒度,另一个更细粒度,但不会像组件那样太细。
Step 1: Micro-Frontends Decisions Framework你需要做的另一个决定是,我如何渲染这些东西,在服务器上还是在客户端。这非常重要,因为它改变了一切。我将展示几个我看过的常见架构。显然,它们之上还有边缘情况。这些是关键。绝大多数时候,如果你做电商,你会选择服务器端渲染。你需要最好的性能,因为每毫秒都重要。客户端渲染则适用于,也许你有一个需要认证的应用,你不关心有机 SKU。因此服务器端渲染可能完美。或者你有一个运行在特定设备上的解决方案。我在嵌入式设备上工作了很久,因此客户端渲染对我来说是家常便饭。有不同框架。在这个阶段,我们可以开始讨论框架了。
Let's start in this migration journey. Back in 2019, I was in Norway. I was delivering a workshop on micro-frontends. The term micro-frontends came out in 2016. 2019 was still early days. You have to figure out how you can create some mental models for people in order to approach the design properly of an architecture. I created the vocabulary around micro-frontends architecture. Then I started to create patterns or better mental models to help people to grasp it better. In 2019, during this workshop, I had this light bulb in a moment. I remember I wrote the idea in a napkin during a break because it was groundbreaking. That was the decision framework. Because I was delivering a few talks on micro-frontends. There are a few things that are happening over and over again that I tested on server-side rendering, edge-side, on client-side. It's always the same thing. Usually, in micro-frontends, you have a few things.如果你决定采用客户端渲染,最常见的是,根据我的经验,现在通常是 Module Federation 或 Single-SPA。这是我更常看到的两个主要框架。还有 Web Components。如果你使用 Angular,Native Federation。Piral,这是一个很好的解决方案,支持客户端或服务器端渲染。它提供了很多我在这里描述的东西作为平台。它完全开源。一定要看看。在服务器端渲染方面,你仍然可以使用 Module Federation,但有一些限制。如今,像 Next.js 和 Astro.js 这样的现代框架,以及最近 Cloudflare 推出的 Web Fragments,它们提供了更内建的方法。例如,Next.js 有 multi-zone 的概念,可以用来创建微前端,因此它将你的应用拆分成多个块。我们稍后会看到如何做。这是一种方法。
First of all, you have either an application shell or a UI composer. What's the difference? An application shell usually is how you describe the container micro-frontends on a client-side rendering solution. While a UI composer might or might not be available in a server-side rendering solution. Because the composer is just composing server-side rendering fragments that will be then composed and shipped to the browser. An application shell has way more logic and stuff that it has to handle. The vast majority of the time, at the application shell level, you handle configuration, authorization, composition, and routing. Those are the four aspects that you care about. A good application shell is the one that could be reused outside your system, the current system that you're having. It has to be domain agnostic. It doesn't have to be shipped every time that a micro-frontend is changing. Because otherwise, you leak the domain outside the system.Astro.js 有 Astro Server Islands,例如,它使你能够拥有 UI 的特定部分完全自主渲染。在同一个视图中,你可以有一部分完全缓存,可以在 CDN 级别使用并渲染和缓存,另一部分可以多次刷新。你基本上依赖于这个服务器岛屿,它可以在服务器上更新 UI 而无需刷新整个页面。这很方便。然后你需要考虑,当你选择这些东西时,如何在微前端之间路由。我如何从首页移动到目录。大多数情况下,路由部分与你渲染东西的策略一致。如果是客户端渲染,你会有路由存在于 application shell 中。如果是服务器端渲染,你会有路由发生在边缘或应用负载均衡器、API 网关,或者你决定在服务器端的任何地方。
You usually have two flavors of micro-frontends. You have a vertical and horizontal. You need to identify them. How micro is a micro-frontend? We go back to the same question. The reality is, those are not mutually exclusive. At the beginning of a system, you might find yourself that it's fine to have more coarse-grained micro-frontends. While when you move towards more maturity and the domain and things are changing, you might go more granular. Those are definitely two approaches. Vertical means that you have one view or group of views that are representing a micro-frontend. Horizontal means that you have a micro-frontend that is living alongside other micro-frontends, like in this case that I'm highlighting here, in the same view. This is the difference between the two. One is more coarse-grained. The other one is more fine-grained, but not too fine-grained like a component.然后你需要考虑如何通信。当你有一个纵向拆分时很容易。你有一组视图生活在一起。绝大多数时候,通信非常简单,因为它生活在同一个领域内。另一方面,当你在同一个视图中有多个微前端时,你需要考虑如何通信。通常,三个常见方案是自定义事件、事件发射器和响应流,其中强烈偏好事件发射器。主要原因是因为它最简单。它提供松散耦合。它还使你能够定义进出特定微前端的事件。每个人都可以接入它们。你不必有一个复杂的全局状态,就像许多团队所做的那样。然后他们责备架构,实际上他们做了错误的决定。
The other decision you need to make is how I'm going to render this stuff, on the server or on the client. That's very important because that changes everything. I will show you a few common architectures that I have seen. Obviously, there are edge cases on top of them. Those are quite key. The vast majority of the time, you will choose server-side rendering if you do an e-commerce. You need to have the best performance ever because every millisecond matters. Client-side rendering is everything that, maybe you have an application that is running behind authentication, you don't care about an organic SKU. Therefore, server-side rendering could be perfect. Or you have a solution that runs inside specific devices. I worked on embedded devices for a long time, therefore, client-side rendering for me was bread and butter. There are different frameworks. At this stage, we can start to discuss about frameworks.现在我见过的常见架构。第一种是客户端渲染。客户端渲染非常简单。无论你使用本地部署还是云提供商,通常你有一个 CDN 来缓存你的静态资源,因为归根结底,如果你考虑单页应用,它只是一堆 JavaScript 文件、CSS 和 HTML。没有更多。这是高度可缓存的,因此你可以将静态文件存储在你的对象存储、blob 存储或随便叫什么中。除非绝对必要,否则请不要使用容器来提供静态文件。因为非常频繁地,我看到开发者告诉我,我们正在使用 Kubernetes 来提供静态文件。不要这样做。你需要维护安全态势和不必要的复杂性。如今,CDN,例如在 AWS 的情况下,CloudFront 和 S3,是一种对象存储。你可以极大地扩展。
If you decide to go with client-side rendering, the most common one, in my experience, is Module Federation or Single-SPA nowadays. Those are the two main frameworks that I have seen more often used. Then there are also Web Components. Native Federation, if you're using Angular. Piral, that is a fantastic solution that works either client or server-side rendering. It provides a lot of the things that I'm describing here as a platform. It's completely open source. Definitely check it out. On the server-side rendering, you can still use Module Federation with certain limitations. Nowadays, modern frameworks like Next.js and Astro.js, and also Web Fragments came out recently from Cloudflare, those are providing a more built-in approach. Next.js, for instance, has an idea of multi-zone that you can use in order to create micro-frontends, so it is splitting up your application in multiple chunks. We will see later how. This is an approach.在我之前的公司,我们的平台在一分钟内从 10,000 用户扩展到 200 万。我们很好地提供了所有服务。提供前端从未出现问题,后端也很少出现问题。相信我,这些东西扩展得非常好。你的部署基本上是将特定的 JavaScript 块推送到 blob 存储或对象存储。这就是你的部署。处理起来非常容易,而且高度可缓存。在服务器端渲染方面,我们仍然有 CDN,因为许多人,例如,认为 CDN 只对静态文件有用。并非如此。CDN 现在提供更多功能。你在边缘有计算。你有安全性,因此你可以做更多事情。绝大多数时候,如果你考虑电商,我跟踪了这个领域的几个客户,他们通常按一级 URL 划分。你可以有一个首页微前端。
Astro.js has Astro Server Islands, for instance, that enables you to have a specific part of your UI that is completely rendered autonomously. In the same view, you could have one part that is fully cached and can be used and rendered and cached at the CDN level, and another part that can be refreshed multiple times. You basically rely on this server island that, on the server, can update the UI without refreshing the entire page. That is pretty handy. Then you need to think about, when you select this stuff, on how we route between micro-frontends. How I can move from home to catalog. Majority of the time, the routing part goes alongside the strategy you want to render stuff. If it's client-side rendering, you will have a routing that is living inside the application shell. If you are server-side rendering, you have a route that can happen at the edge or an application load balancer, API gateway, wherever you decide to have on your server-side.你可以有一个产品微前端,包括产品 ID。你可以有 myaccount。你可以有 checkout 等等。这很常见。然后你如何提供它,可以是带容器的 API 网关,也可以是带容器的应用负载均衡器,完全取决于你。这就是你拆分的方式。酷的是,如果例如产品崩溃了,它不影响其他任何东西。如果我在产品团队,我可以每天发布任意多次,没有人会被影响。我可以测试我的东西,一切都会正常工作。这就像经典的服务器端渲染方法,但你不是只有一个东西,而是有多个容器。如果你访问 formula1.com,这是我几年前合作过的一个客户,他们使用了这种方法。我们在拉斯维加斯的 re:Invent 上做了一个演讲,高级架构师和我一起上台,谈论的正是这个。如果你想看,可以在 YouTube 上找到。
Then you need to think about how to communicate. It's easy when you have a vertical split. You have a group of views that are living all together. The vast majority of time, the communication is pretty easy because it's living inside the same domain. On the other side, when you have multiple micro-frontends in the same view, then you need to think about how to communicate. Usually, the three usual suspects are custom events, event emitter, and reactive streams, with a strong preference around event emitter. The main reason is because it's the simplest one. It provides loose coupling. Also, it enables you to define events in and out of specific micro-frontends. Everyone can plug into them. You don't have to have a complicated global state like many teams have done. Then they blame the architecture when they took a wrong decision.我看到的另一个更高级客户的方法是这种方法。和之前方法相同,但他们在运行时加载页面。如果你考虑 Next.js、Astro.js 或任何现代服务器端渲染框架,它们所做的实际上是将系统按页面划分。你可以在运行时加载页面,因此我不必每次都部署我的基础设施。就像之前一样,我把 JavaScript 文件和微前端丢到对象存储中,那就是我的部署。系统的其余部分始终运行。我不动它。这很常见,我在更高级的客户中见过。然后,如果你现在使用更新的框架,还有更复杂的方式。例如,这是一个业内非常知名的名字,BBC。它是英国的广播公司,非常出名。
Step 2: Common Architectures他们使用的是不同级别的缓存。这显然简化了,但你可以在网上找到关于这个的演讲。他们所做的基本上是有一个 composer,从不同服务检索 HTML 片段,全部组合起来。其中一些被缓存,以避免对上游依赖造成压力,因此它们从缓存中检索。可以简单到像 Redis 缓存,然后提供给浏览器。这稍微复杂一些,但运行得很好。这个实现基本上就是 BBC News,所以可能类似于你在这里看到的 CNN。如果你考虑他们处理多少流量,这就是他们处理的方式。边缘呢?我没有谈到边缘,因为截至今天,边缘在世界各地的企业中还没有得到很好的使用。我想我只有在保险领域发现了一个案例,因为他们采用多区域方法复制数据。
Common architectures that I've seen nowadays. First one is client-side rendering. Client-side rendering is very simple. In whatever, on-prem or cloud provider you're using, usually you have a CDN that is caching your static artifacts, because at the end of the day, if you think about a single-page application, it's a bunch of JavaScript files, CSS, and HTML. Nothing more. That is highly cacheable, and therefore you can store in your object storage, blob storage, or whatever you want to call it, your static files. Please don't use a container to serve static files unless it's strictly necessary. Because very often I've seen developers telling me, we are using Kubernetes for serving static files. Don't do that. You need to maintain a security posture and complexity that is not needed. Nowadays, a CDN, in the case of AWS, for instance, CloudFront with S3, is an object storage. You can scale immensely.他们的服务在多个地区、世界多个地方可用。所有其他情况,如果你在边缘运行服务器端渲染,而你的数据停留在一个区域或一个数据中心里,你不会获得太多好处。开发者体验更差。你绝对可以在区域内完成所有事情,并利用 CDN 来缓存东西,而不是渲染东西,这肯定会运行得很好。
In my previous company we were scaling from 10,000 users to 2 million in one minute inside the platform. We were serving everything very nicely. We never had an issue on serving the frontend, and very rarely an issue on the backend. Trust me, these things scale very nicely. Your deployment basically is pushing a specific JavaScript chunk into the blob storage or object storage. That is your deployment. Super easy to handle that, and highly cacheable. On the server-side rendering, we still have a CDN because many people, for instance, think that the CDN is just useful for static files. Not really. CDN nowadays offers you way more. You have compute on the edge. You have security, so you can do way more. Vast majority of the time, if you think about e-commerce, I follow several customers on that space, they usually divide in first-level URL. You can have a home micro-frontend.第三步,识别微前端。我们已经了解了所有东西如何工作。我对每个团队的建议是从简单开始。从纵向拆分开始。你有一个视图或一组视图代表一个微前端。复杂性较低。更容易拆分,因为如果你考虑,你拿一个网站,看看一级 URL。看看你的用户如何使用你的网站,然后做决定。构建和部署更快,因为实际上,如果你习惯于使用服务器端渲染方法或客户端渲染方法,基本上是你已经在做的工作的一部分,只是把它拆分出来并与其他部分分开。很简单。回滚也更简单。这是另一个重要方面。我们稍后会更多讨论。你需要考虑的如何设计、一个微前端有多微的一件事是:集成者与解耦者。
You can have a products micro-frontend that includes also the product ID. You can have myaccount. You can have checkout, and so on. This is quite common. Then how you want to serve that, it could be API gateway with a container, it could be an application load balancer with container, completely up to you. This is how you split. The cool thing is, if, for instance, products is down, it doesn't affect anything else. If I'm in the product team, I can release how many times I want per day, no one will be affected. I can test my stuff and everything will work very nicely. This is like classic server-side rendering approach that you will see, but instead of having one thing, you will have multiple containers. If you go to formula1.com, one of my customers that I worked with a couple of years ago, is using this approach here. We did a talk at re:Invent in Vegas where the senior architect came with me on the stage talking about exactly this. You can find it on YouTube if you want.有一些指标或启发式方法可以帮助你理解是否想要合并代码。例如,代码波动性。你多久改变一次?如果你有一个系统,事先知道里面有 10 种支付方式,但其中 9 种不经常改变。你只需设置这个 SDK。SDK 每年或更长时间改变一次,但有一种支付方式改变得非常频繁,你可能可以拆分成两个微前端,而不是像我经常看到的十个。因为实际上,在开始时,用更粗粒度更容易,然后如果需要再决定拆分。我做了很久的开发者。我喜欢做抽象。开发者喜欢做抽象,因为这很酷。我可以开始创建一个库,也许我会重用,我的团队也会到处重用。绝大多数时候,你的库在整个系统生命周期中只被用了两次。对此很抱歉,但这就是现实。我年纪更大以后(不能说是更聪明),才明白这种事不划算。
The other approach that I've seen for more advanced customers is this one. Same approach as before, but they are loading pages at runtime. If you think about Next.js, Astro.js, any server-side rendering framework nowadays, what it does is literally dividing the system in pages. You can load the page at runtime, so I don't have to deploy my infrastructure every time. It's literally, I drop my JavaScript file and my micro-frontend inside object storage, like before, and that is my deployment. The rest of the system will be always up and running. I don't touch that. That is quite common, I've seen with more advanced customers. Then you have a more sophisticated way to do that if you're now using the more recent frameworks. This one is, for instance, a very well-known name in the industry called BBC. It's a broadcaster in the UK, and very well-known in general.你需要考虑的另一件事,我想给你举个例子。这是我当时思考系统的方式。我在一家叫 DAZN 的公司工作,它是一个像 Netflix 一样的直播体育流媒体平台,但做的是直播体育。这些主要是我们单页应用的主要部分。当我开始查看流量和用户如何使用我们的系统时,我发现首页有数百万请求。然后有一个下降,因为人们没有被说服,或者别的什么。然后我开始想,所以首页可以是一个微前端。然后我开始说,然后用户进去,登录、注册,因为如果他去首页,意味着用户还没有登录或没有注册服务。
What they use is different level of caches. This is a simplification, obviously, but you can find online the talk about that. What they do is basically have a composer that is retrieving from different services, HTML fragments, composing all together. Some of them are cached in order to not create strain towards the upstream dependencies, and therefore they're retrieving from the cache. It could be as simple as a Redis cache, and then serve to the browser. That is slightly more complicated, but served very well. The implementation of this is basically BBC News, so probably similar to what you have here in CNN. If you think about how much traffic they handle, this is how they are handling that. What about edge? I didn't talk about edge, because edge, as of today, is not very well used by enterprises worldwide. I think I found one only in the insurance space that did that because they have data replicated in multi-region approach.因此,我可以把引导体验放在一起,另一个微前端。然后我有目录和视频播放器,它们在一起可以是另一个微前端。然后我有我的账户,它是一个认证区域,因此我想要一个独立的,也因为我们在混合不同的领域。最后,我们有帮助和常见问题解答,另一个微前端。这就是我们的系统。非常简单。我们开始非常简单。然后我们发现在引导微前端中有一些复杂性,登录、注册,所以我们拆分它。那是在我们开始感到团队内部摩擦之后。这很重要。不要在这里做过早的抽象。尽量在你做的事情上保持简单。在这种情况下,然后我们开始划分团队 A、团队 B、团队 C、团队 D。这就是我们拆分和分配微前端的方式。
Their services are available in multiple regions, in multiple parts of the world. All the others, if you are running server-side rendering on the edge, and your data is staying in a single region or a single data center, you're not gaining much. You have a worse developer experience. You can do everything definitely inside the region and leveraging the CDN, not for rendering stuff, but for caching stuff, and definitely is something that will work very well.现在我们到了有趣的部分,那就是端到端地完成第一个微前端。这是一个建议和推荐,不仅对微前端,我也为世界各地的无服务器客户提供建议。同样地,对于无服务器,如果你在迁移,无论是什么,端到端地完成系统的一个块。它是一个单独的 API,一个单独的微前端,但端到端地完成。为什么?因为你将触及很多东西,很多你以后会重用的工作。首先,你从 application shell 开始。一些启发式方法。它必须是技术无关的。非常频繁地,我看到客户说,我们决定用 Angular,application shell 也是 Angular。问题是,六个月后你立即开始遇到一些挑战,因为 Angular 在改变版本,现在你需要在你的微前端系统中维护不同版本的 Angular。
Step 3: Identify Micro-Frontends可行,但开始时简单。你必须考虑如何在旧系统、遗留系统和微前端之间路由。如何组合微前端:横向、纵向、客户端渲染、服务器端渲染。通常,application shell 应该很快稳定下来。我这里的经验法则或启发式是,如果你能够在一年后创建一个不经常改变的 application shell,那么你在设计 application shell 上就走在正确的道路上。因为它不做太多事。它非常无关。它没有任何领域特定的东西。它只是以最佳方式加载和路由微前端。显然,有时你需要向所有人暴露一个初始配置,因此 application shell 是做这类事情的最佳位置。
Step three, identify micro-frontends. We have understood how everything works. My suggestion to every single team is start simple. Start with a vertical split. You have one view or group of views that represent a micro-frontend. There is less complexity. It's easier to split, because if you think about that, you take a website, you look at the first-level URL. You look at how your users are using your website, and you take a decision. It's faster to build and deploy because in reality, if you are used to working in a server-side rendering approach or in a client-side rendering, it's basically a portion of what you are doing already, and it's just split it and divide it from the others. Nothing more. Very easy. It's easier to roll back as well. That is another important aspect. We will discuss more about that. One thing that you need to think about how to design, how micro is a micro-frontend, is this one: integrators versus disintegrators.第三步,识别微前端。先弄清楚整体是怎么工作的。给每个团队的建议:从简单开始。垂直拆分。一个视图或一组视图,算一个微前端。这样复杂度低。拆起来容易。看网站,看第一级URL。看用户怎么用,然后做决定。部署快,因为服务端渲染或客户端渲染,你本来就在做。只是拆出来,跟其他部分分开。就这些。回滚也容易。这点很重要,后面再说。设计时要想,微前端有多微。这里有两个角色:集成者和解构者。
There are some metrics that will help you, or heuristics, that will help you to understand if you want to co-locate code or not. For instance, code volatility. How often do you change? If you have, for instance, a system that you know upfront that there are 10 payment methods inside your system, but 9 of them don't change very often. You just set up this SDK. The SDK is changing rarely every year or whatever it is, but there is a payment method that is changing very often that probably you can split in two micro-frontends, not in 10, like I've seen very often. Because the reality is, at the beginning, it's easier to work more coarse-grained, and then you decide to split it up if and when it's needed. It's now like, I was a developer for a long time. I love doing abstraction. Developers love doing abstraction because it's cool. I can start to create a library, and maybe I will reuse, and my teams will reuse everywhere. Vast majority of the time, your library is used twice in the whole lifecycle of a system. Sorry about that, but that's the reality. I learned when I became older, I cannot say wiser, that this stuff doesn't pay off.有些指标或经验法则,帮你判断代码要不要放一起。比如代码变更频率:多久改一次?一个系统里有十种支付方式,九种很少改。建好SDK,一年改一两次。有一种经常改,那就拆成两个微前端,不是十个。因为实际上一开始粗粒度更容易。需要了再拆。我做了很久开发,喜欢抽象。开发者都喜欢抽象,觉得酷。建个库,以为能重用。其实大部分库在系统生命周期里只用两次。抱歉,现实就是这样。年纪大了才明白,这道理不划算。
The other thing you need to think about, and I want to give you an example. This is how I was thinking about my system. I was working in a company called DAZN that is a live sport streaming platform like Netflix, but doing live sport. These are mainly the main parts that our single-page application was composed with. When I started to look into traffic and how our users were using our system, I discovered that the landing page had millions of requests. Then there was a drop because people weren't convinced about the offering, whatever it is. Then I started to think, so landing page could be a micro-frontend. Then I started to say, so then the user goes in, sign in, sign up, because if he goes to the landing page, it means that the user is not signed in, or is not signed up to the service.再说一个例子。以前在DAZN工作,一个直播体育平台,像Netflix但做直播。单页应用主要由这些部分构成。看流量和用户行为,发现着陆页请求几百万。然后掉下来,因为用户没被说服。就想,着陆页可以单独成一个微前端。用户进来,要登录注册。因为到着陆页的用户,还没登录或注册。
Therefore, I can take the onboarding experience together, another micro-frontend. Then I have the catalog and video player that those together can be another micro-frontend. Then I have my account that is an authenticated area, and therefore I want to have an independent one also because we were mixing and matching different domains. Finally, we have Help and FAQ, another micro-frontend. This was our system. Very simple. We started very simple. Then we realized that there was some complexity on the onboarding micro-frontend, sign in, sign up, so we split it up. That was after we started to have friction inside the teams. That's the important thing. Don't do premature abstractions here. Try to be very simple in what you do. In this case, then we started to divide into team A, team B, team C, team D. That was how we're splitting and assigning micro-frontends.所以把引导体验放一起,另一个微前端。然后目录和播放器,又一个微前端。账户是认证区域,想独立,因为牵涉不同领域。最后帮助和FAQ,又一个微前端。这就是当时的系统。非常简单。开始很简单。后来发现引导微前端有复杂性,登录注册又拆开。那是团队内部有摩擦之后。重要的事:不要过早抽象。尽量简单。然后分成团队A、B、C、D。这样分配微前端。
Step 4: Go End-to-End with Your First Micro-Frontend第四步:用第一个微前端走通端到端。
Now we arrive at the cool part that is going end-to-end with the first micro-frontend. That's a suggestion and recommendation that I do, not only for micro-frontends, also following the serverless customers worldwide. Also, with serverless, if you are migrating, whatever it is, go end-to-end with one chunk of the system. It's a single API, it's a single micro-frontend, but go end-to-end. Why that? Because you are going to touch a lot of things, a lot of work that you will reuse later on. First of all, you start with application shell. A few heuristics. It has to be technology agnostic. Very often I've seen customers say, we decided to go with Angular, also the application shell is Angular. The problem is that you immediately start to have some challenges six months later because Angular is changing the version and now you need to maintain a different version of Angular inside your micro-frontend system.现在到有趣的部分,用第一个微前端走通端到端。这是给微前端的建议,也给全球的serverless客户。迁移时,端到端走通一个系统块。一个API,一个微前端,但要端到端。为什么?因为你会碰到很多东西,很多工作后续能复用。首先从应用壳开始。一些经验法则:应用壳必须技术无关。常见客户说,我们决定用Angular,壳也用Angular。六个月后Angular版本一换,就要维护多个版本。
Doable, but at the beginning start simple. You have to think about how you route between the old system, legacy system, and micro-frontends. How you are going to compose micro-frontends is horizontal, vertical, is client-side rendering, is server-side rendering. Usually, the application shell should be stable very quickly. My rule of thumb here, or heuristic, is if you are capable of creating an application shell that doesn't change very often after one year, you are in a good way to design the application shell. Because this doesn't do much. It's very agnostic. It doesn't have any domain-specific things. It's just literally loading and routing micro-frontends in the best way. Obviously, sometimes you need to expose an initial configuration to everyone so the application shell is the best place to do these kinds of things.可行,但开始要简单。要考虑旧系统和微前端之间怎么路由。怎么组合微前端:水平、垂直、客户端渲染、服务端渲染。通常应用壳很快稳定。经验是:如果一年后应用壳不太改,那设计得好。因为壳不做太多事,很通用,没有领域特有的东西。只是加载和路由微前端。有时要给所有人都暴露初始配置,应用壳是合适的地方。
Now, you start with the first micro-frontend. You identify, in the previous example, the onboarding experience, for instance, or the catalog. Then you start to go end-to-end. You design. You develop. You test. You deploy. You observe. You go end-to-end. Because when you do this, you touch everything. Think about this. It's a tiny portion, but you are touching basically the design part. You start to think about how micro-frontends should be tested against others in the future, how to deploy the automation pipeline that at the beginning will be simple. You don't have to have all the bells and whistles that you think about, you just need to have something that works and ship it faster. Then you need also to observe. That is one part that usually frontend developers are not very good at, because they didn't have the need before to observe how a system is deployed in production, if it fails, how it looks like.现在,先做第一个微前端。上一个例子里,有新手引导、有目录。然后从头到尾走一遍。设计、开发、测试、部署、观察。从头到尾。因为这样做,每一层都会碰到。虽然很小一块,但设计上的东西差不多都接触到了。开始想以后微前端之间怎么测试、自动化流水线怎么部署,一开始简单就行。不需要那些花哨的东西,能跑起来、发得更快就行。还要观察。这一块前端通常不擅长,因为以前不需要观察系统上线后是什么样子、失败了又是什么样子。
If you have a horizontal split, this stuff becomes very complicated. My recommendation in this case is start to develop all the different horizontal micro-frontends that you need to build and ship them all together inside the same view. Because otherwise, doing a rollback on a system that has a mix of the old world and a mix of the new world is going to be a nightmare. The amount of code that you are writing and complexity in understanding how the things are working inside the system will be magnified tremendously, because one thing is, I have two micro-frontends and that's it. Fast forward one year from now, you probably have 20, 30, 50, 100 micro-frontends alongside the old system, and removing that complexity is very difficult. Therefore, try to think about how to route these things nicely. Because your problem is this one. You have a legacy monolith that has to move into the new catalog.水平拆分的话,事情就复杂了。我的建议是,把需要做的水平微前端都开发出来,一起放到同一个视图里。否则,新旧混在一起,回滚就是噩梦。代码量和复杂度成倍增加。现在两个微前端,一年以后可能变成20、30、50、100个,和旧系统混在一起,去掉复杂度非常难。所以要想好路由。问题是:旧单体要迁到新目录。
One-hundred percent of the traffic here, so how can we move? We want to have an iterative migration. I want first classic Strangler Fig pattern. You want to have a legacy monolith that's living alongside my other micro-frontends. The best way I've found so far is leveraging edge compute. We know, as we have seen before, there's always a CDN in every design that we have experienced. Therefore, what you can do is leverage edge compute and say, everything that is not new catalog, in this case, will be in the legacy monolith. If I found the request for a catalog, I will redirect to the new catalog. Because at the end of the day, either server-side render or client-side render, you need to pass through there. You don't add any additional specific latency. It will be pretty straightforward. Therefore, you can start to do quite cool things. Because you can also say, let me add a bit of canary releases on the system.全部流量都在这里,怎么迁移?迭代迁移。经典的Strangler Fig模式。旧单体和新微前端共存。我目前找到的最好办法是借助边缘计算。每个设计都有CDN。用边缘计算,不是新目录的请求都走旧单体。发现是目录请求,就重定向到新目录。不管是服务端渲染还是客户端渲染,都经过这里。不会增加额外延迟。很简单。还可以做金丝雀发布。
I start to say, now I have a new version of the catalog but I don't want to impact all the users. I want that only 10% of the users, maybe only in a specific country, maybe in a specific browser, will have that specific thing, this new version. All the others can go in the legacy monolith or can go in a previous version of micro-frontend that is more stable. These things are something that you can do very easily. The other thing is rollback strategy. If you design properly the edge compute part, that is basically having a JSON file that is externalized or a service that provides the information on how to move from the new system to the old system, the rollback is changing one configuration. You don't have to deploy anything. You don't have to write code that will be thrown away six months later.现在新版本的目录只让10%的用户看到,也许还限制国家或浏览器。其他人走旧单体或旧版本的微前端。这些很容易实现。回滚策略也一样。边缘计算部分设计好,用一个外部JSON文件或服务来提供路由规则,回滚就改个配置,不用部署,不用写那些六个月后要删的代码。
You just literally change a configuration. It's extremely powerful. With this mechanism, my teams moved from 3 deployments per month of every single micro-frontend to 25 per day per team because now they have safe net. They have the possibility to deploy, test, and see what works, what doesn't, without affecting real traffic. The only challenge you have is how we are going to communicate between one and the other, and the answer is absolute URL. If the only change you do in the legacy monolith is just saying every time that the user clicks the button on the header of the catalog, we have to have the absolute URL, and that will basically land in a hard navigation, so full refresh of the page to the catalog. That's the only drawback that you're going to have. The rest will be sleek and very easy.只是改个配置。威力极大。用这个机制,我的团队从每月部署3次变成每天每团队25次,因为有安全网。可以部署、测试、看效果,不影响真实流量。唯一的挑战是微前端之间怎么通信。答案是绝对URL。旧单体里只改一处:用户点击目录头部的按钮时,跳转到绝对URL,硬导航,页面完全刷新。这是唯一的缺点。其他都很流畅、简单。
Step 5: Let's Move to the Second Micro-Frontend in Your Platform第五步:迁移到平台中的第二个微前端
Now, step five. We have deployed the first micro-frontend. We have the mechanism for routing. We have a lot of stuff already built. Now we have to build a few other things. We build the second micro-frontend, so it means that we need to have design consistency. You need to think about how you handle shared libraries. Ideally, not many at the beginning, or shared components, not many at the beginning. You will need to be very intentional with this stuff, but definitely you will need to have a design system because that will enable you to have UI consistency inside your system. That is the first thing that you need to think about. Then, secondly, we need to change our way to think. Duplication is not evil. As Sandi Metz says, duplication is far cheaper than the wrong abstraction. I'll give you an example. My previous company, we have four or five micro-frontends, whatever it was, and there we decided intentionally to not abstract the header because I did the research inside our GitHub.第五步。第一个微前端已经部署,路由机制也好了。现在要做几个其他事情。建第二个微前端,需要设计一致性。考虑共享库,一开始尽量少。共享组件也少。要有设计系统以保证UI一致。其次,思想要转变:重复不是坏事。Sandi Metz说,重复比错误的抽象便宜得多。举个例子,前公司有四五个微前端,我们故意不抽象头部,因为在GitHub上查过。
We changed the header once in three years. Why do we need to abstract that? If the effort for changing a font for my developers, even if there are five teams, will be probably matters of one hour max, the effort will be creating five tickets. If instead I need to abstract something, I need to create governance around that. I need to understand if something has a specific edge case, they need to understand the edge case of that specific domain. There is way more associated to a simple, let's abstract that because it's available five times. I trust my developers to create a pixel-perfect implementation of the header, because if we are not capable, probably we hired the wrong developers.头部三年才改一次,何必抽象?改个字体,五个团队最多一小时,开五个工单。要是抽象了,还要搞治理、理解各个领域的边缘情况,复杂度高得多。信任开发者能做出像素完美的头部,否则就是招错了人。
Also, this one is something that happens very often. There are some teams that started to create optimization on a multi-framework approach. It's called micro-frontends anarchy. It came from the Thoughtworks Tech Radar from 2017. The idea is, yes, there is a period of time during a migration that is fine that you have same framework, older version and newer version living together, or multiple frameworks, but that is a temporary state. You don't optimize for that because you are bringing philosophies inside your system. The way Vue.js is rendering or Solid.js is rendering Angular, there are different stacks, there are different CI/CD, there are way more than a simple framework that you're bringing inside the company. There are different ways to evolve that are independent from each other, so you don't want to handle that because it's very complicated. At some point, you need to do sharing of data between views.这也是常见情况。有些团队开始做多框架优化,叫微前端乱象,来自ThoughtWorks Technology Radar 2017。迁移期间,新版旧版框架共存或多框架共存是暂时的,别为此优化,因为引入了不同哲学。Vue.js和Solid.js渲染方式不同,CI/CD不同,不只是框架不同。不要处理这种复杂。最终需要视图间共享数据。
Therefore, here comes a few techniques. Usually, when you have something that is persistent, you want to use cookies, you want to use local session storage, or ideally, some backend API. Imagine that you have a personalization for a user. You can potentially, instead of squeezing everything in a cookie that is 100 kilobytes, you're going to have a backend API that stores that thing, understand who the user is and provide the information that's stored. If you have ephemeral data, you can use query strings. Query string is available in JavaScript from anywhere. Every micro-frontend by convention can say, ok, so I need the product ID. I pass inside the query string and they will retrieve from the query string this product ID. You don't have to store it anywhere because it's ephemeral. The user can move and go in another product in a second and everything will work very nice. Plus, it's helpful when you have a deep link.于是有些技术。持久性数据用cookie、localStorage或者后端API。比如用户个性化,不要塞进100KB的cookie,用后端API存。临时数据用查询字符串,JavaScript到处都能读到。每个微前端约定传product ID,用查询字符串取,不用存储。用户切换到另一个产品也方便。还有助于深度链接。
Then you can think about horizontal split communication. We said before that we have custom events, event emitter, reactive streams. Why not a global state? Because the complexity is that if you change an object and transform it into a number, then you need to retest everything. There is a lot of complexity inherited when you have a global state. That is something that you don't want to do. I have a customer trying to do that and they spent four months moving away from this approach. Four months without shipping anything, just refactoring this stuff. We need to remember that what we want is encapsulation instead. This is not related to micro-frontends, but what we want is methods and data that are syncing together. We have events that are coming in and out from a specific micro-frontend because we want to remove unnecessary complexity. If you split it, the state management data with methods, then it becomes very complex.然后考虑水平拆分通信。之前说过自定义事件、事件发射器、响应式流。为什么不用全局状态?因为把一个对象改成数字就要重新测试所有东西。全局状态继承了很多复杂度。有个客户花了四个月才摆脱这个方式,四个月没交付任何东西,只重构。我们要的是封装,是方法和数据一起同步。数据状态和方法分离,就会复杂。
The other thing is you can solve the problem of having multiple micro-frontends, not only having multiple micro-frontends, but delegating to a team that's generating the BFF. Example, my account that I described before. You have payment information. You have user information. You have settings. Those three are three domains that are living in different parts of the company and in different subdomains of my system, therefore, I can create a BFF. Those are fetching the information that is needed, and yet there is a single team handling the view of this complex system instead of having three teams that need to coordinate and create governance and complexity around for doing this. You can also think about this approach. AuthN or AuthZ, it's very easy to think about. Majority of the time, you can have it centralized in the application shell. The application shell takes care of the authentication, authorization part.还可以用BFF。比如前面说的“我的账户”,有支付信息、用户信息、设置,这三个领域分布在公司不同部分和系统子域。创建一个BFF,由单个团队处理这个复杂系统的视图,而不是三个团队协调治理。还有就是认证授权。大部分情况可以集中在应用shell里,微前端不该处理。金融行业有时会分散授权。最后考虑全局路由和本地路由。一级URL变化是全局路由,加载新微前端。微前端内部可以有本地路由,比如目录里有家具,家具进产品详情,这是本地路由,由微前端自己处理。是软导航,不重载,垂直切片下在同一个系统里。
Micro-frontends shouldn't. There are certain cases, especially in finance, that I've seen that they decentralized that and they wanted to have every micro-frontend to check the authorization easily. That's quite common. The last thing is to think about global versus local routing. Usually, when you move between first-level URL, this is a global routing. This is basically, you are loading a new micro-frontend. Inside that micro-frontend, you can have local routing. In catalog, I have furnitures, and furnitures can move into the product details, for instance. This is local routing, that should live inside the same micro-frontend. What does it mean? It means that you delegate, basically, this approach to the micro-frontend itself, when it's loaded inside the future, it will take care of that. It's soft navigation. It's not hard navigation anymore like before. It doesn't have to reload. It's living inside the same system if you work with a vertical split.微前端不应该。但某些金融案例中他们分散了授权。最后考虑全局路由和本地路由。通常一级URL变化是全局路由,加载新微前端。内部可以有本地路由。目录里家具进入产品详情是本地路由,由微前端自己处理。软导航,不用重载,垂直切片下在同一个系统里。
There is an implementation I have done and I share it here. You can find a GitHub repository with an e-commerce that contains the code of all the things that I describe here. It's updated regularly. I use that because I just finished my second edition of the book on micro-frontends. For this one, if you are interested, check it out because it's an implementation with Module Federation. Remember, build and iterate. That is the most important part.我有个实现,分享在这里。GitHub仓库有一个电商示例,包含所有我描述的代码。定期更新。我刚完成第二版微前端书籍,所以用它。如果感兴趣,可以看看,是基于Module Federation的实现。记住,构建并迭代,这是最重要的。
Step 6: Can AI Help Us?第六步:AI能帮助我们吗?
Can AI help us? That's the real question. Yes, but not on everything. Defining boundaries, difficult. There is some context that is inherited inside your company. It's not written in Jira or it's not written inside any other internal tool that you're using. Therefore, you need to be very explicit on how to think about that. Usually, boundaries is better as a human activity. You define through event storming or domain story mapping or other techniques, to define what are your boundaries. Dependency management definitely is something that you can do with agentic AI. There are plenty of tools that currently can help you to write even a Dependabot script or better configuration for updating your dependencies. That is one of the problems with micro-frontends. Adopt fitness functions. It is a way for checking the integrity of your architecture. It's something that you can do very easily with GenAI. I've created quite a few of them.AI能帮我们吗?能,但不是所有方面。定义边界很难。有些上下文继承自公司,没写在Jira或其他工具里,需要明确。边界最好由人通过事件风暴或领域故事映射来定义。依赖管理可以用代理AI。有很多工具可以写Dependabot脚本或更好的配置。这是微前端的一个问题。使用适应度函数(fitness functions)检查架构完整性。可以用GenAI轻松做到。我创建了不少。
Let me show you a few examples. Budget size. I want to make sure that a micro-frontend is not bigger than 30 kilobytes because probably someone forgets about configuring tree shaking or having too large bundles to ship. These kinds of things can easily be handled by GenAI. Second thing is testing your architecture. There is a library called ts-arch that is a porting of ArchUnit in Java. Basically, what it does is say, I want that this folder doesn't have any other dependency, or I want that this micro-frontend doesn't take or import this super useful monorepo, other libraries from outside the shared folder. Stuff like that can be enforced without you discovering six months later when everything is done.举几个例子。包大小:确保微前端不超过30KB,防止有人忘记配置tree shaking或打包过大。GenAI可以处理。第二,测试架构。有个库叫ts-arch,是ArchUnit的移植。可以规定某个文件夹不能有其他依赖,或某个微前端不能引入共享文件夹外的monorepo库。这样不用等六个月后发现。
Step 5.1: AI Can't Do Everything第5.1步:AI不能做所有事
AI can't do everything. There is some stuff that I saw that AI cannot do properly, that is libraries incompatibility. Sometimes you find yourself in a situation that the specific library does clash with another version of another library. For me, it was React Router and Module Federation. If you're using a specific version of React Router and Module Federation, it doesn't work. It triggers an error. AI started to create abstraction on top of abstraction. What was the solution in the end? Just downgrading the version of React Router and everything worked. No problem at all. In the example I shared before, you can see that implementation. This is something that AI wasn't able to handle. I've noticed that it's great on co-located code and less so on modularization. You can provide some instructions, but if you have a session of six hours like I usually do when I write code with GenAI, at some point we will lose context.AI不能做所有事。有些事AI做不好,比如库不兼容。有时特定版本的库冲突,比如React Router和Module Federation。AI会不断加抽象。最终方案只是降级React Router版本,就正常了。在自己共享的例子里可以看到实现。AI处理不了这个。AI在共置代码上表现好,模块化就差些。可以给指令,但六小时的编码会话中,AI会丢失上下文。
It becomes very complicated. Therefore, usually what I do is just co-locate the code first and then I modularize in the way that I want, explicitly saying what I want to achieve. Be mindful with your cognitive load because AI is going not only to create a lot of code but also to delete a lot of code. The things that you were optimizing before if you're not careful might harm your cognitive load, and you start to think, yes, implementing that way, no, sorry, AI removed that. A model is only as smart as the context we give it. Clarity of intention changes everything. Be explicit when you use GenAI on what you want to achieve, and try to do it iteratively. Start small. Plan first. You can ask to your code assistant, I need to do this. How would you do that? That's the real question. Then you iterate on the plan, and finally you start to implement the code.变得复杂。所以我通常先把代码共置,再按需模块化,明确说出目标。注意认知负荷,AI不仅创建很多代码,也删除很多代码。不留意的话,之前的优化可能被破坏。模型只智能于我们给的上下文。意图清晰改变一切。用GenAI时要明确目标,迭代进行。从小开始,先规划。可以问代码助手:我要做这个,你怎么做?然后迭代计划,最后实现代码。
What's Next?接下来是什么?
What's next? There are plenty of other topics that have to be taken into account when you are designing micro-frontends, but I hope that this was a nice glance into how you migrate in multiple steps. I've seen this approach very successful with multiple companies. The other thing is, remember that in a distributed system you need to grow your software, not building it. That's very important. Because when you grow your software, your mindset is that you need to nurture the software. You need to check and recheck why certain decisions were made six months in the past. While if you're building your brain starts to switch off, I've already done this part let's walk away, but your business in the meantime evolved. Your needs from the users evolved, and therefore it's important that you think about that.接下来是什么?设计微前端还有很多要考虑,但希望这次对怎么逐步迁移有所帮助。这个方法在多个公司都很成功。记住,在分布式系统中要“培育”软件,而不是“建造”。培育意味着持续关注之前决策的原因,因为业务和用户需求在演变。
Resources资源
If you're interested in knowing more about micro-frontends, I have a free newsletter. You can subscribe if you want. It's something that I do every two weeks. I have a couple of newsletters, this one is one, the other one is called, Dear Architects. It's weekly. It's my Saturday night, usually. I have a lot of fun sharing what's happening in the community. I have the privilege to know many of the people that are building these frameworks for micro-frontends, so I usually share a few stuff there.如果想了解更多微前端,我有免费的通讯,每两周一期。还有一个叫Dear Architects,每周六晚上。分享社区动态,认识很多框架开发者,所以常分享东西。
See more presentations with transcripts查看更多带文字稿的演讲
/presentations/migration-micro-frontend/en/slides/Luca-1784032498188.jpg)

/sponsorship/rsc/4af8f505-0249-48af-85d0-9fe26f15ad12/cover/AkkaGartner-1752831577847.jpg)
/sponsorship/rsc/b865e48f-3a48-4006-b8fb-2acffef5cf19/cover/DatadogWebinarAug6-RSC-1782479694923.jpg)
/sponsorship/rsc/521c6b76-1144-41d1-95c5-3897292ddeb3/cover/Ad_InfoQ_Q2_The-Case-for-Real-Time-Threat_750x960-1775219624717.jpg)
/sponsorship/rsc/531d8edd-4f74-486b-aaca-10058c609c1c/cover/AkkaODwebinarAI-1752830288364.jpg)
/sponsorship/rsc/62329d1e-8b4f-49df-9c9b-27c0ebb9a802/cover/Datadog-TheObservabilityMigrationPlaybook-1783687400402.jpg)
/sponsorship/rsc/c70a9466-d0c1-45e2-8d1c-658ddb1dd39c/cover/HarnessWebinarJuly16-RSC-1780666655285.jpg)
/filters:no_upscale()/sponsorship/topic/d60483d5-683c-43bc-a560-7a70de67bd33/HarnessWebinarJuly16-RSB-1780677692800.png)
/sponsorship/rsc/dbfb37c5-a393-4740-85e7-8497be3ac7c0/cover/Ad_InfoQ_Q2_The-Rise-of-Client-Side-Risk_750x960-1775219700934.jpg)
/sponsorship/rsc/a5e413c2-9a1c-4aad-8fd1-0730c6dd373c/cover/Eon-HowAnAIAgentDeletedProductionDataAndItsBackupsAtACompany-1777532882841.jpg)
/sponsorship/rsc/41d5d78f-309b-4905-b02c-45b304163e96/cover/NeubirdHIddenWaste-1781078131207.jpg)
/sponsorship/rsc/a5172f09-522c-40ae-b263-4a0857983661/cover/WaveMakerRSC-2-1778079595517.jpg)
/sponsorship/rsc/b3d8361c-cee6-4be3-a69e-2ae972fac8d1/cover/TacnodeWebinarAugust27-RSC-1783696807891.jpg)