Skip to content

A:

你从 DDD 架构上给我分析下这段代码调用,涉及到聚合根的行为,领域对象,in port ,out port. @ActivityCancellationSaga.java (131-133) findById到了IActivityAggregateRepository,然后又到了ActivityAggregateRepository实现类, @ActivityAggregateRepository.java (75) 然后又是ActivityRepository。发现这中间有几层的防腐层,聚合根的,数据库操作的,你可以分析分析下

Q:

🏗️ DDD 防腐层架构完整分析

📊 调用链路图

┌─────────────────────────────────────────────────────────────┐
│ Domain 层  Saga (业务流程编排)                                │
│ ActivityCancellationSaga.executeStep1_CancelActivity()      
  Step1_CancelActivity()                                      │
│                                                             │
│                                                             │
│   返回:Optional<ActivityAggregate>                          │
└───────────────────┬─────────────────────────────────────────┘
                  out Port (接口)
─────────────────────────────────────────────────────────────┐
│ Domain ────┐
│ Domaint (依赖倒置接口)                      
	IActivityAggregateRepository                                 │
│   + findById(ActivityId): Optional<ActivityAggregate>                              │
│   + update(ActivityAggregate): void                         │
└───────────────────┬─────────────────────────────────────────┘
                    │ 实现

┌─────────────────────────────────────────────────────────────┐
│ Infrastructure 层 - 层 -仓储实现 (防腐层 现 (防腐层            │
│ ActivityAggregateRepository                                  │
│   职责:                                                   │
│   1. PO ↔ Aggregate 转换(通过 ActivityConverter)          │
│   2. 组装聚合根(一对多关系处理)                           │
│   3. 管理聚合根生命周期                               │
│   ↓ 调用                                              │
│   activityRepository.findByActivityId(String)               │
│   返回:Optional<ActivityPO>                                │
└───────────────────┬─────────────────────────────────────────┘
                    │ 依赖

┌─────────────────────────────────────────────────────────────┐
│ Infrastructure 层 - JPA Repository (防腐层 2)                │
│ ActivityRepository extends JpaRepository                     │
│   职责:                                                     │
│   1. 数据库 CRUD 操作                                       │
│   2. 复杂查询(@Query)                                     │
│   3. ORM 映射管理                                           │
│   RM 映射管理                                           │
│    │
│   JPA 查询:SELECT * FROM activity WHERE activity_id = ?    │
└───────────────────┬─────────────────────────────────────────┘

ORM 映射───────────────────────────────────────────────────────────┐
│ Infrastructure 层 - PO (持久化对象)                                             
  @Entity activityPO (数据库表映射)                                                        │
│   @Table(name = "activity")                                  │
└─────────────────────────────────────────────────────────────┘
                ┘   │ 转换

┌─────────────────────────────────────────────────────────────┐
│ Infrastructure 层 - Converter (对象转换器) ActivityConverter                                │
│ ActivityConverteroToAggregate(ActivityPO): ActivityAggregate            ActivityAggregateToPO(ActivityAggregate): ActivityPO            │
└─────────────────────────────────────────────────────────────┘
                    │ 返回

┌─────────────────────────────────────────────────────────────┐
│ Domain 层 - 聚合根 根富领域模型)                             │
│ ActivityAggregate                                            │
│   + cancelActivity(reason): void  ): void               │
│   + getActivityId(): ActivityId   ← 值对象                  │
│   + getDomainEvents(): List       ← 领域事件                │
└─────────────────────────────────────────────────────────────┘

🎯 各层职责分析

1️⃣ Domain 层 - Saga (业务流程编排)

文件:ActivityCancellationSaga.java

java
ActivityAggregate activity = activityRepository
    .findById(ActivityId.of(command.getActivityId()))
    .orElseThrow(() -> new IllegalArgumentException("活动不存在"));

// 领域逻辑:取消活动
activity.cancelActivity(command.getReason());

// 持久化
activityRepository.update(activity);

职责:

  • ✅ 只关心业务流程(取消活动 → 退款 → 通知)

  • ✅ 使用聚合根的业务方法(cancelActivity())

  • ✅ 通过 Output Port 依赖接口,不关心实现

  • ✅ 操作的是领域对象(ActivityAggregate),不是数据库对象


2️⃣ Domain 层 - Output Port (依赖倒置接口)

文件:IActivityAggregateRepository.java

java
public interface IActivityAggregateRepository {
    Optional<ActivityAggregate> findById(ActivityId activityId);
    void save(ActivityAggregate aggregate);
    void update(ActivityAggregate aggregate);
}

职责:

  • ✅ 定义聚合根的持久化契约

  • ✅ 参数和返回值都是领域对象(ActivityAggregate, ActivityId)

  • ✅ 不涉及任何技术细节(无 PO、无 SQL)

  • ✅ 实现依赖倒置原则:Domain 定义接口,Infrastructure 实现


3️⃣ Infrastructure 层 - 聚合根仓储实现 (防腐层 1) ⭐

文件:ActivityAggregateRepository.java

java
@Component
public class ActivityAggregateRepository implements IActivityAggregateRepository {
    
    @Resource
    private ActivityRepository activityRepository;  // JPA Repository
    
    @Resource
    private ActivityConverter activityConverter;    // 对象转换器
    
    @Override
    public Optional<ActivityAggregate> findById(ActivityId activityId) {
        // 1. 调用 JPA Repository 查询 PO
        Optional<ActivityPO> activityPO = activityRepository
            .findByActivityId(activityId.getValue());
        
        if (activityPO.isPresent()) {
            // 2. 通过 Converter 转换为聚合根
            ActivityAggregate aggregate = activityConverter
                .poToAggregate(activityPO.get());
            return Optional.of(aggregate);
        }
        
        return Optional.empty();
    }
}

