【微前端架构】微前端——功能团队中缺失的一块拼图

在任何合法的前端开发团队中,提高可扩展性和敏捷性很少会成为头等大事。在处理大型、复杂的产品时,如何确保快速、频繁地交付同时包含后端和前端的功能?像后端那样将前端单体分解成许多更小的部分似乎是答案。如果执行得当,微前端可以提高团队的有效性和效率。就是这样。

微前端背后的想法是将网站或 Web 应用程序视为由独立团队拥有的功能的组合。每个团队都有自己关心和擅长的不同业务领域或任务。团队是跨职能的,从数据库到用户界面,端到端地开发其功能。

将较大的问题分解为较小的问题以提高敏捷性、可重用性和可扩展性一直是 IT 的圣杯之一,过去二十年来该领域取得的进展令人震惊。但是,使用越来越大的代码库的新限制仍在不断出现。业内最有头脑的人一直在努力应对这一挑战,试图从技术和组织的角度来解决这个问题。迄今为止,微服务架构肯定仍然最接近解决它。

在本文中,您将学习:

  • 微服务架构和微前端如何运作;

  • 他们最大的优势是什么;

  • 实施微前端时必须满足哪些要求;

  • 您可以使用哪些技术或方法;

  • 以及这些解决方案中的每一个如何相互比较。

微服务架构和微前端——它们解决了什么以及它们如何运作


微服务架构承诺:

  • 更容易构建和维护应用程序

  • 提高团队生产力

  • 使用技术的灵活性

  • 可扩展性

解决方案是将大型系统拆分为围绕明确定义的业务能力组织的细粒度、松散耦合的服务。然后,每个服务都足够小,可以由一个开发团队开发,并且可以快速调整以适应新的业务需求并进行部署,而不会破坏系统的其他部分。

这种方法的危险在于系统将被分解成许多不相关的应用程序。尽管这对开发人员来说很好处理,但这并不是用户对系统的期望;大多数人不喜欢使用大量的小型应用程序来完成他们的工作。因此,必须将为此过程分解的内容重新组合到用户界面中。

将碎片重新组合在一起


常见的方法是在用户和微服务之间构建一个单一的前端层。对于每一个重要的系统,庞大的前端代码库都会威胁到微服务架构的好处。这就是微前端的用武之地。

微前端的基本思想非常简单——将用户界面由多个围绕明确定义的业务能力组织起来的细粒度部分组成。然后每个部分都足够小,可以由一个垂直结构的团队开发和拥有。拥有前端和后端的团队建立了一个真正自给自足的功能团队。

【微前端架构】微前端——功能团队中缺失的一块拼图_第1张图片

然而不幸的是,微前端带来了同样的挑战,使得微服务难以实现。此外,任何捷径都可能以负面的方式影响用户体验。必须确保一致性、安全性、互操作性、性能、可扩展性和所有权,以确保无缝的用户界面。

尽管不同的微前端方法解决了各种问题,但还没有一个能够涵盖所有这些问题。但在我们深入研究这些不同方法的细节之前,让我们先仔细看看选择微前端的主要动机,以及与单体方法相比的优缺点。

解释微前端的 3 大优势

微前端的优势#1:可扩展的团队设置


3 种不同的前端方法及其对团队组织的影响
选择微前端方法有很多很好的理由,但最重要的(如果不是最重要的)之一来自可扩展的团队设置。

至少可以确定影响开发团队组织的三种可能的前端架构:单片前端和后端、带有后端微服务的单片前端和微前端。下面,我们将描述每个团队组织的后果。

单片前端和后端


构建需要前端和后端的解决方案的常用方法是水平拆分项目并通过 REST API 在这些层之间进行通信。

如果系统足够小,可以供一个团队开发,最好的选择是保持架构简单。您可以通过创建单页应用程序 (SPA) 前端并通过 REST API 将其连接到后端来实现此目的。然后根据每一层所需的工作量调整您的团队设置。

良好的做法是确保从一开始您的代码就结构良好,并且当您的解决方案增长时,您可以引入另一个或两个团队,而无需重新构建它。

但是这种架构的局限性是显而易见的:团队修改代码库的次数越多,就需要更多的协调和集成,这会导致产品开发瘫痪。

【微前端架构】微前端——功能团队中缺失的一块拼图_第2张图片

带有后端微服务的单片前端


