模块边界定义原则:业务边界、数据边界、技术边界
模块拆分的首要问题并非“切多细”,而是“切在哪”。错误的切分会带来跨模块耦合、数据乱流、技术栈不一致,最终导致系统难以维护、无法独立部署。本章提出三种相互独立又彼此约束的边界类型——业务、数据、技术,并给出每条边界的判定原则与典型冲突场景。
核心观点:一种良好的边界划分,必须在业务内聚、数据所有权、技术栈隔离三个维度同时成立;缺少任何一维,模块都会在演进过程中退化。
1. 业务边界
业务边界对应领域驱动设计(DDD)的限界上下文。每个模块应完整封装一组紧密关联的业务能力,对外仅暴露接口,内部实现细节对外部不可见。
- 判定原则:同一业务事件的所有参与者(实体、值对象、领域服务)应归属同一模块;不同上下文之间通过领域事件或远程调用协作。
- 典型反例:将“订单创建”与“库存预占”放在同一模块,但“库存扣减”与“商品上架”又在另一模块,导致跨模块事务频繁。
- 落地工具:事件风暴(Event Storming)产出候选聚合根和上下文映射图。
2. 数据边界
数据边界决定了模块对数据的所有权与访问权限。一个模块的数据表、缓存、文件应只被该模块直接修改;其他模块只能通过接口获取或写入授权数据。
- 判定原则:如果两个模块频繁因数据一致性产生跨库查询或同步脚本,说明数据边界切错了。
- 常见策略:独立数据库模式(每个模块一个 Schema / DB)、共享数据库但隔离表空间、事件驱动最终一致性。
- 风险点:数据边界过于严格会导致大量重复数据或接口膨胀;过于松散则退化回单体数据层。
3. 技术边界
技术边界关注模块之间的通信协议、运行时环境和依赖约束。不同模块可以选用不同技术栈,但必须通过稳定、兼容的接口交互。
- 判定原则:如果两个模块的部署、扩缩容、版本升级无法独立完成,则技术边界尚未建立。
- 常见形式:REST / gRPC 协议边界、进程边界(微服务)、语言/Python与Java混合时的FFI边界。
- 典型冲突:业务上属于同一上下文(业务边界正确),但为了性能强行拆成两个RPC服务(技术边界错误),反而导致延迟放大。
四种边界对比
| 维度 | 核心问题 | 错误信号 | 推荐做法 |
|---|---|---|---|
| 业务边界 | 模块职责是否内聚? | 一个功能变更需修改3个以上模块 | 事件风暴 + 上下文映射 |
| 数据边界 | 谁拥有数据写入权? | 跨模块JOIN查询 · 数据同步SLA不达标 | 每个模块独占数据源 |
| 技术边界 | 能否独立部署和扩缩? | A模块升级B模块必须跟着停机 | 协议版本化 · 异步解耦 |
| 综合决策树 | 三边界冲突时以哪个优先? | 发生冲突意味着架构层划分不合理 | 优先业务边界,其次数据,最后技术 |
实践建议:初期设计时先用业务边界划定模块,再为每个模块分配独立数据源,最后根据部署目标确定技术边界。常见的错误是在技术边界(如“必须是微服务”)上先做决定,反过来牵制业务和数据,导致模块频繁跨域通信。
风险点与落地步骤
- 风险①:业务边界正确但数据边界不对——例如订单模块的“已支付订单列表”被外部模块通过直接查表查询,绕过了业务接口。→ 对策:强制所有数据读取走模块暴露的查询API,数据权限由模块控制。
- 风险②:技术边界过细导致模块数量爆炸,维护成本超过收益。→ 对策:允许同一个业务上下文内包含多个模块(如 order-service 和 order-worker),只要它们共享数据边界。
- 风险③:三边界同时出现矛盾且无法调和时,考虑重新划分子域或引入防腐层(Anti-Corruption Layer)。
验收标准(第1章)
- 团队能对任意业务功能说出对应的业务边界、数据边界和技术边界。
- 模块的接口定义清晰声明了数据所有权和协议版本。
- 不存在跨模块的直接数据库访问或共享可变状态。