第2章 API的模块边界定义:内聚与耦合、职责分配、接口归属
模块边界是API设计的起点,决定了通信的粒度、安全域和演进成本。本章从经典的内聚与耦合度量出发,结合分层架构中的职责分配,给出接口归属的判断规则,并延伸至数据流、控制流与信任链路的落地约束。
核心观点:API的模块边界不是纯技术线,而是业务内聚性与系统依赖成本的平衡产物。边界越清晰,后续变更的冲击范围越小。
2.1 内聚与耦合:边界质量的量化
内聚衡量模块内部元素的关联强度,耦合衡量模块之间的依赖强度。高内聚、低耦合是模块化设计的黄金标准,它直接影响API的接口粒度与变更频率。
| 维度 | 高内聚 | 低耦合 | 反面(坏味道) |
|---|---|---|---|
| 定义 | 模块内元素共同完成单一职责 | 模块间依赖少且明确 | 模块内职责混杂,模块间蛛网依赖 |
| API对应 | 一个API只操作一个聚合根 | API参数不含对方模块的内部状态 | API返回整个数据库行,或调用链超过3层 |
| 度量方式 | 类/函数的扇入扇出比 < 1.5 | 模块间接口数 ≤ 该模块对外暴露的API数 | 单模块同时依赖6个以上外部模块 |
| 重构倾向 | 保持内聚,拆分过大的聚合 | 引入防腐层或适配器 | 尽早进行模块拆分 |
实践提示:在代码评审中加入内聚/耦合检查点——如果发现一个模块的修改同时影响了三个以上下游模块的API,说明边界定义需要重新审视。
2.2 职责分配:单一职责原则在边界上的应用
每个模块只能拥有一个变更理由。对于API,这意味着一个接口族群应当只服务于一个业务域。常见的分配模式包括:
- 实体服务层:负责核心实体(如订单、用户)的CRUD与业务规则,对应RESTful资源API。
- 流程编排层:组合多个实体服务完成一个业务用例,对应RPC或事件驱动API。
- 数据查询层:专用于复杂查询与报表,通常使用GraphQL或CQRS中的读模型API。
- 基础设施层:缓存、消息、文件存储等通用能力,通过SPI或SDK暴露,不使用业务语义API。
常见错误:将查询逻辑与更新逻辑混在同一个API中(例如在订单查询接口里更新状态),导致职责边界模糊,测试和缓存策略都难以收敛。
2.3 接口归属:谁提供、谁定义、谁维护
接口归属决定了API的演化主权。三种常见归属模式:
- 服务提供者定义:最常用,提供者控制协议,消费者适配。适合稳定核心域。
- 消费者驱动契约:消费者先定义期望,提供者实现。适合多方调用方场景(如开放平台)。
- 中间层定义:BFF(Backend For Frontend)或API Gateway统一整理接口,对下适配服务,对上满足客户端。适合移动端与多端场景。
| 归属模式 | 适用场景 | 风险点 | API版本管理策略 |
|---|---|---|---|
| 提供者定义 | 内部服务间调用 | 消费者被迫升级 | 向后兼容优先 |
| 消费者驱动 | 开放平台/SaaS | 契约膨胀、维护复杂 | 契约先审批后开发 |
| 中间层定义 | 多端统一后台 | BFF成为单点瓶颈 | BFF层独立版本 |
决策规则:如果该API的主要调用方固定且内部团队,优先提供者定义;如果该API需要服务上百的未知客户端,优先消费者驱动;如果该API的业务域分散但前端希望合并,优先中间层定义。
2.4 系统分层与组件职责:边界在哪一层?
典型的四层逻辑模型(展示层 → 应用层 → 领域层 → 基础设施层)要求API边界只存在于相邻层之间,不允许跨层直接调用。边界定义的核心是:
- 展示层-应用层边界:接收DTO(数据传输对象),返回View Model。这一边界最常产生API,也是HTTP/JSON接口的默认位置。
- 应用层-领域层边界:传输领域对象或聚合根,通常使用进程内接口(如Java Interface)。不对外暴露,仅做防腐。
- 领域层-基础设施层边界:通过Repository或SPI隔离,外部模块只能通过依赖反转后的接口调用。
2.5 数据流、控制流与信任链路
模块边界上的通信形式决定了API的安全性、延迟和一致性模型:
- 数据流:请求/响应或事件发布,关注序列化格式和字段兼容性。边界处必须显式定义数据契约(如Protobuf或JSON Schema)。
- 控制流:同步RPC(依赖上游返回继续)、异步消息(事件驱动)、回调(长任务)。控制流选择直接影响超时、重试和幂等策略。
- 信任链路:跨模块调用必须携带身份与上下文,通常使用JWT或Service ,并在边界层做权限校验。不允许未鉴权的内部直连。
信任链路风险:如果模块间允许无认证的内部DNS直连,一旦某个模块被攻破,整个内部网络都会暴露。所有跨模块API必须经过API Gateway或Service Mesh的mTLS隧道。
2.6 选型权衡
| 权衡维度 | 选项A | 选项B | 推荐场景 |
|---|---|---|---|
| 接口粒度 | 粗粒度(单API返回整棵对象树) | 细粒度(多个API聚合) | 粗粒用于读多写少场景,细粒用于领域驱动设计 |
| 同步 vs 异步 | 同步REST/gRPC | 异步消息队列 | 实时交互用同步,最终一致场景用异步 |
| 序列化 | JSON(易读、灵活) | Protobuf(高性能、强类型) | 内部服务间推荐Protobuf,外部API推荐JSON |
| 版本策略 | URI版本(/v1/) | 请求头版本(Accept-version) | URI版本适合外部,头版本适合内部 |
2.7 风险点
- 边界模糊:模块职责重叠,导致API重复或环形依赖。解决方案:定期使用依赖分析工具(如JDepend、SonarQube)生成模块依赖图,强制去环。
- 过度拆分:模块粒度太细,API数量剧增,运维和调试成本上升。管理策略:每个微服务至少包含一个完整的业务子域,避免“贫血模型”。
- 跨层调用:表示层直接调用基础设施层API,绕过应用的领域逻辑。硬规则:禁止非相邻层直接通信,引入BFF层做隔离。
- 信任泛滥:内部模块之间不加鉴权,一旦内网攻破,全量沦陷。对策:所有跨模块API必须携带有效service ,并定期轮换。
2.8 落地步骤
- 业务领域建模:用事件风暴或四色原型划分子域,每个子域对应一个候选模块。
- 定义模块内聚评估矩阵:列出每个模块内的实体、行为、数据源,确保无交叉职责。
- 设计接口契约:先确定接口归属方,再编写OpenAPI/Protobuf定义,并加入版本号。
- 建立信任链路:部署API Gateway或Service Mesh,配置mTLS和校验。
- 模拟边界压力测试:制造最坏调用链(如6级嵌套调用),验证超时、重试、熔断是否生效。
- 验收并冻结边界:通过自动化契约测试,确保每个模块只按定义暴露接口,禁止运行时动态扩展。
2.9 验收标准
- 模块内聚度:每个模块的内聚指数(LCOM)≤ 0.8,且无跨模块的循环依赖。
- 接口归属明确:所有对外API的OpenAPI文档均标注所属模块及运维联系人,超过90%的API拥有版本策略。
- 信任链路完备:100%的跨模块API在测试环境经过鉴权验证,无未鉴权端点。
- 变更影响范围:任意模块修改其内部逻辑后,无需改动其他模块的API调用代码(除非发布新版本)。
- 验收测试通过率:边界场景(如超时、非法、数据缺失)的自动化测试覆盖率达到95%以上。
总结:模块边界的定义不是一次性设计,而是随业务演进而不断调整的维度。第2章给出的内聚耦合度量、职责分配规则和接口归属决策,为后续的API设计、版本管理及治理提供了可重复的判据。