从微前端到微后端,不可思议的前端架构思考
背景
微服务的概念已经过去了好久,微前端也已经实践了一段时间,在去年不同的分享会上都有听到各家公司关于微前端的实践,总体来说,微前端是因为前端架构的不断演进,结合后端微服务的理念而创造出来的,用于解决不同前端框架,甚至相同框架的不同版本,如何结合的问题。
微前端是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。各个前端应用还可以独立运行、独立开发、独立部署。
之前和前端框架架构的同学也聊了一下,一直思考的是,微前端目前如何落地,能解决什么问题,似乎目前这个场景更多的是解决历史系统的迁移问题,在新系统上,前端,特别是在一个统一的体系中,很少会出现跨多语言,多架构的场景。更多的是直接重构掉,做一个新的。
既然前端能微前端,后端能不能微呢?微服务在某些场景下,解决了不同语言的互调问题,相对带来的,其实还有配套的管理和调度系统,而前面说到,在单一语言领域内,用户可能更熟悉开发一个大系统,这就造成了系统越来越大,越来越复杂的情况。
在类似阿里的体系中,前端写的 Node.js 系统非常多,特别是中后台系统,搭配管理端和前台,业务可以很复杂,前端可以拆分,而后端基本上都是一个,要微服务化,或者 FaaS 化,总有道不明的风险,这里的风险更多的是人员成本,人们总是说“明明跑的好好的,为什么要升级”。
![](https://filescdn.proginn.com/c76678c6c6e1e1b7264ac68276df7351/1cd2fddf4ba089f80b0b5d353eb4c83d.webp)
这么说我们并不是想开历史的倒车,让熟悉传统开发的用户一步到服务化,甚至 Serverless 还是有一些困难的,特别是面对 http 的传统应用,路由的思维以及方便的开发环境已经非常成熟,不管是开发体验或者底层概念上都很难转变。
我们设想过很多种方法让应用变轻,变薄,变得可维护,可扩展。我们在 Midway 中尝试使用了IoC 的方式进行解耦,使用装饰器和注入替换传统的实例化和调用。
![](https://filescdn.proginn.com/bba233269ce792fbea8edf59d97250c0/6da294b0bf584a458258416288637157.webp)
但是这样并没有把应用拆小,应用还是原来的应用,逻辑还在。
传统的分布式调用是把系统拆成很多服务,但是这样做在没有成熟的配套前会牺牲可维护性,排查问题也会变得困难,同时资源的开销也会难以评估。
那么,是否有办法既方便的拆分应用,又不影响原本的开发调试,资源评估,甚至是可维护性呢?
答案可能是有的。
我们一向擅长用简单的方式解决复杂的问题,但这次有几个问题。
1、本地开发的问题,不能影响开发
2、部署的问题,尽量减少成本消耗
3、我要拆得简单,容易理解和维护
4、面向未来扩展,可迁移到微服务乃至 FaaS 场景
面对这么多问题,我们只能一一来思考和解决。
如何拆分
恰好在去年的 jsconf 2019 上,我们提出过一种把应用变为函数的拆分方法,把路由层(Router,Controller)单单独拆分,同时将,业务逻辑(Service)垂直拆分的设想。
有谁还记得去年的 jsconf 2019 的拆分模型呢。
这种拆分方式将应用分为两类(Controller + Service),同时又将业务本身按照领域进行了划分。假如在这个基础上,我们把路由也进行领域(垂直划分),是非常自然的,而原始目录结构中的 controller
目录和 service
目录也很好的印证了这个想法。
第二个问题是,拆分了之后,代码的组织方式,开发模式有什么变化呢?
开发方式的变化
用户是非常懒和挑剔的,任何增加成本减少收益的事情都是不会干的。如果调整了代码组织方式,并且还要修改代码,甚至调整原来习惯的开发方式的话,都会骂娘的。
代码分离之后,管理方式还好说,现在可以用 git 分仓库管理或者用类似 lerna 的工具来同仓库管理。但是开发方式是实打实的体验,如何在尽可能不修改的情况下来拆分逻辑,增加扩展性呢?
这就需要框架加载方式的支持了。
我们以一个 midway 项目举例,使用了 lerna 管理 monorepo,模拟拆分的情况。
我们简化一下示例目录结构,大致如下。
![](https://filescdn.proginn.com/2309289346bf2e7602b993f127b7628d/6af12e83e65f418679cbd6b6baff13e6.webp)
main
为主 app,而 api
、book
、video
都是对应的领域模块(子应用),包含对应领域的能力,比如提供 API,图书服务以及影视服务。
可以看到,我们的子包的目录结构和原本的大应用是相同的,而文件也就是原来领域抽象的文件(原来的代码写的好的话)。
当然,这样拆了之后,肯定不能运行。启动就会报错,找不到子包的文件,我们还需要对框架做一点点调整。
熟悉 Midway 框架的同学可能知道,IoC 是通过扫描目录文件,预先加载到内存中即可拿到实例,不过这个目录结构下,也是无法扫描到的。幸好我们的 IoC 容器支持传入自定义扫描路径,那么只需要做一点点小小的修改,让 IoC 容器能拿到子包的路径就行。
在 monorepo 下,子包也是一般的 npm 包,我们只要让用户定义就行了,比如有一个名叫 configuration.ts
的文件,内容大概是这样。
![](https://filescdn.proginn.com/4a2c921b4fc0cb207592dcc544733410/06e525a513a1f2432ad50297750cba36.webp)
哇,这下我们只需要在框架里找到子包(lerna 下 resolve 能找到)的地址,加到扫描路径里就行了。按照原来的依赖,我们对相应子包(api)也增加依赖,用于提供 Restful API 服务。
![](https://filescdn.proginn.com/9fc01bbb387a633d65d71897b2d88587/d102fee42c5799772f131c29208bbb46.webp)
这下,我们的 main
以及 api
包都能独立开发,独立工作了,而且和原来的开发模式完全保持一致。
使用的时候,是通过导入 npm 包的形式来进行协作,使用的也是标准的 import
语法,以及 class
形式,没有增加特别的语义,无需去额外学习。
这就是梦想中的模式,我们似乎可以叫他“微后端”。
部署模型
开发模型上,我们把代码变成了子模块,通过 IoC 扫描的方式让代码的开发模式和以前完全一致,那部署呢?
由于主包没有什么太大的变化,发布构建的时候只要单独发布 main
包即可,这种方式特别适合 CRM 后台系统,多路由组合的情况。
最后
得益于 IoC 体系,我们能向其他场景去尝试输出这些能力。在 2019 年底我们开放了 midway-faas 体系的代码,这就出现了一些从 midway 代码转变为 midway-faas 或者复用的需求,那么,我们是不是能够将一部分 midway(service)代码,直接拷贝成一个新的模块(子包)的形式,让 midway-faas 的能够运行这些代码呢?
这其实是个 feature 预告,下一版本 midway,以及 midway-faas 已经支持了这种扩展模型,你可以随时的去扩展自己的可复用能力,也可以拆分自己的应用,构建垂直化领域模型代码,甚至是从 midway 模块迁移到 midway-faas 模块。
技术永无止境,一切皆有可能,尽在 midway。
最后
如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:
点个「在看」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)
欢迎加我微信「qianyu443033099」拉你进技术群,长期交流学习...
关注公众号「前端下午茶」,持续为你推送精选好文,也可以加我为好友,随时聊骚。
![](https://filescdn.proginn.com/5de4d4336264b460aaf63f9957d77ec7/a79231ff937fae339df8639cb548fdcf.webp)