尤其是在大型企业中,对额外开发团队的需求通常从后端开始。大多数系统需要大量的业务逻辑。例如,它们需要在浏览器端无法完成的处理,或者与遗留系统的复杂集成。在这些情况下,一个跨职能团队已经不够了。

大多数公司扩展其架构的第一步是垂直拆分后端代码库以解决复杂性。前端被分配给一个专门的前端团队,而其余的工作则分配给各个后端团队。因此,积压的项目被分解成块,并由不同的团队交付。然后,这些团队要么通过合同谈判解决依赖关系,要么——以更敏捷的方法——通过大量积压计划来解决依赖关系。

【微前端架构】微前端——功能团队中缺失的一块拼图_第3张图片

这种方法带来的最大挑战是管理这些依赖关系和同步发布。测试也变得很麻烦,因为您需要等待所有单独的部分到达测试环境才能验证它。

当解决方案必须嵌入来自不同产品 backlog 的功能时,它变得更加复杂(这种情况经常发生)。

【微前端架构】微前端——功能团队中缺失的一块拼图_第4张图片

在更大的范围内,这种方法需要大量的管理。日常部署几乎是不可能的。这就是为什么在具有复杂前端的大型企业中工作的开发人员和架构师寻求最终垂直扩展的解决方案,将前端添加到他们已经改变游戏规则的微服务架构 - 微前端。

微前端


为了快速开发、测试和发布其功能,团队需要能够在不依赖其他团队的情况下工作。微前端可以在用户界面领域实现后端微服务的相同承诺,并且可以应用支持独立团队合作的相同原则。

在此设置中,前端和后端这两个领域紧密耦合,因为需求来自一个产品待办列表。再一次,一个团队可以在一个简单的架构中交付整个功能。如果执行得当,这不会影响用户体验。

【微前端架构】微前端——功能团队中缺失的一块拼图_第5张图片

为了很好地执行它,微前端带来了许多后端微服务已知的类似问题,必须解决。否则,用户可能仍将系统感知或体验为不同特征的拼凑。

微前端的优势#2:技术选择自由


除了创建可扩展且独立的团队设置外,微前端方法还有助于处理应用于前端的大量技术。每个季度都有关于如何开发面向用户的系统部分的新想法。很多时候,新版本的框架甚至不向后兼容,这意味着每个版本实际上都是一个单独的框架。

【微前端架构】微前端——功能团队中缺失的一块拼图_第6张图片

微服务架构的优势之一是可以自由选择所使用的技术。当用户界面被拆分成独立的模块时,前端开发人员可以享有同样的自由——至少在一定程度上。

微前端的优势#3:弹性


任何系统的实际成本都不能很好地体现在代码库的初始开发成本上,而是体现在维护上。代码重构和系统重构的无休止螺旋的目的是保持与开始时相同的速度引入功能更改。

微前端架构通过引入以下约束使代码库更具弹性:

  • 隔离变更的影响;

  • 防止代码耦合;

  • 并随着时间的推移保留架构。


这些约束防止不受控制的依赖关系、限制代码重用和强制服务边界。

不再有不受控制的依赖


多年来开发的大型应用程序不可避免地充满了难以跟踪和维护的代码依赖关系。开发人员在上市时间的压力下工作,或者只是试图优化他们的工作方式,会在代码的不同部分之间产生许多不受控制的依赖关系。当引入新的依赖项时,重用一些业务逻辑、缓存数据或资源池似乎总是一个好主意。后来发生的事情是这种共享功能变化的不可预见的后果。

【微前端架构】微前端——功能团队中缺失的一块拼图_第7张图片

通过将代码库分成几个不相连的部分,很容易避免这种依赖关系。由于两者之间的自然边界,一个微前端不可能重用其他微前端的现有功能。因此,更改的任何意外副作用仅限于一个微前端。

有限的代码重用


普遍遵循的原则不要重复自己(DRY)的目的是限制代码库的大小,从而降低出错的可能性。另一方面,开发成代码的每个抽象都引入了依赖性。随着时间的推移,抽象也经常出现必须根据特定的使用上下文进行调整。当您的微前端代码库仅限于几个功能时,开发人员不太可能试图创建这样的抽象。相反,当他们找到重用代码的机会时,他们只是复制并粘贴相关的片段,这通常比引入依赖项要好得多。

服务边界执行


