跳转到内容

一个微前端应该有多大?

“Microfrontend”这个词很容易让人产生一种直觉:微前端就应该是一个特别小的 Frontend。

于是很快就会出现各种规则:一个 Remote 只能包含少量 Component,只能对应一条 Route,或者最多只能覆盖页面上的一小块区域。有时甚至会用文件数量、函数数量、Bundle 大小或代码行数来衡量它是否“够微”。

这些指标对 Performance、Build 时间或维护成本当然可能有意义。但它们回答不了那个战略问题:这个区域是否被合理地切分,并且能够由一个团队独立承担职责。

一个很小的 Bundle 也可能拥有糟糕的业务边界。反过来,一个规模很大的 Remote 完全可能拥有清晰而稳定的边界。

所以,对“一个微前端应该有多大?”最直接的回答是:视情况而定——但决定因素不是代码行数、Component 数量、Route 数量或屏幕面积。

一个微前端应该大到足以覆盖一个团队能够合理理解、负责并在很大程度上独立修改的业务变化空间。

在微服务领域,人们很早就开始讨论一个 Service 最多应该有多少行代码、多少个 Endpoint。到了微前端,这个问题只是换成了 Component、View 和 Bundle。

“Micro”这个前缀听起来像是在描述尺寸,但它并没有提供任何可靠的计量单位。没有一个数字可以告诉你:从这一刻开始,这个 Frontend 就大得不能再叫微前端了。同样,一个技术单元也不会因为足够小,就自动成为一个合理的业务边界。

一个只包含五个 Component 的 Remote 可能混合了多个互不相关的职责。一个包含一百个 Component 的 Remote,则可能只是完整表达了一个连贯的产品领域。

可见面积也帮不了太多。Dashboard 上一张很小的卡片,可能代表一个拥有独立数据、独立 Release Cycle 和明确 Ownership 的业务能力。而一整页界面,也可能只是一个更大业务流程中的某一个步骤。

因此,微前端的大小无法从 UI 面积上读出来。真正重要的是:这条边界内部究竟聚合了什么职责。

按业务变化空间,而不是按屏幕面积

Section titled “按业务变化空间,而不是按屏幕面积”

一条可持续的边界,应该把业务上真正属于一起的功能放在一起。它们使用同一种语言,因为相似的业务原因发生变化,并且能够由同一个团队共同理解。

核心问题是:

哪些东西通常会一起变化?哪些东西应该能够彼此独立地变化?

如果一组功能满足下面这些特征,它们通常更适合位于同一个微前端中:

  • 使用相同的业务术语和规则;
  • 因相似的业务原因发生变化;
  • 拥有共同且可控的 Backend Contract;
  • Release 和变化周期相近;
  • 能由同一个团队理解并承担职责;
  • 与相邻区域之间存在清楚可解释的边界。

业务上的接近,比页面上的接近更重要。

两个元素可以在同一页面上紧挨着,却属于完全不同的职责范围。反过来,一个业务上连贯的区域也可能跨越多个页面、Dialog、Route 和后台流程。

屏幕布局描述的是 UI 如何组合。它不会自动告诉你系统的业务架构应该如何切分。

诊所里的日历,看起来可能只是一个简单功能。实际上,它完全可能包含相当深的业务复杂度。

日历 Domain 可能涉及:

  • 预约创建和改期;
  • 医生、房间和其他资源;
  • 不同预约类型;
  • 周期预约和重复规则;
  • 禁用时段和缺席;
  • 冲突检测;
  • Waiting List;
  • 取消;
  • 权限;
  • 不同日历视图;
  • 关于可用性和利用率的业务规则。

这样的日历无论在技术上还是业务上都不小。但它仍然可以形成一个非常清晰的业务空间。

它与患者档案、结算、文档、用户管理或沟通等区域之间的职责边界通常可以清楚描述。日历内部的很多规则彼此紧密关联,而边界之外使用的是另一套术语、另一类变化原因,往往也属于不同 Ownership。

因此,日历不会因为自身足够“深”就显得过大。

真正值得怀疑的是:同一个 Remote 是否在日历之外,又继续承担了结算、患者管理、文档等能够独立变化的领域。那时增长的不只是 Domain Depth,更是职责的宽度。

微前端不一定要限制一个 Domain 的深度。它更重要的作用,是限制职责与协调范围的宽度。

只要外部边界仍然清楚,一个深 Domain 完全可以很大。

一个业务上很深的日历区域包含预约、医生、房间、资源、预约类型、周期、冲突和等待列表,并与患者档案、结算和文档清楚分离。

