You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 19:09:03