Appearance
多业务线前端架构:业务分离与能力复用的边界
在不断扩展的管理后台里,新业务往往从旧业务中“长”出来:筛选条件大体相同,表格字段暂时一致,接口甚至可以复用。最省事的做法,是给旧页面加一个业务类型参数,用条件判断切换数据范围。
这种方案上线快,代价却隐蔽:多条业务线被绑进同一个页面,之后的每次修改都可能牵动全局。另一个极端是整页复制、各自维护——隔离是彻底了,但公共 Bug、交互升级和接口变更都要重复处理。
真正要回答的问题,不是“要不要复用代码”,而是:
哪些代码会因为同一个原因变化,哪些代码只是现在看起来相似?
相似不等于稳定
两个报表可能都有日期、渠道、消耗、收入和 ROI,但字段相同不代表语义相同。随着业务演进,它们会逐渐分化出不同的:
- 筛选条件和默认值
- 数据权限和菜单权限
- 汇总口径
- 下钻层级
- 导出字段
- 操作按钮
- 发布节奏
如果过早把它们抽成一个“通用报表”,这些差异最终会以业务判断的形式渗回代码:
ts
if (isBusinessA) {
// ...
} else if (isBusinessB) {
// ...
}起初只有一个分支,随后扩散到筛选、请求、缓存、弹窗、导出和生命周期。到最后,修改任何一条业务线,都要回归所有业务线。
两种常见的极端
用条件分支复用整页
这条路代码量最小,共享的却是最不稳定的页面编排。典型信号包括:
isXxx判断持续增加- 组件的 Props 和 Slots 不断膨胀
- 公共组件开始认识业务名称
- 修改单个业务也要执行全链路回归
- 新需求只能继续堆配置项,无法在业务目录内闭环
到了这一步,共享页面不再降低成本,而是在放大每次变更的影响范围。
整页复制所有代码
复制能快速建立业务边界,但如果连稳定能力也一并复制,问题会换个形式出现:
- 同一个 Bug 需要修复多次
- 相同的格式化逻辑逐渐产生差异
- API 类型和枚举难以保持一致
- 通用交互升级无法同步
- 评审时难以分辨:哪些差异来自业务需要,哪些只是版本漂移
“完全独立”不应等于“所有代码都复制”。
推荐方案:业务编排独立,稳定能力共享
更稳妥的结构是:每条业务线拥有独立的页面与应用逻辑,同时复用不含业务含义的底层能力。
text
业务A页面
└─ 业务A composable
└─ 公共 API / 组件 / 工具
业务B页面
└─ 业务B composable
└─ 公共 API / 组件 / 工具业务线应当独立拥有的:
- 路由和页面入口
- 页面状态
- 筛选配置
- 请求参数构造
- 权限控制
- 下钻流程
- 生命周期和错误处理
适合下沉共享的:
- HTTP Client 和 API 封装
- DTO、枚举和协议转换
- 日期、金额、比例等格式化
- 分页、导出和下载能力
- 不含业务判断的基础组件
- 已被多条业务线长期验证一致的列工厂
依赖方向始终保持单向:
text
业务页面 → 业务逻辑 → 公共能力公共能力不反向依赖业务页面,更不通过业务类型参数决定自身行为。
用“变化原因”判断是否复用
抽取公共代码之前,可以依次问自己:
- 两段代码未来会因为同一个原因修改吗?
- 字段名称相同,业务语义也完全相同吗?
- 两条业务线总是同步发布吗?
- 修改一方时,另一方理应跟着回归吗?
- 抽取后是否需要传入业务类型参数?
- 公共模块能否在不知道业务名称的前提下工作?
多数答案为否,就应当保持分离。
判断标准可以收敛为一句话:
按变化原因分离,按稳定程度复用。
同一个原因变化的代码放在一起,不同原因变化的代码分开——这比追求更少的文件和代码行更可靠。
允许暂时重复
重复并不总是坏事。面对尚未稳定的新业务,渐进式抽象往往更稳妥:
- 第一条业务线按自身需求实现。
- 第二条业务线允许适量重复。
- 观察两边经历数次真实需求后的演化方向。
- 只抽取被证明长期一致的部分。
- 一旦共享模块开始出现业务开关,重新检查边界。
短期重复的成本可见、可估算;错误抽象带来的耦合,则会在之后的每一次需求中持续付息。
与 Java / Spring 分层的对应
对 Java 开发者来说,这个结构并不陌生——它接近 Controller 与 Application Service 的分层习惯:
text
BusinessAController → BusinessAService
BusinessBController → BusinessBService
↓
SharedClient / Converter / Utility两条业务线可以共用 Feign Client、DTO Converter 和工具类,但很少有人会为了减少类的数量,把两个 Application Service 合并成一个布满业务类型判断的“大 Service”。前端页面与 composable 的边界,本质上是同一个问题。
最后的取舍
架构设计的目标不是消除重复,而是控制变化:
- 业务能够独立演进
- 修改范围容易预测
- 回归边界清晰
- 稳定能力只维护一份
- 公共模块不感知具体业务
当“复用”开始要求公共代码理解业务差异时,选择分离;当多条业务线反复修复完全相同的问题时,才说明其中存在值得下沉的稳定能力。
最理想的平衡不是最少的代码,而是:
让业务自由变化,让稳定能力持续复用。