4. 分层架构中的 Repository 定位:领域层、持久化层、应用层间的职责隔离

4.1 业务边界与分层动机

在经典分层架构(应用层 → 领域层 → 持久化层)中,Repository 承担着“领域对象仓储”的语义,而非“数据访问封装”。其核心目的是将领域模型的生命周期管理与底层存储细节彻底分离,使领域层完全不感知数据库、ORM 或任何持久化技术。这种隔离为领域逻辑的可测试性、可演化性提供了基础。

层次 职责 依赖方向 关注点
应用层 编排业务用例、控制事务、调用领域服务 依赖领域层接口(Repository、Service) 请求/响应 DTO、用例流程、权限
领域层 定义核心业务逻辑、实体、值对象、聚合、Repository 接口 零依赖(除领域原语) 业务规则、不变条件、领域事件
持久化层 实现 Repository 接口,完成数据库读写、ORM 映射 依赖领域层接口,可依赖基础设施(数据库驱动) SQL/NoSQL 优化、连接管理、性能
关键原则:领域层中的 Repository 接口仅描述“存入”和“取出”聚合的行为,不出现任何持久化框架特有注解、接口继承或连接对象。持久化层实现必须遵守该接口契约,但可以将 ORM 细节完全封装在实现类内部。

4.2 数据流、控制流与信任链路

理解分层间的控制流向是正确解耦的前提:

  • 控制流(依赖方向):应用层 → 领域层接口(编译时);持久化层 → 领域层接口(编译时)。反转了传统的“上层调用下层”,即依赖倒置原则(DIP)。
  • 数据流(运行时):应用层调用领域层接口获得聚合根;领域层返回纯领域对象;持久化层在实现中完成对象 ↔ 表记录的转换。
  • 信任链路:领域层是“可信核心”,它不信任任何外部注入的实现;持久化层必须履行领域层定义的契约,但领域层不检查持久化层的内部行为。
分层架构中 Repository 的职责隔离与依赖倒置 应用层 Application 编排用例 / 控制事务 领域层(核心) Domain 实体 / 值对象 Entity / Value Object Repository 接口 <interface> Repository 持久化层 Persistence Repository 实现 impl. Repository 数据库 依赖(调用) 实现 信任链路与层间契约 • 领域层定义接口,不依赖任何持久化框架 • 持久化层实现接口并负责ORM/SQL细节 • 应用层只能通过领域接口访问聚合,不可直接引用持久化类

4.3 选型权衡:接口风格与实现策略

在确立分层边界后,Repository 接口的设计方式直接影响耦合度与团队维护成本:

接口风格 优点 缺点 适用场景
集合式
findById/save/delete
语义贴近领域,易测试 复杂查询需规范命名 聚合根较小,CQRS 不频繁
仓储式
getBySpecification(Spec)
查询灵活,避免接口膨胀 需要 Specification 模式支持 大量组合查询、过滤条件多
分类式
IOrderRepository / IUserRepository
按聚合划分清晰 每个聚合增加一个接口文件 大多数分层架构默认选择
常见反模式:在领域层 Repository 接口中暴露 Pageable、Sort、Criteria 等持久化框架专有对象。这些类一旦出现在领域层,隔离便形同虚设。正确做法是将排序、分页等需求表示为领域层自身的值对象或闭包,由持久化层在实现时转换。

4.4 风险点与控制措施

  • 泄漏持久化关注点:领域层出现 @Table、@Column 注解或继承 JpaRepository —— 立即隔离,通过代码审查和依赖检查工具(如 JDepend)阻止。
  • ORM 懒加载陷阱:在应用层或表现层尝试访问未初始化的延迟加载属性,导致 LazyInitializationException。应使用“关闭读写”或“在持久化层完成聚合加载”,可使用专用 DTO 或 @Transactional 边界。
  • 过度抽象:为简单 CRUD 引入泛型 IRepository<T> 导致领域语义丧失。建议为每个聚合定义独立的接口,即使方法签名相似。
  • 事务边界模糊:应用层控制事务,持久化层不应直接开启事务。若持久化层自行提交回滚,会破坏应用层的原子性。使用依赖注入控制事务管理器。

4.5 落地步骤

  1. 抽取接口:为每个聚合根定义领域层 Repository 接口,方法签名使用领域语言(如 findByCustomerId(CustomerId) 而非 getByCondition)。
  2. 创建空实现:在持久化模块中编写实现类,注入数据源或 ORM 会话,完成对象映射。
  3. 依赖注入配置:确保应用层组合根(Composition Root)将持久化实现绑定到领域接口,且领域模块不引用持久化模块。
  4. 编写集成测试:针对持久化实现执行数据库测试,验证映射正确性;领域层 Repository 接口使用模拟对象进行单元测试。
  5. 审查层间引用:使用模块化工具(如 Java 的 module-info.java 或 Maven/Guard)强制领域层不依赖持久化层包。

4.6 验收标准

  • 领域层不引用任何持久化框架(无 JPA、Hibernate、MyBatis 包导入)。
  • Repository 接口方法签名不含 Session、Connection、Pageable 等类型。
  • 持久化层实现类的编译时只依赖领域接口和数据访问框架,不依赖应用层。
  • 替换持久化方案(如从 JPA 换为 MyBatis)时,仅需改动持久化层实现类,领域层和应用层无需修改。
  • 所有对聚合的访问均通过 Repository 接口,不出现直接使用 DAO 或原生 SQL 的情况(性能补救场景除外,但需文档说明并走架构例外流程)。
核心度量:在单元测试中,领域层业务逻辑与持久化实现完全解耦。继承自持久化层的 mock 接口应能在 5 分钟内完成替换,而不影响任何领域测试用例的通过率。