2. 模块边界定义原则
模块边界拆分是企业应用架构中最关键的设计决策之一。错误的边界会导致后续的集成、测试、部署成本指数级上升。本节围绕三条核心原则展开:高内聚低耦合、单一职责原则、限界上下文,并结合审批流场景说明如何在实践中应用这些原则。
2.1 高内聚低耦合:模块拆分的基石
高内聚(High Cohesion)指模块内部的元素在功能上紧密相关,共同完成一个明确的任务;低耦合(Low Coupling)指模块之间的依赖关系尽可能弱,一个模块的变更对其它模块的影响最小。
| 维度 | 高内聚表现 | 低耦合表现 |
|---|---|---|
| 变更影响 | 修改集中在模块内部 | 修改不扩散到外部模块 |
| 复用性 | 模块可独立抽取复用 | 外部依赖少,复用成本低 |
| 测试难度 | 单元测试覆盖率高 | Mock 外部依赖少 |
| 团队协作 | 团队可独立开发模块 | 接口契约稳定,并行开发 |
实践检查点:在审批流场景中,如果“审批规则引擎”与“审批表单渲染”放在同一个模块,修改表单字段时可能意外影响规则计算,这就是内聚不足、耦合过高的信号。
2.2 单一职责原则:模块只做一件事
单一职责原则(Single Responsibility Principle, SRP)要求一个模块只对一个角色(Actor)或一个变化原因负责。在模块边界定义中,SRP 帮助我们将“变化点”隔离到独立的模块中。
- 变化原因分离:审批流程的流转逻辑、审批人的计算规则、审批通知的发送策略,三者变化频率和原因不同,应拆分为独立模块。
- 职责粒度控制:一个模块的职责描述应能用一句话说清楚。例如:“审批流引擎模块负责根据配置的流程定义驱动审批节点流转”。
- 避免“上帝模块”:如果一个模块的代码超过 3000 行或包含 5 个以上不相关的功能,说明 SRP 已被违反。
常见误区:不要将“职责”等同于“功能”。一个模块可以包含多个功能,只要这些功能服务于同一个变化原因。例如“审批流引擎”可以包含“节点跳转”、“超时处理”、“驳回重审”,它们都服务于“流程流转”这一职责。
2.3 限界上下文:领域驱动的边界语言
限界上下文(Bounded Context)是领域驱动设计(DDD)的核心概念,它定义了模型应用的显式边界。每个限界上下文内部有统一的语言(Ubiquitous Language),上下文之间通过明确的接口进行通信。
在模块拆分中,限界上下文提供了比技术分层更稳定的边界依据:
- 业务语义边界:“审批”在“采购上下文”中可能指“采购订单审批”,在“人事上下文”中可能指“请假审批”,两者虽然都叫“审批”,但业务规则、数据模型完全不同,应属于不同的限界上下文。
- 数据所有权:每个限界上下文拥有自己的数据存储,不允许直接访问其他上下文的数据库表。
- 通信方式:上下文之间通过事件(Event)或 API 进行异步或同步通信,避免共享内部模型。
识别技巧:当两个团队对同一个术语(如“客户”、“订单”、“审批人”)有不同理解时,这就是一个强烈的信号——它们应该属于不同的限界上下文。
2.4 三条原则在审批流模块拆分中的综合应用
以下 SVG 图展示了如何将高内聚低耦合、单一职责、限界上下文三条原则应用于审批流系统的模块边界定义:
2.5 原则冲突时的权衡策略
在实际项目中,三条原则有时会相互冲突。以下表格总结了常见的冲突场景及推荐权衡策略:
| 冲突场景 | 原则 A | 原则 B | 推荐权衡策略 |
|---|---|---|---|
| 审批规则与审批表单强关联 | 单一职责(应拆分) | 高内聚(频繁一起变化) | 优先高内聚,放在同一模块,但通过接口隔离内部变化点 |
| 多个上下文共享审批人计算逻辑 | 限界上下文(不应共享) | 低耦合(复用减少重复) | 抽取为共享内核(Shared Kernel),但严格控制变更流程 |
| 通知服务需要同步返回结果 | 低耦合(异步更优) | 单一职责(通知只负责发送) | 保持异步事件通信,通知服务内部实现同步重试机制 |
| 采购与人事审批流程高度相似 | 限界上下文(应分离) | 高内聚(代码复用) | 通过抽象公共流程库,但保持各自上下文的独立部署权 |
核心原则:当原则冲突时,优先保证限界上下文的边界完整性,因为业务语义的混淆比代码重复带来的成本更高。在上下文内部,再追求高内聚和单一职责。
2.6 模块边界定义检查清单
完成模块边界定义后,使用以下清单进行自检:
- 内聚检查:模块内的所有元素是否都服务于同一个业务目标?能否用一句话描述模块的职责?
- 耦合检查:模块对外部的依赖是否少于 3 个?修改模块内部实现是否需要同步修改外部模块?
- 职责检查:模块是否只有一个变化原因?不同角色(如业务人员、运维人员、开发人员)对模块的修改需求是否不同?
- 上下文检查:模块内的术语是否在整个团队中有一致的理解?是否存在同一个术语在不同模块中含义不同的情况?
- 数据检查:模块是否拥有并管理自己的数据?是否存在跨模块直接访问数据库表的情况?
推荐实践:在项目初期使用事件风暴(Event Storming)工作坊,与业务专家一起识别限界上下文。这比纯技术架构师闭门设计更准确,也更容易获得业务方的认同。