系统的架构通常受某些分析和设计决策的影响。然而,决定某事和遵守这些决定往往是不一样的。在服务被拆分并就职责达成一致并记录在案之后,漂移就开始了。缺乏明确的边界或跨越边界的能力可能导致并允许开发人员破坏先前商定的设计。有一些工具,例如在 CI/CD 管道中实现的代码审查或依赖检查,但使用单独的微前端,不可能偏离约定的架构。

微前端的 6 个常见要求


为了不失去微前端的任何潜在好处,这种架构的实际实现必须满足一些共同的要求。以下六点很重要,不容忽视:

独立部署

——使用微前端的最大风险是,当它们没有正确实施时,它们将需要部署协调。除了将有意义的功能封装在单个组件中并始终确保向后兼容性的良好设计之外,组件本身必须可以一个一个地部署,而无需任何协调。

热部署

——开发某些应用程序片段的团队必须能够部署新版本而不会造成任何停机。必须考虑使用滚动更新或金丝雀部署等策略。使用带有经过深思熟虑的路径系统的高级 HTTP 路由机制可以提供很大帮助。

统一的样式/组件

——将应用程序构建为不兼容的拼贴块可能会对用户体验产生破坏性影响。如何确保视觉和行为一致性的问题必须从引入微前端的一开始就解决。该解决方案通常涵盖技术(共享 UX 库)和组织方面(共享 UX 团队)。

身份验证和授权

——显然,用户必须只进行一次身份验证。授权上下文和规则必须由前端和后端的所有组件共享。

跨组件通信

——即使组件之间的通信引入了耦合并因此应该避免,但很难想象一个应用程序由完全分离的部分组成。特定的微前端必须能够共享应用程序上下文(即用户或其他资源标识)并相互通知内部状态的变化。选择的通信方法应该更喜欢基于事件或地址栏的间接通信,而不是直接使用其他组件 API。

搜索引擎优化

——这种需求的严重程度取决于具体的用例,但对于某些应用程序来说,它是要解决的第一类公民。

微前端技术——解耦前端的 3 种方法


微前端技术可以分为三类,每一种技术都有一些优点和缺点。我们可以区分:

  • 构建时集成——应用程序作为一个捆绑包生成,然后一次性交付给用户

  • 服务器端集成——在提供页面的同时集成应用程序,用户浏览器获得完整的应用程序

  • 客户端集成 – 应用程序分批交付给用户 Web 浏览器,然后集成到浏览器中

下面我们通过使用像 Strava 或 Endomondo 这样的健身追踪应用程序的示例来仔细研究这三种方法中的每一种。这些应用程序中的每一个都具有相似的特性和功能,例如显示运动员个人资料摘要、他们的最新活动、一些正在进行的挑战等的仪表板。

构建时集成


解耦前端的第一种方法是将代码库组织在独立的存储库中。通过构建时集成,每个微前端都作为独立包构建和发布。完整的应用程序导入这些包并从包含的组件组成用户界面。

这样,在组织团队和适当划分团队之间的功能上稍加努力,就可以实现合理的团队独立性。虽然它不会提供微前端的所有可能好处,例如技术多样性或部署自主性,但它的优点是它不需要任何其他工具,除了开发人员已经使用的工具。

与其他方法相比,构建时集成简化了一致性保证。这也是减少传输到用户浏览器的数据量的最简单和最有效的方法,因为整个应用程序包在构建阶段进行了优化。

【微前端架构】微前端——功能团队中缺失的一块拼图_第8张图片

在我们的示例中设计健身跟踪应用程序时需要考虑的是使用组件之间的间接通信,这将减少耦合。

服务器端集成


第二种方法的总体思路如下图所示。

【微前端架构】微前端——功能团队中缺失的一块拼图_第9张图片

浏览器对页面 (1) 的请求来自“布局服务”,该服务首先为页面布局 (2) 请求“页面模板服务”。布局包含 HTML 兼容标签,其中包含要包含的页面片段的 URL (3)。“布局服务”请求实现特定功能的所有包含部分调用服务的内容。布局服务的更高级实现并行执行查询 (4),支持故障转移和快速响应流。

在检索到所有部分 (5) 之后,准备好请求页面的全部内容并将其返回到浏览器 (6)。

布局服务在从用户角度建立应用程序的一致行为方面发挥着核心作用,但它由独立开发和部署的部分组成。

