Repository不能继承AggregateRoot,因为它是基础设施层对数据访问的抽象接口,而AggregateRoot是领域层中封装业务规则与一致性的核心实体,二者分属不同架构层次且职责严格分离。

Java中仓储层不继承AggregateRoot,这是常见误解。Repository 是独立接口,它操作聚合根,但绝不是聚合根的子类或扩展。
为什么 Repository 不能 extends AggregateRoot
聚合根(AggregateRoot)代表业务领域中的一个有生命周期、有身份、需强一致性的核心实体,比如 Order 或 Customer。它的职责是封装业务规则、维护内部一致性、响应领域行为(如 confirm()、cancel())。
而 Repository 是基础设施层对“数据获取与持久化”的抽象,属于访问机制,不是领域对象。让它继承聚合根会严重违反单一职责原则和分层边界:
- 混淆领域逻辑与数据访问逻辑,导致领域模型被技术细节污染
- 破坏四层架构(表现层→应用层→领域层→基础设施层)中领域层的纯粹性
- 使聚合根承担本不属于它的数据存取职责,违背 DDD “聚合根只管领域行为” 的设计约束
- 引发编译错误:JPA/Hibernate 等 ORM 框架要求实体类是普通 POJO 或
@Entity,而 Repository 是接口/实现类,类型系统无法兼容
正确的 Repository 契约设计方式
DDD 推荐的契约结构是:定义泛型接口,限定操作对象为聚合根类型,但不继承它。
立即学习“Java免费学习笔记(深入)”;
- 接口定义在领域层(如
domain包),体现业务意图 - 实现类放在基础设施层(如
infrastructure包),隐藏 JPA、MyBatis 或远程调用等细节 - 泛型参数明确指定该仓储服务哪个聚合根,例如
Repository<Order>
示例接口:
public interface Repository<T extends AggregateRoot> {
T findById(String id);
void save(T aggregate);
void delete(T aggregate);
List<T> findAll();
}
注意:T extends AggregateRoot 是类型约束(表示 T 必须是聚合根类型),不是继承关系;Repository 本身仍是独立接口。
更推荐的实践:按聚合根定制接口
比起泛型通用接口,DDD 更鼓励为每个聚合根定义专属仓储接口,提升可读性与业务表达力:
public interface OrderRepository {
Order findById(OrderId id);
void save(Order order);
void delete(Order order);
List<Order> findByStatus(OrderStatus status);
Optional<Order> findByOrderNumber(String number);
}
好处包括:
- 方法命名直接反映业务语义(如
findByOrderNumber),而非通用 CRUD - 便于在应用服务中精准注入所需仓储,避免泛型擦除带来的运行时不确定性
- 支持聚合特有的查询逻辑(如跨表关联、状态过滤),不强行套用统一模板
- 与 Spring Data JPA 结合时,可自然映射到
JpaRepository<Order, Long>,同时保持领域层无框架依赖
关键设计要点总结
契约设计的核心不是语法技巧,而是分层意识和职责分离:
- 领域层只声明“需要什么数据”,不关心“怎么拿”——所以只有接口,无实现
- 基础设施层负责“怎么拿”,可自由切换 ORM、HTTP 客户端、文件读取器等,不影响领域逻辑
- 聚合根永远不引用 Repository,否则形成循环依赖;数据加载应由应用服务协调,或通过工厂+Repository 组合完成
- 若使用 Spring,用
@Qualifier或接口名区分多个仓储实现(如DbOrderRepository和CacheOrderRepository)



















