边界、内聚与耦合:把系统拆得明白,质量才有落脚点
所谓“架构设计的功力”,很大程度上就藏在这三个字:边界、内聚、耦合。
你可能已经能画出一张漂亮的系统图,但真正能把系统拆得恰到好处,让每个模块既高内聚又低耦合,能自由组合、还能独立演化,这件事就不是靠「加几个微服务」能解决的。
这一章想帮你做到三件事情:
- 理解什么是系统边界、分层边界、领域边界,为什么架构师永远在“分分合合”
- 用一个记得住的方式理解 7 种内聚和 7 种耦合,知道哪些要追求、哪些要避
- 手上有一套能实际落地的“高内聚、低耦合”原则,面试时不只会背定义,还能讲真实案例
一、先谈边界:没有边界感的系统,很难谈质量
系统边界有多层,不同层次的边界决定了你后续的质量优化空间:
-
领域 / 系统边界:
- 类似“国家”级别:比如你的 C2C 电商系统就是一个完整领域
- 属于 DSSA(领域特定架构) 的范畴,常会参考业内成熟做法
-
子领域 / 子系统边界:
- 类似“省 / 市”级别:商品域、订单域、发票域…
- 可以用 ABSD(基于需求) 或 AT(架构思维方法)来拆
-
分层边界:
- 类似“军区”“高校”这种横跨区域的边界
- 比如应用层 / 接口层 / 领域层 / 基础层,都会有各自的边界和接口控制
整理边界的方法可以借用三国的比喻:
- 孙权(继承派,DSSA):参考前人做法,延续成熟经验
- 刘备(需求派,ABSD):调研用户需求,按业务场景规划系统
- 曹操(定制派):根据自身资源和优势,量体裁衣做剪裁
真正落地时,大多数团队都会混合使用这三种思路。关键是:
- 先把边界划清,再谈质量维度(扩展性、性能、可用性、性能、安全、伸缩性)才有意义
二、七种内聚:用王大锤的“相亲 7 日游”记住它们
内聚描述的是“一个模块内部的组件关系”,越靠左越高内聚:
| 类型 | 特征 | 王大锤的比喻 |
|---|---|---|
| 功能内聚 | 模块里所有元素只干一件事 | “只爱一个人” |
| 顺序内聚 | 必须按固定顺序执行 | 先和粉衣,后和白衣 |
| 通信内聚 | 共享同样的输入/输出 | 都喷香奈儿 5 号香水 |
| 过程内聚 | 按统一流程被控制 | 女儿国国王一声令下 |
| 时间内聚 | 同一时间一起执行 | 每晚 12 点跑批 |
| 逻辑内聚 | 由某个条件/标记区分 | 只要尖下巴我都喜欢 |
| 偶然内聚 | 纯粹凑巧放一起 | 完全靠缘分 = 最差 |
实践中尽量向左靠,功能内聚 / 顺序内聚是我们追求的目标,偶然内聚是要极力避免的。
常见应用:
- 注册 + 登录:常见的顺序内聚(注册完自动登录)
- 定时批任务:典型的时间内聚
- 按 Flag 分支:容易退化成逻辑内聚,要谨慎拆分
三、七种耦合:尽量把连接“松”下来
耦合描述的是“模块之间的关系”,越靠左越低耦合:
| 类型 | 特征 | 策略 |
|---|---|---|
| 非直接耦合 | 没有直接依赖 | 最理想,只通过第三方间接交互 |
| 数据耦合 | 只传必要数据 | DTO/VO;消息体;函数参数 |
| 标记耦合 | 共享同一数据结构 | 尽量拆 DTO,避免 DO 暴露 |
| 控制耦合 | 通过控制流 / Flag | 能不用就不用;必要时把控制逻辑抽出去 |
| 外部耦合 | 共同受外部变量影响 | 限定范围,做好配置隔离 |
| 公共耦合 | 共享全局数据 | 尽量避免,尤其是“紧耦合型”公共变量 |
| 内容耦合 | 直接操作内部实现 | 坚决避免,控制 public 暴露 |
几个常用的降耦技巧:
- 用接口隐藏实现(依赖倒置)
- 减少 DTO 大小,拉开和 DO 的距离
- 把控制流从模块间搬到调度层
- 公共数据通过稳定接口访问(仓储层 / DAO),而不是全局变量
- 内容耦合最危险,能 private 的都 private,public 尽量少
四、如何真正做到“高内聚 + 低耦合”
抽象原则说了一堆,落地时可以抓住这些点:
1. 低耦合设计的关注点
- 模块间传输的数据越多,耦合越高
- 传控制信息比传数据耦合更强
- 接口越复杂(参数多、结构多),耦合越高
- 重用实现细节(而不是抽象接口),耦合会越来越紧
2. 落地策略
- 接口 + 依赖反转:面向接口编程,把实现细节藏在模块内部
- 单处定义 / 命名分离:避免出现 “User” 到处都是却语义不明
- 禁止全局变量:模块间数据传递全走 DTO
- DAO 层 / Repository:领域层不直接操作底层数据库 / 中间件
- 适配器 / Anti-Corruption Layer:领域层和中间件之间加一层适配
- 限制公共耦合范围:即便共用资源,也要用松散方式(消息 / 事件)
3. 高内聚三原则
- 功能聚焦:单一职责,模块只解决一类问题
- 接口隔离:外露的接口越少越好,最好遵循六边形架构那种有限接口数量
- 一切向功能内聚靠拢:遇到逻辑内聚 / 偶然内聚及时拆分
五、王大锤踩坑记:两个“血泪教训”
1. 一个模块同时服务 CEO / CFO / CTO,结果全挂了
大锤做了一个“员工管理报表模块”,同时满足:
- CEO:工时 / 加班宣传
- CFO:工资发放
- CTO:代码效率监控
然后 CEO 改了工时计算逻辑(对外营销),被 CFO / CTO 的系统直接污染:
- 账面看起来员工都超负荷,加班工资爆炸
- 代码效率报告骤降一半,CTO 要裁人
根因:
- 违反单一职责,多个高变业务放在同一模块
- 偶然内聚,把互不相关的报表强行捆绑
- 没有考虑后续变更的频率和利益相关方
可以怎么解?
- 把 CEO / CFO / CTO 的诉求拆成不同模块
- 引入规则引擎,让“工时报表”可配置,避免硬编码
- 让工资 / 绩效模块只依赖稳定数据,不受外部宣传逻辑影响
2. 模块依赖环:CFIH 互相引用,导致任何改动都得全量发布
大锤把系统拆成了多个模块(C、F、I、H…),但 CF IH 之间形成了有向环:
- 只要改其中一处,其他模块都得跟着改 / 跟着发
- 违反依赖无环原则
解决方案:
- 合并模块:如果功能确实高度相关,干脆合并成一个模块
- 依赖反转:把 H 对 I 的依赖转换为 I 对接口的依赖,H 实现接口
- 稳定依赖:抽出一个稳定模块(如用户认证服务),其他模块依赖它
一图胜千言:
I -> H (原有依赖)
↓
抽出接口 IFoo,放在 I 里
H 实现 IFoo
这样就不会形成环,模块可以独立演化。
六、面试高频题怎么答?
-
“如何划分系统边界?”
- 提到 DSSA(领域经验)、ABSD(需求驱动)、定制剪裁
- 最好结合自己项目讲一个「怎么结合需求 vs 领域知识」的案例
-
“如何定义模块?”
- 先抛「高内聚、低耦合」的原则
- 结合一个实际模块设计案例,讲内聚类型 + 耦合控制技巧
- 别忘了强调“曾踩过的坑”和“怎么化解”
-
“中间件 / 数据库经常升级,怎么减少主业务改动?”
- DAO / Repository 层隔离
- 适配器层 / Anti-corruption Layer
- 接口 + 依赖反转,让领域层不直接依赖具体中间件
- 同时提提「稳定依赖原则」会加分
小结:架构的第一步,是把关系捋清楚
边界、内聚、耦合听起来像基础概念,但它们决定了你的系统能否:
- 随业务变动快速调整
- 在局部故障时保持可控
- 在多个质量维度(扩展性/性能/可用性/安全/伸缩)上真正发挥空间
你可以从现在开始练习:
- 画系统时,不只画“有哪些服务”,还标清楚它们的边界和耦合关系
- 为每个模块写下它的内聚类型,问自己能不能再向左靠一点
当你习惯用这些维度审视系统时,系统会变得更简单、更清晰,也更容易在质量维度上做提升。下一章,我们就正式进入五大质量维度的核心内容:扩展性、性能、可用性、安全、伸缩性,继续把这套方法走实。+