从技术上看,把一个日历页面拆成多个 Remote 完全没有问题:

日历页面
├── Toolbar-Remote
├── Filter-Remote
├── Calendar-Remote
├── Detail-Remote
└── Action-Remote

在架构图上,这种拆法甚至可能显得非常整齐、非常模块化。但从用户和业务流程来看,Toolbar、Filter、Calendar、Detail 和 Action 很可能只是同一个业务流程的不同部位。

Filter 变化会影响 Calendar。用户在 Calendar 中选择一个预约,会打开 Detail。某个 Action 改变预约后,又需要同步更新 Calendar、Detail 和用户反馈。Error、Loading 和 Permission 同样跨越多个部分。

这种技术切分并没有创造独立的变化空间。它只是把一个本来连贯的流程分布到了多个技术 Artifact 中。

常见结果包括:

  • 更多 Runtime Dependency;
  • State 被拆散并需要额外同步;
  • Error 和 Loading 处理被碎片化;
  • 对整个流程的 Ownership 变得模糊;
  • Integration Test 和 E2E Test 更复杂;
  • 本地开发更慢;
  • 更多 Build 和 Deployment Pipeline;
  • 虽然独立部署,却仍然必须共同 Release;
  • Observability 和故障分析成本上升。

一个能够独立 Deployment 的 Widget,还不等于一个能够独立负责的产品区域。

这并不是说小 Remote 一定错误。只要它表达的是一个真正独立的业务能力,有清楚的 Owner,并且能够独立变化,那么很小的范围也可以是一条非常好的边界。

例如,一个 Reporting Widget 即使视觉上只占很小区域,也可能拥有自己的数据源、独立 Release Cycle 和明确 Ownership。它看起来小,并不能证明它应该或不应该独立存在。

问题不在于“小”。

问题在于缺少业务独立性。

技术上可以拆开,不代表业务上就应该拆开。

与过度碎片化相反的情况,是一个 Remote 几乎覆盖了整个应用:

Administration-Remote
├── 患者
├── 日历
├── 结算
├── 文档
├── 用户
└── 设置

形式上,这个系统依然可以独立 Build 和 Deploy。相对于 Host,它甚至可能拥有非常清晰的技术边界。

问题在于,Remote 内部同时聚合了多个本来可以独立变化的业务区域。它们使用不同的业务语言,依赖不同的 Backend Contract,也可能由不同团队负责。

于是 Build 和 Test 的范围重新变宽。Release 又开始覆盖多个领域。Refactoring 的影响半径变大。多个团队在同一片代码空间里工作,需要持续协调彼此修改。

单独的 Deployment Pipeline 并不会改变这些事实。这个 Remote 只是变成了 Host 内部的一个 Frontend Monolith。

一个 Remote 能够独立部署,并不代表它的切分就合理。

这里的问题同样不主要是文件数量。关键在于:多组本可独立变化的业务职责,被压在同一个 Ownership 边界里。

所以,大 Remote 也并非天然错误。一个业务很深的区域完全可能需要很多代码。真正值得警惕的是:同一个 Remote 中出现不同的 Owner、不同的业务语言、明显不同的 Release 节奏,或者不同的安全要求。

三种微前端边界:技术上被过度碎片化的日历、业务上连贯的日历区域,以及职责过宽的 Administration-Remote。

Route 是 Navigation 概念。它让内容可以通过 URL 被寻址,并决定某个 URL 下显示什么界面。

它并不会因此自动获得业务职责。

一个微前端可以拥有多条 Route。一条 Route 也可能只是一个更大业务流程中的某一步。一个页面可以集成多个独立区域。Dialog、Card 或 Widget 同样不会因为自身是一个 UI 单元,就自动成为合理的架构边界。

URL 结构描述的是 Navigation,而不是业务 Ownership。

同样,共享 Layout 也不能证明业务上属于一起。Header、Navigation 和 Content Area 可以在视觉上形成一个产品整体,同时拥有不同生命周期和职责。

反过来,一个连贯业务区域也可以跨越 List、Detail、Wizard 和 Dialog。沿这些视觉元素继续切分,并不会让业务模型变清楚。

Route 不是 Bounded Context。UI Component 也不是组织单元。

一个 Remote 对应一个 View,是启发式规则

Section titled “一个 Remote 对应一个 View,是启发式规则”

“一个 Remote 对应一个 View”仍然可以是一个有用的经验规则。它限制了单个业务 Flow 中需要发生的 Runtime Integration 数量,也更容易保持 State Boundary 清晰。

