Skip to content

A:

我对整个过程不理解的点在于,ActivityJpaRepository的防腐层做在哪,从聚合根repository到 jpa repository,这个过程设计是否合理了,还有就是ActivityJpaRepository里面的写法是否都正确了

Q:

非常好的问题!你提出了 DDD 架构中非常核心的设计疑问。让我详细分析:

问题 1:防腐层在哪里?设计是否合理?

📊 当前的三层架构

┌─────────────────────────────────────────────────────────────┐
│  Domain 层(业务核心)                                        │
│  IActivityAggregateRepository (接口)                         │
│  - 定义业务需要的持久化能力                                   │
│  - 使用领域对象:ActivityAggregate, ActivityId               │
│  - 不关心技术实现                                            │
└─────────────────────────────────────────────────────────────┘
                      ↑ implements
┌─────────────────────────────────────────────────────────────┐
│  Infrastructure/Adapter 层(适配器 - 第一层防腐)            │
│  ActivityAggregateRepository (实现类)                        │
│  - 实现领域接口                                              │
│  - 转换:ActivityAggregate ↔ ActivityPO                     │
│  - 协调:调用 JPA Repository                                 │
│  ⚠️ 这里是第一层防腐层:隔离领域模型和持久化模型              │
└─────────────────────────────────────────────────────────────┘
                      ↓ uses
┌─────────────────────────────────────────────────────────────┐
│  Infrastructure/DAO 层(数据访问 - 第二层防腐)              │
│  ActivityJpaRepository (JPA 接口)                            │
│  - 封装 Spring Data JPA 框架                                 │
│  - 操作数据库表                                              │
│  ⚠️ 这里是第二层防腐层:隔离 JPA 框架和业务代码               │
└─────────────────────────────────────────────────────────────┘
                      ↓ operates
                  Database

🎯 两层防腐层的设计

第一层防腐:ActivityAggregateRepository

java
// 防腐点 1:隔离领域模型和持久化模型
public class ActivityAggregateRepository implements IActivityAggregateRepository {
    private final ActivityJpaRepository activityRepository;
    private final ActivityConverter activityConverter; // 关键:转换器
    
    @Override
    public void save(ActivityAggregate aggregate) {
        // 🛡️ 防腐:Aggregate → PO
        ActivityPO activityPO = activityConverter.aggregateToPO(aggregate);
        
        // 调用 JPA Repository
        activityRepository.save(activityPO);
        
        // 🛡️ 防腐:处理领域事件(JPA 不知道的概念)
        aggregate.clearDomainEvents();
    }
    
    @Override
    public Optional<ActivityAggregate> findById(ActivityId activityId) {
        // 🛡️ 防腐:ActivityId → String
        Optional<ActivityPO> activityPO = activityRepository.findByActivityId(
            activityId.getValue()  // 值对象转换
        );
        
        // 🛡️ 防腐:PO → Aggregate
        return activityPO.map(activityConverter::poToAggregate);
    }
}

防腐职责:

  • ✅ 转换领域对象(Aggregate)↔ 持久化对象(PO)

  • ✅ 转换值对象(ActivityId)↔ 原始类型(String)

  • ✅ 处理领域事件(Domain Event)

  • ✅ 隔离领域逻辑和持久化技术

第二层防腐:ActivityJpaRepository

java
// 防腐点 2:隔离 JPA 框架
@Repository
public interface ActivityJpaRepository extends JpaRepository<ActivityPO, Long> {
    
    // 🛡️ 防腐:封装 JPA 方法命名规则
    Optional<ActivityPO> findByActivityId(String activityId);
    
    // 🛡️ 防腐:封装 JPQL 查询
    @Query("SELECT a FROM ActivityPO a WHERE a.status = 'PUBLISHED'")
    List<ActivityPO> findPublishedActivities();
}

防腐职责:

  • ✅ 封装 Spring Data JPA 框架

  • ✅ 如果换成 MyBatis,只需修改这一层

  • ✅ 隔离 SQL 方言和数据库特性

  • ✅ 提供统一的数据访问接口


问题 2:ActivityJpaRepository 的写法是否正确?

✅ 正确的部分

  1. 方法命名查询(Method Name Query)

    java
    // ✅ 正确:利用 Spring Data JPA 的方法命名规则
    Optional<ActivityPO> findByActivityId(String activityId);
    List<ActivityPO> findByCreatorId(String creatorId);
    List<ActivityPO> findByStatus(String status);

    优点:

    • 不需要写 SQL,JPA 自动生成

    • 简单查询推荐使用

    • 类型安全

  2. @Query 注解(复杂查询)

    java
    // ✅ 正确:复杂查询使用 @Query
    @Query("SELECT a FROM ActivityPO a WHERE a.status = 'PUBLISHED' AND a.isDeleted = false")
    List<ActivityPO> findPublishedActivities();
    
    @Query("SELECT a FROM ActivityPO a WHERE a.status = 'PUBLISHED' AND a.startTime <= :now " +
           "AND a.endTime >= :now AND a.isDeleted = false")
    List<ActivityPO> findOngoingActivities(@Param("now") LocalDateTime now);

    优点:

    • 复杂条件更清晰

    • 可以优化查询性能

    • 支持动态参数

Powered by VitePress