DDD下Generic Repository实现:如何将Domain层仓储接口定义为泛型?
实现方案
1. Domain层定义无持久化依赖的泛型仓储契约
Domain层只声明面向领域模型的泛型接口,完全不涉及任何数据库实体、JPA相关定义:
// Domain层,仅依赖领域模型 public interface DomainRepository<T, ID> { Optional<T> findById(ID id); T save(T domainModel); List<T> findAllByAttribute(String attribute); void deleteById(ID id); }
所有业务的专属仓储接口直接继承该泛型接口即可,比如文章仓储:
// Domain层 public interface ArticleRepository extends DomainRepository<Article, Long> { // 可扩展文章专属的查询方法 }
2. Persistence层定义泛型映射中间基类
在持久化层处理双向映射逻辑,引入两个泛型参数:领域模型类型D、JPA实体类型E,同时依赖你已定义的JpaMappedTypeRepository和泛型转换器:
2.1 定义泛型转换器接口
单独拆分映射逻辑,避免耦合在仓储实现中:
// Persistence层 public interface DomainEntityMapper<D, E> { D toDomain(E entity); E toEntity(D domain); }
可直接集成MapStruct框架自动生成该接口的实现类,无需手写转换逻辑
2.2 实现通用泛型仓储基类
该基类实现Domain层的DomainRepository接口,封装所有通用的映射、持久化操作,避免样板代码:
// Persistence层,@NoRepositoryBean避免Spring直接实例化 @NoRepositoryBean public abstract class AbstractGenericRepositoryImpl<D, E extends AbstractJpaMappedType, ID> implements DomainRepository<D, ID> { private final JpaMappedTypeRepository<E> jpaRepository; private final DomainEntityMapper<D, E> mapper; // 构造注入依赖 protected AbstractGenericRepositoryImpl(JpaMappedTypeRepository<E> jpaRepository, DomainEntityMapper<D, E> mapper) { this.jpaRepository = jpaRepository; this.mapper = mapper; } @Override public Optional<D> findById(ID id) { return jpaRepository.findById((Long) id) // 你的主键固定为Long可直接转换,多类型可调整逻辑 .map(mapper::toDomain); } @Override public D save(D domainModel) { E entity = mapper.toEntity(domainModel); E savedEntity = jpaRepository.save(entity); return mapper.toDomain(savedEntity); } @Override public List<D> findAllByAttribute(String attribute) { return jpaRepository.findAllByAttribute(attribute) .stream() .map(mapper::toDomain) .toList(); } @Override public void deleteById(ID id) { jpaRepository.deleteById((Long) id); } }
3. 具体业务仓储实现
每个业务只需要声明极小量的代码即可完成仓储实现,完全复用通用逻辑:
// Persistence层,文章仓储实现 @Repository public class ArticleRepositoryImpl extends AbstractGenericRepositoryImpl<Article, ArticleJpaEntity, Long> implements ArticleRepository { public ArticleRepositoryImpl(ArticleJpaRepository jpaRepository, ArticleDomainEntityMapper mapper) { super(jpaRepository, mapper); } // 实现ArticleRepository中扩展的专属方法 }
对应的JPA仓储定义和你现有逻辑完全兼容:
// Persistence层 public interface ArticleJpaRepository extends JpaMappedTypeRepository<ArticleJpaEntity> { }
松耦合保障说明
- 领域层、服务层完全不依赖任何持久化相关包,仅依赖Domain层声明的仓储接口,依赖注入时直接注入对应接口即可,不需要感知实现细节
- 映射逻辑完全收敛在持久化层,调整映射规则、更换持久化框架都不会影响上层代码
- 所有通用CRUD逻辑仅需写一次,新增业务时只需要声明对应的JPA实体、Mapper、仓储实现类即可,无重复样板代码
内容的提问来源于stack exchange,提问作者npdrums
相关产品推荐
相关产品推荐

