基于微前端的前后端结合分层设计(原理篇)

引入

什么是微前端

微前端(Micro Frontends)意指前端的微服务(Micro Service)。是一种可独立交付的前端应用程序组成大型整体的架构模式[1]。简而言之,微前端继承了微服务“拆”的原则,将前端分成多个模块,各个前端模块独立开发测试、独立部署,是实现大型前端应用的一种的架构。

背景及应用场景

当前,已然不是前端应用天然解耦的时代,由前端框架带来的前端单页应用(SPA)已经占据了主流。这些框架很好的解决了前端组件化、前后端分离、数据更新等各种各样的问题,但随着应用规模的提升,单页应用变得越来越臃肿,开发效率降低。

同时,由于单页应用普遍采用单一技术栈,技术栈的更新也成了一个问题。许多团队面对庞大的历史代码,只能望洋兴叹。

这便是微前端存在的意义:为大型应用在框架带来的便利下重新解耦,提供更多的重构、升级机会。

行业现状

参考行业现状 by 蚂蚁金服

误区

架构模式一定要与业务/产品规模相匹配。与微服务等任何一种架构模式相似,微前端不是银弹,仅适用于大规模复杂应用。

本文所介绍的基于微前端的前后端结合分层设计,结合 SPA 微前端和 MPA 的特点,指出了落地实践中的一些问题并尝试解决。

微前端设计原则及意义

优势

微前端的优势,参考Micro Frontends by Cam Jackson

简言之,有以下几点:

  1. 高度的灵活性:

    • 独立开发
    • 独立部署
    • 独立升级
  2. 团队自治:功能团队端到端负责一项功能

    e2e
    from [1]

原则

由优势可以引出微前端设计 2 条基本原则:1. 开发隔离,2. 运行时隔离。

开发隔离

  • 每个子应用在不同的 git 仓库
  • 每个子应用有不同的 package.json 和构建工具配置
  • 每个子应用都可以选择自己的工作流

部分参考single-spa 微前端概念

运行时隔离

  • 每个子应用的技术栈相互隔离,不会互相影响

“聚散真容易“

一个框架的设计要求考虑“散”的极端场景,但是“散”是有成本的,一个应用的设计应该考虑哪些东西需要“聚”。框架的设计也应该提供“聚”的能力。

spectrum by Cory House

如上图(by Cory House) 所示,一个包含多个微前端的团队应该在技术栈,api 设计,公共底层组件和 CI/CD 上达成一致,以获得更高的开发、部署效率。

除此之外,如脚手架,基座应用等也需要加以考虑。

微服务划分原则

微前端与微服务相似,在一个知识边界内划分出来的 service,效率一定是最高的by Hao Xu, Thoughtworks

微服务的划分一定是与业务相关的,在单个微前端内部的团队成员应该对整块业务(前、后、测试、设计等)有全面的认识,也应该对此微服务在应用中的位置和周边相关的业务有一定的认识。

实现方式及对比

参考实施微前端的六种方式 by phodal

微前端存在的问题

微前端存在许多问题,蚂蚁金服总结了一套微前端落地实践的解决方案[5]

解决方案

除此之外,微前端带来的问题依然存在:

  1. 产品规模较大带来的性能上的问题,子应用无论多么简单,都需要依托微前端框架
  2. 子应用自治带来的管理上的问题,
    • 子应用间的协调
    • 产品版本与子应用版本的协调
  3. 多产品系统的单一产品子应用难以复用,BFF 层与前端层的割裂

设计理念

参考微前端的设计理念 by phodal

这里,针对微前端存在的问题,有几点需要补充讨论:一是路由与通信,二是应用版本,三是依赖的引入,四是服务发现。

路由与通信

尽管在Inter-app communication single-spa提到,A good architecture is one in which microfrontends are decoupled and do not need to frequently communicate. 当许多场景下,必须要实现通信。例如以下场景(仅示意):

声音控制器

总声音可以控制其余 3 个声音,如果 3 个声音在不同的微服务内,则可以使用通信加以解决。

在一个 SPA 应用中,前端路由是必须的,但是在 MPA 或者简单应用中则不尽然。可以用通信来代替一部分路由的功能。例如以下场景:

单页应用

在这个页面中,导航栏和搜索栏是单独的微服务,开发者只需要对text部分进行开发,则不需要路由。

这里你可能要问了,对于这两个例子而言,没有必要使用微前端,是的,但这种场景在现实中的确又存在。当团队决定采用某种技术栈后,团队成员很可能倾向于想当然的使用该技术,因此需要统一知识上下文&制定规范(扯远了。

路由与通信二者皆可以以插件形式提供。

应用版本

管理应用版本可以使用 git 或持续集成所带有的 tag,版本问题主要存在于是否允许多个版本的子应用存在于同一个产品中。咋听起来可能会感觉不允许,但有以下情形:

  • 子应用灰度升级
  • api 变更,对子应用依赖的应用暂未对其适配

因此,微前端中应提供应用版本控制插件。

服务发现

当今主流的微前端框架皆是基于前端服务注册。对于更加复杂的服务,如多产品系统下的单一产品服务,在系统内是无法感知的。如果系统存在 BFF 层的话,仅仅将子应用用注册到前端势必会造成与 BFF 层的割裂,微前端与 BFF 层不是 1 对 1 的,知识边界扩大。

对于此类复杂系统,微服务框架应该提供服务发现的插件,允许后台对复杂子产品及其子应用进行统一管理。

依赖的引入

微前端框架的主代码应该尽可能的精简,将路由、通信、版本控制、服务发现等功能当作插件来处理。保留最基本的子应用标识、前端注册、生命周期功能。

前后端结合的微前端分层设计

基于以上种种,对于大型复杂系统,可以采用前后端结合的微前端分层设计。而其依托的微前端框架的设计,将由下一篇继续。

参考资源

  1. Micro Frontends by Michael Geers
  2. Micro Frontends by Cam Jackson on Martin Fowler’s website
  3. Thinking in Microfrontend by phodal
  4. sinle-spa
  5. qiankun
  6. 用微前端的方式搭建类单页应用
  7. 可能是你见过最完善的微前端解决方案
  8. Microfrontends: the good, the bad, and the ugly