边界与契约
模块边界定义变更时谁需要同步。好边界让团队并行;坏边界表现为「改一行牵十处」或「只能靠集成环境才发现 breakage」。
边界类型
| 类型 | 载体 | 契约形式 | 典型失败 |
|---|---|---|---|
| 进程内模块 | 包、target、namespace | 公开 API、禁止 deep import | 循环依赖 |
| 进程间 | HTTP/gRPC | OpenAPI/Proto + 版本策略 | 隐式字段、破坏性变更 |
| 异步 | 消息、事件 | Schema registry、幂等键 | 重复消费、乱序 |
| 数据 | DB、缓存 | 迁移脚本、读写分离约定 | 共享表直写 |
设计原则
- 按业务能力切,不按技术层切:「计费」「通知」优于「utils」「helpers」大包。
- 契约优先于实现:对外只承诺 schema 与语义;内部可重构。
- 防腐层(ACL):集成第三方或遗留系统时,在边界内翻译模型,禁止外部类型渗入领域。
- 失败隔离:超时、熔断、舱壁;默认值与降级策略写进文档。
注意
分布式单体:多个部署单元共享数据库且互相直写——表面是微服务,实际是紧耦合。优先「每服务自有存储 + 通过 API/事件协作」。
API 版本与兼容
| 策略 | 适用 | 做法 |
|---|---|---|
| 向后兼容扩展 | 公共 REST/JSON | 只加可选字段;不改语义 |
| 显式版本 | 多客户端、长生命周期 | /v1 路径或 header;旧版有下线日 |
| 事件版本 | 消息流 | type + schemaVersion;消费者双读过渡 |
破坏性变更清单(需主版本或协调发布):
- 删除或重命名字段、改类型
- 改错误码语义、鉴权方式
- 缩小可用枚举值
上下文映射(DDD 简表)
| 关系 | 含义 | 集成方式 |
|---|---|---|
| 共享内核 | 同一模型子集 | 共享库(谨慎,控制体积) |
| 客户-供应商 | 上游定 API,下游适配 | 明确 SLA、契约测试 |
| 防腐层 | 模型不兼容 | ACL 翻译 |
| 开放主机 | 通用子域对外 | 文档化公开 API |
| 各行其道 | 无集成 | 不要假集成 |
契约测试(工程落地)
| 角色 | 责任 |
|---|---|
| 提供者 | 发布契约工件(Pact、OpenAPI snapshot) |
| 消费者 | CI 拉取契约跑兼容性;失败阻塞合并 |
| 事件 | 消费方对样例 payload 做契约测试 |
与 CI/CD 衔接:契约失败 = 与编译失败同级。
评审问题
- 新依赖是否跨过边界 import 了「内部」包?
- 超时、重试、幂等是否在边界两侧一致?
- 错误模型是否稳定(机器可读 code + 人可读 message)?
- 是否有「临时」共享 DTO 超过两个模块?