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
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
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
@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
@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
@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
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 层纯粹性
// Domain 层代码完全不知道数据库的存在
activity.cancelActivity(reason); // 只关心业务
activityRepository.update(activity); // 不关心怎么存2. 技术栈可替换
今天:JPA + MySQL
明天:MyBatis + PostgreSQL
后天:MongoDB + Redis
→ Domain 层代码 0 修改!
→ 只需重新实现 ActivityAggregateRepository3. 测试友好
// 单元测试:Mock IActivityAggregateRepository
@Mock
private IActivityAggregateRepository activityRepository;
when(activityRepository.findById(any()))
.thenReturn(Optional.of(mockAggregate));4. 聚合完整性
// ActivityAggregateRepository 负责组装完整聚合
ActivityAggregate aggregate = assembleAggregate(activityPO);
├─ 查询主表:Activity
├─ 查询从表:ActivityMembers
├─ 查询从表:ActivityTags
└─ 组装成完整聚合根✅ 总结:完美的防腐层设计
| 层次 | 文件 | 职责 | 操作对象 |
|---|---|---|---|
| Domain - Saga | ActivityCancellationSaga | 业务流程编排 | ActivityAggregate |
| Domain - Port | IActivityAggregateRepository | 定义持久化契约 | ActivityAggregate |
| Infra - 防腐层1 | ActivityAggregateRepository | PO ↔ Aggregate 转换 | 两者都有 |
| Infra - 防腐层2 | ActivityRepository | 数据库 CRUD | ActivityPO |
| Infra - 转换器 | ActivityConverter | 对象转换逻辑 | 两者都有 |
| Infra - PO | ActivityPO | 数据库映射 | 数据库表 |
这是教科书级别的 DDD 实现!👏