十大微前端反模式
每年都有越来越多的公司转向微前端。虽然组织效益是(或者更确切地说应该是)转向微前端的核心,但技术原因往往也是驱动力。例如,微前端允许您动态地编写应用程序,或者在不同的应用程序之间重用已完成的域逻辑。
在本文中,我想强调一些我在实际涉及微前端的项目中经常遇到的问题。这些反模式通常已经为人所知,或者从微服务中移植过来——因此相似之处并不少见。
1. 隐藏的巨石
如果您遇到无法直接发布或回滚微前端的情况,那么您很可能创建了一个隐藏的单体应用。当您创建的功能组件分布在多个微前端上时,就可能出现这种情况。虽然这样的功能可能存在,但从技术上讲,应该可以独立开发和发布各个组件,同时保持整个应用程序的正常运行。
如果不是,那么您肯定创建了隐藏的整体,它具有整体的所有缺点(例如,需要大量调整的较大版本)和分布式系统的所有缺点(例如,调试不能同时对所有代码进行)。
此反模式的主要特性包括:
- 缺乏自主性:团队不能直接部署,即使部署了,他们也需要额外的协调和配合。
- 复杂的部署:部署并不由单个团队负责,而是需要协调或大量的 CI/CD 魔法。
- 共享状态:应用程序中有一些重要对象和部分,每个人都会使用它们 - 但没有人觉得有责任维护或继续开发它们。
使用 DDD 正确拆分应用程序并保持松散耦合,以避免微前端之间的依赖
2. 健谈的前端
由于其解耦的特性,微前端仍然需要一些通信(例如以事件的形式)来相互通信,以用于各种原因,例如处理应用程序的工作负载。
虽然通信本身没有问题,但几乎每个操作都触发事件这种过度通信会导致不同 UI 片段之间低效的交互。总的来说,这会降低它们的效率,也更难开发。
此反模式的主要特性包括:
- 频繁事件发射:完全不清楚哪些事件是必要的,哪些不是;您所看到的只是许多事件一直在发射。
- 长调用级联:一个微前端调用其他东西,调用其他东西,调用其他东西等等 - 直到其中一个接收者停止链。
- 嘈杂的调试:由于所有事件和其他消息,几乎不可能清楚地了解调试期间发生的情况。
仅发出有用的事件,仅在有相关方时才可能引入事件
3. 框架疯狂
不幸的是,历史上提倡或引入微前端的最常用原因之一是能够从多个框架渲染组件。
虽然这个功能确实很酷,但它并非微前端独有,也不应该被滥用。当然,如果你的框架纯粹在服务端渲染,或者像 Svelte 一样在编译时就消失,那么成本可能几乎无关紧要。然而,每次加载不必要的 JavaScript 都会降低性能。
引入多个框架除了性能成本之外,组件之间的通信也会受到影响。此外,尝试理解应用程序的多个方面时,认知负担肯定会增加——对于只熟悉单个框架的人来说,这无疑会使其更加难以理解。
此反模式的主要特性包括:
- 性能差:加载应用程序需要很长时间 - 大部分时间不是花在加载某些数据上,而是实际加载代码来加载数据。
- 高复杂性:人们需要成为多框架及其特定问题和哲学的专家,才能理解单个微前端之外的代码。
- XKCD 927:为了整合现有标准,我们创建了一个新标准——最终实现了我们期望的框架独立性。这不太可能,只是意味着我们在内部托管了另一个框架。
尝试确定单一技术并仅引入对其他框架的支持 - 仅在合理的情况下引入。
4. 微型万物
进入微前端最难的事情之一就是找到“正确”的领域分解。一个潜在的观察是,理想的领域分解是不可能找到的——因此,保持其实用性是关键。
当你遇到某个功能难以归入现有子域名时,你可能会想直接开辟一个新的子域名,并创建一个新的微前端。虽然这种方法可行,但它很容易导致创建大量非常小的微前端。
微前端过多会带来一些问题。维护和一致性会受到影响,认知负荷也会增加。
总的来说,更好的策略是将没有明确子域的微前端放入应用外壳(如果相关性看起来更技术性/领域特定性较低),或者放入托管所有“未分配”部分的特殊微前端。这样,一旦明确了该功能的指定,转移到专用的微前端就更有意义了。
此反模式的主要特性包括:
- 过度碎片化:最终用户的单一功能由三个以上的微前端整合在一起。
- 参考地狱:由于分布式部分太多,基本上每个函数都会调用来自另一个远程部分的函数。
- 低内聚:内聚是指模块内元素协同工作以实现单一、明确目标的程度。低内聚时,元素之间松散关联,并服务于多种目的。
不要分裂得太早,从最接近的域/微前端开始,并且仅在出现清晰的子域后才提取。
5.违反单一职责
这里,我们看到了对面向对象设计中模块职责的根本性违反。当单个微前端承担了多个本应分离的职责或关注点时,就会发生这种情况。例如,当支付处理微前端也处理用户注册时。
单一职责原则 (SRP) 是面向对象编程 (OOP) SOLID 原则的基石之一。它致力于降低复杂性,从而减少错误并使组件更具可插拔性。因此,实现起来可能更加专用、轻量级,并且更易于测试。
永远记住,“微”一词并不意味着一定的代码行数限制,而是关注应用程序的单个子域。
此反模式的主要特性包括:
- 所有权不明确:当被问及某个功能或微前端背后的负责人时,没有人觉得自己有责任。
- 不可重用:任何时候有人提出这样的想法:“我们可以从您的微前端使用这个组件”或“我们可以将您的微前端插入这里吗?”答案是否定的 - 仅仅是出于技术原因。
- 领域重叠:单个微前端的开发涉及多个 PO 或领域专家。
将物品放在其所属的位置 - 不要混合搭配。
6.意大利面条式建筑
一些反模式是不言自明的,就像意大利面条式架构一样,这是一种常见的软件架构表达,它缺乏清晰的结构和组织,导致相互连接的组件和模块混乱不堪。
避免意大利面式架构的一个相当好的模型是扁平模型,其中所有微前端都由一个中心位置——微前端发现服务——提供。当然,为了保持扁平,任何微前端都不允许加载其他微前端。现在最大的问题是:一个微前端如何使用其他微前端的组件(或任何东西)?
简而言之,微前端应该是松耦合的(参见第 10 点)。因此,一个完善的架构应该采取措施,确保一个微前端永远不需要了解(或加载)任何其他微前端。
此反模式的主要特性包括:
- 复杂的控制流:沿着参考地狱和长尾,我们在这里看到同样的情况——甚至可能在不知不觉中异步流也会做出贡献。
- 缺乏关注点分离:边界实际上没有定义,并且每个微前端在技术层面上看起来和感觉都不同。
- 技术重复:您会看到同一个问题以不同的方式得到解决,例如,通过使用多个状态容器库、多个框架或其他东西以不同的方式解决同一个问题。
保持松散耦合,不依赖调用网络来提供功能。
7.分布式数据不一致
第 2 点(聊天式前端)存在的原因之一是,沟通似乎是必需的。否则,你如何才能将数据从一个微前端共享到另一个微前端呢?然而,事实证明,我们不应该这样做。
一旦共享从领域角度来看属于同一个微前端的数据,就会遇到各种麻烦。其中一个可能遇到的问题就是一致性问题。如果原始数据发生变化,则需要传播后续更改,但这并非唯一可能的更改。另一种可能性是,获取数据副本的微前端也需要这些更改。这种情况真的允许吗?这样一来,原始数据和副本数据又会再次出现偏差。
理想情况下,数据只保存在一个地方(遵循第 5 点中的 SRP),其他微前端仅在发生相关事件时以 props 或 fragments 的形式访问数据。
此反模式的主要特性包括:
- 冲突操作:两个微前端对同一数据进行操作可能会导致竞争条件或某些未知状态。
- 所有权问题:谁拥有数据?如果你(或其他)状态容器中存在数据来源不明的数据,那么显然存在一个悬而未决的所有权问题。
- 异步更新:虽然什么都没发生,但肯定有事情发生——可能是由于待处理的任务、延迟的请求或计划的操作。无论如何,数据都应保持一致,并明确所有权。
将数据保存在它所属的位置;让其他人不要直接访问数据,而只能通过属性/道具等间接访问。
8.忽视人为因素
项目中当然有一些方面应该超越项目目标和指标。但是,如果您认为在满足技术目标和截止日期时没有考虑团队成员的福祉、士气和工作与生活的平衡,那么就会出现问题。
一般来说,你应该将微前端视为一种改善团队组织和结构的模式,而不是让团队承受痛苦。因此,如果微前端解决方案带来了巨大的痛苦,那么肯定是出了什么问题。
此反模式的主要特性包括:
- 过度劳累:长时间工作(无偿)、周末工作或错过休息时间——有很多理由说明你应该逃跑。
- 微观管理:上级不但没有进一步授权团队,反而更加试图控制团队的每一个行动。如果你发现团队的权力不增反减,你就知道是时候做出改变了。
- 不切实际的期望:有没有想过“他们到底在听吗?”或者“谁会把坏消息告诉管理层?”。当你害怕让他们保持冷静,或者现实无法满足你的需求时,你就知道失败即将来临。
加强团队建设,尽量避免中央管理。
9.避免可观察性
调试分布式系统始终是一项挑战。当任何地方都无法实现可观察性时,整个问题只会变得更加严重,例如,如果您不知道哪个微前端是错误的根源,或者您无法在本地调试有问题的部分。
一般来说,您的机器上应该可以运行单个(或根据需要/需要多个)微前端。在进行本地调试时,整体行为及其状态、性能和其他指标都应该是可测试的。
此反模式的主要特性包括:
- 有限的日志记录:如果发生意外,您需要查看一些日志,至少能找到一些线索来查找问题所在。如果日志记录太少,甚至没有,那么问题就比较严重了。
- 指标不足:有时会出现诸如“提升性能”之类的用户故事。当故事声称性能下降时,情况会变得更糟。使用正确的指标,我们至少可以建立基准,并以此为基础进行改进。
- 稀疏追踪:尤其是在分布式系统中,我们需要能够追踪路径——跨越障碍。如果追踪稀疏,找到根本原因就会非常困难。
引入中央解决方案来简化日志记录、收集跟踪等。
10.紧耦合
紧耦合无疑并非反模式。尽管如此,仍有各种理由将其列入此列表。最值得注意的是,紧耦合通常是微前端扩展过程中许多问题的根源。
紧耦合源于这样的观察:单体应用实际上包含许多我们真正需要的方面。我们确实想知道导入和使用的内容,并且希望表现得像是在掌控之中。然而,分布式系统天生就是分布式的,因此任何控制都只是一种幻觉。转向松耦合只是正式放弃这种幻觉的一种方式。
为什么要选择松耦合?虽然一开始可能看起来比较困难(例如,考虑事件——你只是发出一些抽象事件,而不知道微前端会对其做出什么反应以及如何反应),但以后你会感觉更自然、更强大。此外,它隐式地避免了紧耦合,因此很大程度上解释了隐藏单体存在的原因。
此反模式的主要特性包括:
- 无法关闭:微前端的必要测试是关闭单个(任意)微前端。如果系统继续运行,则一切正常——否则,存在一些不应该存在的依赖关系。
- 更长的入职流程:新团队的入职应该非常快捷。从标准样板创建微前端,设置 CI/CD 流水线,然后推送代码。如果涉及更多步骤,整个解决方案可能会比预期复杂得多。请始终记住:微前端解决方案总体上更复杂(作为整体),但对于个人而言,开发起来应该更简单。
- 增强协调工作:一切都关乎自主性——因此,如果您需要比以前更多地与单体应用进行协调,那么一定是出了问题。一般来说,微前端应该允许您自行决定,并让您的团队做出必要的决策;协调工作是创建良好用户体验的可选因素,而非软件发布的必要条件。
避免直接引用需要任何技术知识(URL、模块路径、内部名称等)的其他微前端。
结论
希望你没有发现自己的项目符合上述一个或多个类别。然而,即使你符合,在特定情况下,如果理由充分,这样做或许也是合理的。
因此,请谨慎对待这份清单,并找到一个不仅能解决手头问题,而且还能利用可用资源(例如团队)的解决方案。
鏂囩珷鏉ユ簮锛�https://dev.to/florianrappl/top-10-micro-frontend-anti-patterns-3809