当应用程序包含由许多独立尾部组成的页面时,服务器端集成非常有用,有些是用户特定的,有些是用户之间共享的,如电子商务网站通常具有的。

有很多技术可以应用这种模式,例如服务器端包含、边缘端包含和 Zalando 的带有“片段”标签的项目 Mosaic。

服务器端包括


服务器端包含 (SSI) 是一种由 Web 服务器解释的脚本语言,用于将一个或多个文件的内容包含到网页中。语言语法基于放置在 HTML 注释中的指令,这些指令由启用 SSI 的 Web 服务器处理。

最常用的指令是“包含”,允许将一个文档包含到另一个文档中。包含的文档可以通过相对于当前文件目录的路径来引用。例如:

更强大和推荐的选项是使用虚拟参数,它可以指定任何资源,包括 CGI 脚本或远程页面:

该语言还提供变量和条件子句,以在页面上创建更多上下文感知内容。

SSI 受到流行的 Web 服务器(如 Apache 或 NGINX)的支持。

边侧包括


Edge Side Includes (ESI) 是一种用于边缘级动态 Web 内容组装的标记语言。ESI 的目标是解决 Web 基础设施扩展和内容发布的问题。与 SSI 类似,它通过 esi:include 标签提供嵌入:

条件执行命令与大量预定义变量相结合,可以在提供给用户的页面上实现真正的动态内容。


    
        
    
    
        
    
    
        
    

缓存代理服务器(如 Varnish 或 Squid)支持 ESI。

Zalando的马赛克项目


Mosaic 内置于 Zalando,是首批成熟的、有目的的、用于实现服务器端集成的微前端框架之一。

【微前端架构】微前端——功能团队中缺失的一块拼图_第10张图片

Mosaic 架构的中心点是“Tailor”,即在这种服务器端微前端架构中实现布局服务。为了在页面中包含微前端,使用了“片段”标签:



    


    
    
    

与普通的 SSI 或 ESI 标签相比,片段标签提供了额外的有用属性:

  • primary – 表示设置页面响应代码的片段

  • timeout – 可选的片段超时时间(以毫秒为单位)(默认为 3000)

  • async - 将片段推迟到正文标记的末尾

  • public – 防止 Tailor 将过滤后的请求标头从上游转发到片段

  • fallback-src – 在当前片段超时/错误的情况下回退片段的 URL

如上所述,Mosaic 旨在为微前端提供服务,并提供以下功能优势:

  • 在后端编写预渲染标记。这对 SEO 很重要,并且可以加快初始渲染。

  • 确保快速到达第一个字节。Tailor 并行请求片段并尽快将它们流式传输,而不会阻塞页面的其余部分。

  • 执行绩效预算。这在其他设置中非常具有挑战性,因为没有单一的点可以控制性能。

  • 容错:呈现有意义的输出,即使页面片段失败或超时。

如果需要更复杂的模板管理,可以简单地从文件系统或专用服务提供页面模板。

马赛克的第二部分是船长。在 Innkeeper 的陪伴下,Skipper 建立了一个先进的 HTTP 路由器,可以在需要隐藏复杂的微服务世界时使用。Skipper 本身提供了基于规则的 HTTP 请求路由,具有过滤和丰富功能。Innkeeper 用作运行时 Skipper 规则管理的 API。

服务器端集成允许从微前端轻松编写应用程序,但它不能解决需要真正丰富的前端应用程序时出现的挑战。

客户端集成


最后但并非最不重要的一点是客户端集成方法。在这里,微前端的构建是将应用程序集成到用户 Web 浏览器中。应用程序的每个部分都独立交付给浏览器,然后应用程序在呈现时被粘合。

使用这种方法,在运行时构建应用程序不需要额外的基础设施,而且它似乎是最灵活的。应用程序组件可以共享一些用户上下文,因此就像在构建时集成的那样,而不会影响微前端的其他要求。

【微前端架构】微前端——功能团队中缺失的一块拼图_第11张图片

Iframes


iframes 是一种旧的客户端集成技术,可用于将一个 HTML 文档嵌入到另一个中。在微前端的上下文中,解决方案在于使用 iframe 标记嵌入每个微前端应用程序页面布局,其中 src 属性指向为应用程序提供服务的 URL。

与这种方法中的 SSI/ESI 类似,每个微前端都可以托管在不同的地址上。与 SSI/ESI 相反,客户端浏览器负责独立下载每个片段并显示完整页面。