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

如何通过Spring Crud/Jpa Repository合规实现领域驱动设计(DDD)

我完全理解你现在的困惑——DDD的分层规范和Spring Data JPA的便捷性好像天生有点冲突,对吧?别担心,这里有两种贴合DDD原则的解决方案,帮你平衡规范和开发效率:

解决DDD领域层仓储接口与Spring JPA依赖的矛盾

核心问题在于:DDD要求领域层必须保持纯净,不能依赖基础设施层的框架代码,但Spring的JpaRepository属于基础设施层的Spring Data组件,直接让领域层的仓储接口继承它会把框架依赖带入领域层,破坏了分层原则。

方案1:领域层定义纯净仓储接口,基础设施层实现(最贴合DDD规范)

这是DDD推荐的标准做法,严格分层,隔离业务与技术细节:

  • 第一步:在领域层定义完全不依赖Spring的纯净仓储接口,只暴露业务需要的方法:
// 领域层代码 - 无任何Spring框架依赖
public interface CustomerRepository {
    Customer findById(Long id);
    void save(Customer customer);
    List<Customer> findAll();
    // 这里只放领域业务需要的查询方法,比如findByEmail(String email)等
}
  • 第二步:在基础设施层创建Spring Data JPA的实现类,继承JpaRepository并实现领域层的CustomerRepository:
// 基础设施层代码 - 依赖Spring Data JPA
@Repository
public class JpaCustomerRepository extends JpaRepository<Customer, Long> implements CustomerRepository {
    // 基础CRUD方法不需要重复实现,JpaRepository已经提供
    // 如果领域层有自定义查询方法,在这里用Spring Data的规则实现(比如方法名、@Query注解)
}
  • 第三步:领域服务中注入领域层的CustomerRepository接口,而非具体的JPA实现,保持领域层的独立性:
// 领域层代码 - 领域服务
@Service
public class CustomerService {
    private final CustomerRepository customerRepository;

    // 构造注入,依赖领域层抽象接口
    public CustomerService(CustomerRepository customerRepository) {
        this.customerRepository = customerRepository;
    }

    // 业务逻辑方法示例
    public Customer registerCustomer(Customer customer) {
        // 先做业务校验,比如邮箱是否已存在
        // ...
        return customerRepository.save(customer);
    }
}

方案2:用@NoRepositoryBean做折中(减少代码重复)

如果觉得定义两层接口太繁琐,可以用Spring Data的@NoRepositoryBean来简化,同时尽量遵守DDD原则:

  • 在领域层定义一个通用的领域仓储标记接口(或包含通用业务方法),标注@NoRepositoryBean:
// 领域层代码
@NoRepositoryBean
public interface DomainRepository<T, ID> {
    T findById(ID id);
    void save(T entity);
    // 其他领域通用的持久化方法...
}
  • 在基础设施层创建继承JpaRepository和DomainRepository的具体仓储接口:
// 基础设施层代码
@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long>, DomainRepository<Customer, Long> {
    // 自定义查询方法,比如findByEmail(String email)
}
  • 领域服务中优先注入DomainRepository<Customer, Long>,如果需要调用Spring Data的特定方法,再注入CustomerRepository(但尽量避免在领域层使用框架特定方法)

关键原则提醒

  • 领域层只关心业务规则和领域概念,绝对不能出现任何与持久化框架相关的代码(比如Spring的注解、JPA的API)
  • 基础设施层负责实现领域层定义的抽象,处理具体的技术细节(不管是JPA、MyBatis还是MongoDB,都不影响领域层)
  • 遵循依赖倒置原则:高层模块(领域层)不依赖低层模块(基础设施层),二者都依赖抽象(领域层的仓储接口)

内容的提问来源于stack exchange,提问作者user1188867

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:17:59