职责(防腐层核心):

  • ✅ 对象转换:ActivityPO ↔ ActivityAggregate

  • ✅ 聚合组装:处理一对多关系(Activity + Members + Tags)

  • ✅ 技术隔离:Domain 层不知道 PO、JPA、数据库的存在

  • ✅ 接口实现:实现 Domain 层定义的契约

为什么需要这一层?

  • 🛡️ 保护领域模型:Domain 层不被 JPA 注解污染(@Entity, @Table)

  • 🛡️ 技术解耦:可以随时替换 JPA 为 MyBatis、MongoDB,Domain 层不受影响

  • 🛡️ 聚合完整性:负责组装完整的聚合根(主表 + 从表)


4️⃣ Infrastructure 层 - JPA Repository (防腐层 2)

文件:ActivityRepository.java

java
@Repository
public interface ActivityRepository extends JpaRepository<ActivityPO, Long> {
    
    Optional<ActivityPO> findByActivityId(String activityId);
    
    List<ActivityPO> findByCreatorId(String creatorId);
    
    @Query("SELECT a FROM ActivityPO a WHERE ...")
    List<ActivityPO> customQuery(...);
}

职责:

  • ✅ 数据库 CRUD:增删改查操作

  • ✅ SQL 查询:复杂查询、分页、排序

  • ✅ ORM 映射:PO 与数据库表的映射

  • ✅ 只操作 PO:不涉及领域对象

为什么需要这一层?

  • 🛡️ 技术框架隔离:封装 JPA 技术细节

  • 🛡️ 查询优化:可以写原生 SQL、JPQL

  • 🛡️ 批量操作:利用 JPA 的批量能力


5️⃣ Infrastructure 层 - Converter (对象转换器)

文件:ActivityConverter.java

java
@Component
public class ActivityConverter {
    
    // PO → Aggregate
    public ActivityAggregate poToAggregate(ActivityPO po) {
        return ActivityAggregate.builder()
            .activityId(ActivityId.of(po.getActivityId()))
            .creatorId(UserId.of(po.getCreatorId()))
            .title(po.getTitle())
            .location(new Location(po.getAddress(), po.getCity(), ...))
            .build();
    }
    
    // Aggregate → PO
    public ActivityPO aggregateToPO(ActivityAggregate aggregate) {
        ActivityPO po = new ActivityPO();
        po.setActivityId(aggregate.getActivityId().getValue());
        po.setTitle(aggregate.getTitle());
        return po;
    }
}

职责:

  • ✅ 双向转换:PO ↔ Aggregate

  • ✅ 值对象处理:ActivityId, Location ↔ String, JSON

  • ✅ 集合转换:List、Set 等复杂结构

为什么需要这一层?

  • 🛡️ 单一职责:转换逻辑独立,不污染 Repository

  • 🛡️ 可测试性:转换逻辑可以独立测试

  • 🛡️ 复用性:多个 Repository 可以共享 Converter


🎭 两层防腐的意义

防腐层 1:ActivityAggregateRepository

隔离对象:ActivityAggregate ↔ ActivityPO

java
Domain 层看到的:
ActivityAggregate (富领域模型)
  ├─ Location (值对象)
  └─ cancelActivity() (业务行为)

Infrastructure 层实际操作的:
ActivityPO (贫血模型)
  ├─ String activityId
  ├─ String address, city, province
  └─ 只有 getter/setter

防腐层 2:ActivityRepository

隔离技术:JPA ↔ 数据库

ActivityAggregateRepository ggregateRepositoryitory.findByActivityId(String)
  → 返回 ActivityPO

数据库实际执行的:
SELECT * FROM activity PO

数据库实际执行的:
SELECT * FROM activity= false

🌟 核心价值

1. Domain 层纯粹性

java
// Domain 层代码完全不知道数据库的存在
activity.cancelActivity(reason);  // 只关心业务
activityRepository.update(activity);  // 不关心怎么存

2. 技术栈可替换

今天:JPA + MySQL
明天:MyBatis + PostgreSQL
后天:MongoDB + Redis

→ Domain 层代码 0 修改!
→ 只需重新实现 ActivityAggregateRepository

3. 测试友好

java
// 单元测试:Mock IActivityAggregateRepository
@Mock
private IActivityAggregateRepository activityRepository;

when(activityRepository.findById(any()))
    .thenReturn(Optional.of(mockAggregate));

4. 聚合完整性

java
// ActivityAggregateRepository 负责组装完整聚合
ActivityAggregate aggregate = assembleAggregate(activityPO);
  ├─ 查询主表:Activity
  ├─ 查询从表:ActivityMembers
  ├─ 查询从表:ActivityTags
  └─ 组装成完整聚合根

✅ 总结:完美的防腐层设计

层次文件职责操作对象
Domain - SagaActivityCancellationSaga业务流程编排ActivityAggregate
Domain - PortIActivityAggregateRepository定义持久化契约ActivityAggregate
Infra - 防腐层1ActivityAggregateRepositoryPO ↔ Aggregate 转换两者都有
Infra - 防腐层2ActivityRepository数据库 CRUDActivityPO
Infra - 转换器ActivityConverter对象转换逻辑两者都有
Infra - POActivityPO数据库映射数据库表

这是教科书级别的 DDD 实现!👏

Powered by VitePress