本文最初发表于ITNEXT的博客站点,经原作者[Manu Ustenko授权由 InfoQ 中文站翻译分享。
在本文中,我将会讨论从创业公司到成熟公司过渡时期的可扩展性问题,分享我的想法,并介绍在我工作的组织中所使用的技术。
有很多文章都讨论多可扩展性这个话题,但是我想要澄清在本文中,可扩展性的含义:
可扩展性指的是在系统生命周期的每个阶段,前端系统都能以更快、更可靠和更简单的方式向终端用户交付价值的能力。
一个可扩展的应用应该具备很多的原则,但是我想要强调其中最重要的一条:关注点分离与架构设计。
在本文中,我将会基于团队拓扑概念进行阐述,并且会按照功能将团队分为四个组,分别是业务导向(stream-aligned)团队、赋能(enablement)团队、复杂子系统(complicated subsystem)团队和平台(platform)团队。
创业公司的时代
我们正生活在一个创业公司的时代中。每天都有新的创业公司开业或关闭,企业家们都在寻找新的机会,为我们的生活带来新的商业模式。在这个过程中,我们不断看到个人和公司的成长与失败。
从技术角度来看,在公司生命周期的这个阶段,公司中的技术规模比较小,易于维护,易于为业务带来新的价值。在这个阶段犯错并不致命,可以很容易地解决。当然,这并不意味着最初的架构不重要,企业需要非常优秀的专家,他们可以在公司的早期阶段做出正确的架构决策。在这个阶段,一个或两个开发小组(每个小组有 5 到 10 个成员)就足够了。我认为如果在这个阶段拥有更多的团队将是组织方面的错误。
在公司的这个时代,从前端的角度来看,最佳方式使用较小的单体应用。它有很多的好处:
使代码保持紧凑,易于开展工作
单体架构是很自然的选择,易于实现
持续集成和部署都很简单
巨头时代
我们不会过多地讨论 IT 巨头和成熟的公司。我们只需要知道,成熟的公司已经有了良好的业务、工作流程、行业基础并且会有多个团队专注其功能。
在这个阶段,关键要素之一就是将功能性的任务分派给专门的团队,每个团队专注自己的功能,不允许任何团队涵盖多个功能。
从创业公司向成熟公司的过渡
随着时间的推移,创业公司会达到一个沸点,前端应用开始变得不再灵活,无法与业务保持相同的速度。
在试图交付新的功能以满足业务需要时,每个交付周期都会延迟。技术团队对业务的承诺经常落空,他们还没有认识到环境已经发生了变化。团队依然认为他们能够如期交付某个新特性,但是却一次又一次地失败。
组织可以通过增加团队或工程师的数量来满足重要的需求。但令人遗憾的是,在很多情况下,将更多的人塞入到技术团队中并不会带来任何的价值。现在,应该改变流程了。
系统难以进行扩展的症状包括:
系统难以维护
系统变得更加脆弱,开发人员不知道在进行变更的时候会破坏其他功能,从而导致经常发生严重的事故
持续集成/持续部署过程变得不堪重负,工程师为了交付特性或修复缺陷不得不排队
要从一个技术栈转移到另一个技术栈时不得不对整个系统进行修改
难以保持应用处于最新状态,难以升级版本
当多个团队在同一个代码库中进行交付时,会造成代码库的混乱
在没有团队负责维护和改进的情况下,有些代码会被遗弃
团队之间没有明确的责任
存在多个职能化的团队,比如业务导向团队在做着赋能团队或平台团队的任务
在过渡期间,如果出现技术错误,其代价是非常高昂的。在这个阶段,创业阶段的错误会逐渐显露出来。
为了让业务继续保持增长,组织中的技术领导层应该调整目前的状态。流行的方案就是将单体架构迁移至微服务(后端技术)或微前端(前端技术)架构。
由于本文讨论的是前端技术,所以我会继续讨论微前端,而不是微服务。同时,我也不会讨论微前端架构是好是坏以及哪种微前端方式更好的问题,因为这方面已经有很多的文章和讨论了。
相反,我会从可扩展性的角度来考虑微前端架构,以及它是如何解除企业在转向多团队模型时所面临的障碍的。
微前端架构
多团队模型中微前端架构的收益
微前端是一种架构风格(模式),在这种风格中,独立交付的前端应用会被组合成一个更大的整体(microfrontends.com)。这是一种会影响组织的技术,会让团队之间解耦,避免出现过多的集中化,最重要的是,这会给团队授权,让他们针对自己的功能进行决策,不必依赖其他的团队。
一旦某个组织出现了我们上文所述的难以扩展的症状,工程师就可以决定将系统从单体架构转换成微前端架构。与此同时,它会给企业带来一系列的收益。
代码组织
微前端架构允许团队将单体应用的代码库分割成更小块的代码。每个独立微前端的源码规模将小得多,并聚焦于应用中一小部分的功能。应用中的哪一部分要解耦成微前端,这应该由工程团队决定。它可以是很大的内容(如页面)也可以是很小的内容(如元素)。借助新的代码组织形式,新开发人员花更少的时间就能掌握代码库,并在短时间内就开始对代码做出贡献。
团队独立性
微前端的代码库应该是隔离的,这有助于业务导向团队选择自己的工作策略和流程。每个团队对应用的一个垂直切片拥有全部的所有权,并且可以在其领域内进行专业化。他们不用担心团队之间的干扰,这减少了团队之间的协调需求。微前端能够让应用按照团队的结构进行组织,这遵循了康威定律。
设计系统的架构受制于产生这些设计的组织的沟通结构
Melvin E. Conway
具体样例:
如果某个团队想在单体架构中尝试新的技术(新的状态管理器、测试库等),这个决定需要在所有从事该应用的团队间取得一致。这很费时间,而且最终可能难以被一些团队接受。如果团队是独立的,该团队的工程师就可以决定要使用什么技术。这个决定完全不涉及其他的团队。
独立发布
与微服务的方式相同,微前端要对它的部署负责。它让业务导向团队能够独立发布应用的某一部分。此时不用重新部署整个应用,每个团队都可以只部署其中的小部分功能。这能够让团队在发布的时候,不需要与其他团队协调,也不需要适应全公司的发布周期。如果最后的部署出现问题,能够很容易地回滚而不影响其他团队的工作。将代码库分割成小块,使得微前端的部署需要更短的时间。
领域驱动架构
采用微前端的原因之一就是实现垂直领域的所有权拆分。从整个公司掌握所有权的单体架构,转变成多个团队掌握所有权的微服务,这有助于公司在不同的团队中扩大开发规模,促进后端所有权的拆分。每个业务导向团队都拥有一个垂直方向的组件,他们从头到尾负责该组件。
减少测试覆盖率涉及的范围
与大型应用相比,少量的代码更易于测试覆盖。这会促成整个系统中更好的测试覆盖率,因为所有小部分的代码都会被测试覆盖到。
使用不同技术和框架的可能性
微前端的优势之一就是不依赖于所选择的技术和框架。它更容易找到专门的团队来完成微前端的工作,允许使用其他的技术和框架取代不适用的技术和框架,也能够以较小的技术风险测试新技术。
微前端架构的基本原则
接下来,我将介绍微前端架构应该遵循的最重要的原则,以保持系统的可扩展性。要实现这一点,我们要引入两个术语:
编排器(orchestrator)是用来协调所有微前端的父应用。在理想的情况下,它不包含任何逻辑或业务功能,只作为一个操作者(operator)存在。
子微前端是一个领域驱动的应用,涵盖了应用中某一个特定的组成部分。
不管技术团队具体使用哪种微前端的方式,为了保持系统的可扩展性,遵循下面这些强制要求是非常重要的。
子微前端和编排器之间的强制要求:
编排器和子微前端应用之间的耦合性几乎为零
编排器不应该关心子微前端应用是如何实现的。如果子微前端的实现方式发生了变化,但提供的接口是相同的,那就不应该对整个应用造成破坏。
子微前端应用必须提供一个统一的接口,以便于集成到编排器中。
子微前端和编排器之间的所有必要的通信都通过回调或简单事件实现
编排器应该能够决定始终使用子微前端的最新版本还是使用指定的特定版本
子微前端之间的强制要求:
子微前端之间是零耦合的。每个子微前端都不应该知道其他的微前端,必须能够独立工作。
不能从一个微前端导入另一个微前端
不能使用共享的状态管理工具(如 Vuex,Redux)。
子微前端之间可以共享库
所有的 CSS 代码必须是范围内的,以排除对样式属性的覆盖。
微前端架构的维护
决定采纳微前端架构并不是过渡旅程的结束。这将是一个痛苦和危险的过程,但同样也是一个有趣的认知经历。许多陷阱将等待工程团队去解决。
其中一个问题是整个微前端架构的维护问题。随着时间的推移,系统可能会有几十甚至几百个微前端,而工程团队将面临新的挑战:如何维护这些微前端。
引入新的微前端
如何引入新的微前端应用将是工程团队在迁移到微前端架构后遇到的第一个问题。如何在不增加复杂性的情况下搭建一个新的微前端脚手架?由哪个团队负责创建新的微前端?微前端应该遵循哪些标准?
这些相关的问题必须由工程团队解决,以保持系统的可扩展性和易维护性。否则,系统在很短的时间内就会变得非常混乱。
我们预期得到的解决方案是,每个新的微前端都能快速创建,并且有非常相似的设置,以保持所有微前端之间的一致性。我想到了“传送带(conveyor)”这个词。
我们有多种方法可以达成该目的。我想和大家分享一个如何实现该目标的想法,那就是使用一个可以在几分钟内基于模板创建新微前端的工具。这可以是现成的解决方案,如cookiecutter或其他类似工具。
同步现有的微前端
创建新的微前端只是万里长征第一步。随着时间的推移,将会有许多微前端需要与基本模板进行同步。这可能也是我们需要模板的原因:解决漏洞、更新配置、更新其他软件包的版本等。
令人遗憾的是,cookiecutter 无法将现有的项目与模板同步。为了解决这个问题,在我的公司中,我们开发了自己的解决方案,它可以为新创建或已有的微前端提供一个脚手架。
在编写概念验证(Prove of Concept)项目的过程中,我创建了mucli项目,它基于我们组织中遵循的类似原则。该项目会在 Github 上获取仓库,在仓库中寻找模板,并创建新项目或同步现有的项目。它提供了如下开箱即用的功能:
从模板库创建和同步项目。你可以创建自己的模板库并使用 mucli 进行项目创建和同步。模板库是完全独立的,由你自行维护。mucli 包只是与你的模板协作而已。
用户友好的命令行界面,可以为每个模板添加自定义的用户问题。
支持在文件中插入动态值,能够根据设置或用户互动来创建自定义的文件。
前缀系统,可以使用不同的前缀来标记文件,以便在同步过程中对这些文件进行操作。
可组合的模板,它可以基于较小的模块创建出一个非常灵活的模板。
欢迎了解mucli项目并为其贡献内容。我将在接下来的文章中深入探讨它的实现细节。
结论
在这篇文章中,我分享了我们从创业公司转变为成熟公司期间的想法和决定。如果你对这些话题感兴趣的话,可以通过GitHub或Twitter与我联系。
相关阅读
Htmx 意外走红,我们从 React“退回去”后:代码行数减少 67%,JS 依赖项从 255 下降到 9
评论