如果一整个 View 基本由一个 Remote 负责,本地开发和测试往往会简单很多。Loading、Error 和用户交互都留在一个职责区域内,Host 也不需要协调过多技术单元。

因此,这条规则可以作为防止过度碎片化的一盏警示灯。

但它不是微前端的定义。

一个页面完全可以集成多个真正独立的区域,例如 Navigation 和其他平台能力,一个拥有独立生命周期的 Reporting Widget,或者一项附加 Capability。

一个 View 包含很多 Remote 时,真正的问题不是数量本身,而是这些 Remote 是否共同组成一个不可分割的业务流程。持续交换 State、只能一起测试、只能同步发布,以及没人真正负责整体体验,才是更明显的警告信号。

一个 Remote 对应一个 View,是用来提醒你别把 UI 切得太碎的启发式规则,不是普遍适用的架构定律。

即使业务边界本身合理,一个区域仍然可能大到一个团队难以承担。

Domain 可能非常深,需要大量专业知识,也可能变化频率极高。多个并行 Initiative、技术运维责任和复杂业务规则都可能显著增加 Cognitive Load。

这里需要区分一件事:

一条边界可以在业务上正确,却在组织上仍然太宽。

解决办法也不是自动把 Component 或页面区域分给更多团队。首先应该确认:这个领域内部是否真的存在进一步可以独立变化的子空间。

也许 Domain 内部存在不同子领域,拥有自己的语言、规则和 Release 节奏,那么继续拆分就可能合理。

也可能这些部分本来就高度关联。如果为了团队数量强行进行技术切分,反而只会增加沟通。这时问题更可能出在 Team Size、Prioritization、知识分布或技术复杂度,而不是业务边界本身。

一个 Remote 由多个团队同时修改,是警告信号。

但它还不是边界错误的自动证明。

不存在固定的尺寸公式。但可以通过一些具体问题检查边界:

  • 这些功能使用同一套业务语言吗?
  • 它们通常因为同一种原因发生变化吗?
  • 一个团队能合理理解并承担这片职责吗?
  • 它们是否共享一个清晰、可控的 Backend Contract?
  • 它们通常一起 Release,还是能够独立 Release?
  • 一次修改是否经常需要和其他 Remote 协调?
  • 再拆一步会真正带来自治,还是只会增加沟通?
  • Remote 内部是否存在不同 Owner?
  • 某些部分是否拥有明显不同的 Release 速度?
  • 能否在不启动产品大部分区域的情况下,本地开发和测试这个范围?
  • 不看架构图,业务人员和开发者能否解释这条边界为什么存在?

这些问题不会算出一个 Score,也不会给出数学意义上的“正确大小”。它们只是帮助看清:技术边界与真实变化空间是否一致。

经济上合理的边界,通常不是技术上最小的边界

Section titled “经济上合理的边界,通常不是技术上最小的边界”

每增加一条 Remote Boundary,都有成本。

Remote 过小时,需要额外 Pipeline、Integration Mechanism、Contract、Test 和 Observability。Error 会跨越更多 Runtime Boundary。本地开发和故障分析变得更麻烦。团队明明是为了自治而拆分,却可能因为大量技术边界反而需要更多协调。

Remote 过大则会产生另一类成本。Build 和 Test 覆盖面变宽,Release Cycle 变长,Refactoring 影响更多功能。多个团队必须在同一区域协调修改,Ownership 变得模糊,决策速度下降。

因此,经济上合理的边界通常不是技术上能够切出的最小单元。

它应该位于这样一个位置:继续切分所获得的独立变化能力,大于它新增的 Integration 和运维成本。

即便最初经过认真设计的边界,后来也可能被证明不合适。产品会变化,Ownership 会移动,原本紧密关联的功能也可能逐渐形成不同生命周期。

因此,划分边界始终是一项带有不确定性的架构决策。

多个 Remote 经常需要一起修改,可能意味着一个连贯区域被切得太碎。相反,如果多个团队长期在同一个 Remote 内不断发生冲突,也可能说明这条职责边界太宽。

这些信号都不是自动证明。共同 Deployment 并不会单独证明边界错误。一个很大的 Remote 也不会自动等于 Frontend Monolith。真正值得观察的是变化是否持续被同一组依赖绑在一起。

好的边界不是一次测量出来的,而是在真实修改中不断被检验。

微前端的正确大小无法靠计数得到。它体现在:团队是否能够在这条边界内完成修改,而不需要经常协调产品其余部分。

“越小越好”不是架构规则。

“边界清楚到足以独立变化”才是。