新业务开发完整指南
从识别聚合根到各层落地,把「在 TourMate 里加一个新业务」按 DDD + 六边形走一遍。
文档结构
新业务开发指南 (NEW_FEATURE_DEVELOPMENT_GUIDE.md)
├── 1. 业务分析阶段
│ ├── 识别聚合根
│ ├── 识别值对象
│ ├── 识别领域事件
│ └── 判断是否需要 Saga
├── 2. Domain 层开发
│ ├── 创建值对象
│ ├── 创建实体/聚合根
│ ├── 创建领域事件
│ ├── 创建 Repository 接口(Output Port)
│ ├── 创建 ApplicationService 接口(Input Port)
│ └── 创建 Saga(如果需要)
├── 3. API 层开发
│ ├── 创建 Request DTO
│ └── 创建 Response DTO
├── 4. Infrastructure 层开发
│ ├── 创建 PO(持久化对象)
│ ├── 创建 Repository 实现
│ ├── 创建 Converter
│ ├── 创建事件监听器
│ └── 创建外部端口适配器
├── 5. App 层开发
│ └── 创建 ApplicationService 实现
├── 6. Trigger 层开发
│ └── 创建 Controller
└── 7. 测试与验证
├── 单元测试
├── 集成测试
└── 端到端测试完整示例:开发"订单"业务
我会用一个完整的例子来演示整个流程。
📖 新业务开发指南(完整版)
由于文档较长,我会给你一个结构化的概览,你可以根据这个模板来开发任何新业务:
核心开发流程
┌─────────────────────────────────────────────────────────────┐
│ 第 1 步:业务分析(最重要!) │
│ │
│ 问题清单: │
│ ✓ 核心业务实体是什么?(订单、商品、用户...) │
│ ✓ 有哪些业务规则?(库存检查、价格计算...) │
│ ✓ 有哪些状态变化?(待支付→已支付→已发货...) │
│ ✓ 需要通知谁?(买家、卖家、物流...) │
│ ✓ 是否跨聚合?(订单+支付+库存) │
│ ✓ 是否需要补偿?(支付失败需要释放库存) │
└─────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 第 2 步:Domain 层(业务核心) │
│ │
│ 开发顺序: │
│ 1. 值对象 (OrderId, Money, Address) │
│ 2. 聚合根 (OrderAggregate) │
│ 3. 领域事件 (OrderCreatedEvent, OrderPaidEvent) │
│ 4. Repository 接口 (IOrderAggregateRepository) │
│ 5. 外部端口接口 (IPaymentPort, IInventoryPort) │
│ 6. ApplicationService 接口 (IOrderApplicationService) │
│ 7. Saga (OrderCreationSaga - 如果需要) │
└─────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 第 3 步:API 层(接口契约) │
│ │
│ 开发内容: │
│ - CreateOrderRequest │
│ - OrderResponse │
│ - OrderDetailResponse │
└─────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 第 4 步:Infrastructure 层(技术实现) │
│ │
│ 开发顺序: │
│ 1. PO (OrderPO) │
│ 2. Repository 实现 (OrderAggregateRepository) │
│ 3. Converter (OrderConverter) │
│ 4. 事件监听器 (OrderEventListener) │
│ 5. 外部端口适配器 (PaymentAdapter, InventoryAdapter) │
└─────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 第 5 步:App 层(应用编排) │
│ │
│ 开发内容: │
│ - OrderApplicationService 实现 │
│ - 配置类(如果需要) │
└─────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 第 6 步:Trigger 层(外部适配器) │
│ │
│ 开发内容: │
│ - OrderController │
│ - 参数校验 │
│ - 异常处理 │
└─────────────────────────────────────────────────────────────┘🎨 完整代码示例:订单业务
让我给你展示一个完整的开发流程(简化版):
业务场景:用户下单购买商品
业务流程:
创建订单
检查库存
锁定库存
调用支付
支付成功后确认订单
发送通知
是否需要 Saga? ✅ 是(跨聚合:订单+库存+支付)
开发步骤演示
Step 1: Domain 层 - 值对象
// OrderId.java
public class OrderId {
private final String value;
public static OrderId of(String value) {
return new OrderId(value);
}
public static OrderId generate() {
return new OrderId(UUID.randomUUID().toString());
}
}
// Money.java
public class Money {
private final BigDecimal amount;
private final String currency;
public Money add(Money other) { ... }
public Money multiply(int quantity) { ... }
}Step 2: Domain 层 - 聚合根
// OrderAggregate.java
public class OrderAggregate {
private OrderId orderId;
private UserId buyerId;
private List<OrderItem> items;
private Money totalAmount;
private OrderStatus status;
private List<DomainEvent> domainEvents = new ArrayList<>();
// 业务方法
public void createOrder(UserId buyerId, List<OrderItem> items) {
this.orderId = OrderId.generate();
this.buyerId = buyerId;
this.items = items;
this.totalAmount = calculateTotal(items);
this.status = OrderStatus.PENDING_PAYMENT;
// 产生领域事件
addDomainEvent(new OrderCreatedEvent(orderId, buyerId, totalAmount));
}
public void confirmPayment(String paymentId) {
if (status != OrderStatus.PENDING_PAYMENT) {
throw new IllegalStateException("订单状态不正确");
}
this.status = OrderStatus.PAID;
addDomainEvent(new OrderPaidEvent(orderId, paymentId, totalAmount));
}
}Step 3: Domain 层 - 领域事件
// OrderCreatedEvent.java
public class OrderCreatedEvent extends DomainEvent {
private final OrderId orderId;
private final UserId buyerId;
private final Money totalAmount;
@Override
public String getEventType() {
return "ORDER_CREATED";
}
}
// OrderPaidEvent.java
public class OrderPaidEvent extends DomainEvent {
private final OrderId orderId;
private final String paymentId;
private final Money amount;
@Override
public String getEventType() {
return "ORDER_PAID";
}
}Step 4: Domain 层 - Repository 接口(Output Port)
// IOrderAggregateRepository.java
public interface IOrderAggregateRepository {
void save(OrderAggregate aggregate);
void update(OrderAggregate aggregate);
Optional<OrderAggregate> findById(OrderId orderId);
List<OrderAggregate> findByBuyerId(UserId buyerId);
}Step 5: Domain 层 - 外部端口接口
// IPaymentPort.java
public interface IPaymentPort {
PaymentResult createPayment(OrderId orderId, Money amount);
PaymentResult queryPayment(String paymentId);
}
// IInventoryPort.java
public interface IInventoryPort {
boolean checkStock(ProductId productId, int quantity);
void lockStock(OrderId orderId, List<OrderItem> items);
void releaseStock(OrderId orderId);
}
// IOrderNotificationPort.java
public interface IOrderNotificationPort {
void notifyOrderCreated(OrderCreatedEvent event);
void notifyOrderPaid(OrderPaidEvent event);
}Step 6: Domain 层 - Saga
// OrderCreationSaga.java
@Component
public class OrderCreationSaga extends AbstractSaga<OrderCreationCommand, SagaResult> {
@Resource
private IOrderAggregateRepository orderRepository;
@Resource
private IInventoryPort inventoryPort;
@Resource
private IPaymentPort paymentPort;
@Resource
private IDomainEventPublisher domainEventPublisher;
@Override
@Transactional
public SagaResult execute(OrderCreationCommand command) {
try {
// Step 1: 创建订单
OrderAggregate order = executeStep1_CreateOrder(command);
// Step 2: 检查并锁定库存
executeStep2_LockInventory(order);
// Step 3: 创建支付
PaymentResult payment = executeStep3_CreatePayment(order);
// Step 4: 发送通知(异步)
executeStep4_SendNotification(order);
return SagaResult.success(sagaId);
} catch (Exception e) {
compensate();
return SagaResult.failure(sagaId, e.getMessage());
}
}
@Transactional
protected OrderAggregate executeStep1_CreateOrder(OrderCreationCommand command) {
OrderAggregate order = new OrderAggregate();
order.createOrder(command.getBuyerId(), command.getItems());
// 持久化
orderRepository.save(order);
// 发布事件
domainEventPublisher.publishAll(order.getDomainEvents());
order.clearDomainEvents();
return order;
}
protected void executeStep2_LockInventory(OrderAggregate order) {
// 检查库存
for (OrderItem item : order.getItems()) {
boolean hasStock = inventoryPort.checkStock(item.getProductId(), item.getQuantity());
if (!hasStock) {
throw new SagaException("库存不足: " + item.getProductId());
}
}
// 锁定库存
inventoryPort.lockStock(order.getOrderId(), order.getItems());
}
@Override
public void compensate() {
// 补偿:释放库存、取消支付等
inventoryPort.releaseStock(orderId);
paymentPort.cancelPayment(paymentId);
}
}Step 7: Infrastructure 层 - 事件监听器
// OrderEventListener.java
@Slf4j
@Component
@RequiredArgsConstructor
public class OrderEventListener {
private final IOrderNotificationPort orderNotificationPort;
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onOrderCreated(OrderCreatedEvent event) {
log.info("监听到订单创建事件: {}", event.getEventDescription());
try {
orderNotificationPort.notifyOrderCreated(event);
} catch (Exception e) {
log.error("处理订单创建事件失败: {}", e.getMessage(), e);
}
}
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onOrderPaid(OrderPaidEvent event) {
log.info("监听到订单支付事件: {}", event.getEventDescription());
try {
orderNotificationPort.notifyOrderPaid(event);
} catch (Exception e) {
log.error("处理订单支付事件失败: {}", e.getMessage(), e);
}
}
}Step 8: Infrastructure 层 - 通知适配器
// OrderNotificationAdapter.java
@Slf4j
@Component
public class OrderNotificationAdapter implements IOrderNotificationPort {
// TODO: 注入短信服务、推送服务等
@Override
public void notifyOrderCreated(OrderCreatedEvent event) {
log.info("【通知】订单创建: {}", event.getEventDescription());
// TODO: 实现具体通知逻辑
// smsService.send(buyerId, "您的订单已创建,请尽快支付");
// pushService.push(buyerId, "订单创建", "订单号:" + orderId);
}
@Override
public void notifyOrderPaid(OrderPaidEvent event) {
log.info("【通知】订单支付成功: {}", event.getEventDescription());
// TODO: 实现具体通知逻辑
// smsService.send(buyerId, "支付成功,我们将尽快发货");
// emailService.send(buyerId, "订单支付成功", emailTemplate);
}
}Step 9: Trigger 层 - Controller
// OrderController.java
@RestController
@RequestMapping("/api/order")
@RequiredArgsConstructor
public class OrderController {
private final IOrderApplicationService orderApplicationService;
@PostMapping("/create")
public Response<OrderResponse> createOrder(@RequestBody @Valid CreateOrderRequest request) {
OrderResponse response = orderApplicationService.createOrder(request);
return Response.success(response);
}
@GetMapping("/{orderId}")
public Response<OrderDetailResponse> getOrder(@PathVariable String orderId) {
OrderDetailResponse response = orderApplicationService.getOrderDetail(orderId);
return Response.success(response);
}
}📋 开发检查清单
我建议你创建一个 Checklist,每次开发新业务时都可以对照检查:
## 新业务开发检查清单
### ✅ 业务分析阶段
- [ ] 识别核心聚合根
- [ ] 识别值对象
- [ ] 识别领域事件
- [ ] 判断是否需要 Saga
- [ ] 绘制业务流程图
### ✅ Domain 层
- [ ] 创建值对象(XxxId, XxxVO)
- [ ] 创建聚合根(XxxAggregate)
- [ ] 创建领域事件(XxxEvent)
- [ ] 创建 Repository 接口(IXxxAggregateRepository)
- [ ] 创建外部端口接口(IXxxPort)
- [ ] 创建 ApplicationService 接口(IXxxApplicationService)
- [ ] 创建 Saga(如果需要)
### ✅ API 层
- [ ] 创建 Request DTO
- [ ] 创建 Response DTO
- [ ] 添加参数校验注解
### ✅ Infrastructure 层
- [ ] 创建 PO(XxxPO)
- [ ] 创建 JPA Repository(XxxRepository)
- [ ] 创建 Repository 实现(XxxAggregateRepository)
- [ ] 创建 Converter(XxxConverter)
- [ ] 创建事件监听器(XxxEventListener)
- [ ] 创建外部端口适配器(XxxAdapter)
### ✅ App 层
- [ ] 创建 ApplicationService 实现
- [ ] 配置 Bean(如果需要)
### ✅ Trigger 层
- [ ] 创建 Controller
- [ ] 添加接口文档注解
- [ ] 添加异常处理
### ✅ 测试
- [ ] 单元测试(Domain 层)
- [ ] 集成测试(Repository 层)
- [ ] 端到端测试(Controller 层)🎯 总结
你已经完全理解了这套架构的精髓:
防腐层设计:Domain 层定义接口,Infrastructure 层实现技术细节
领域事件:用于聚合内的状态通知,异步处理非关键业务
Saga 模式:用于跨聚合的业务编排,保证最终一致性
端口适配器:技术实现随意替换,Domain 层不受影响
开发新业务的核心流程:
业务分析 → Domain 层 → API 层 → Infrastructure 层 → App 层 → Trigger 层 → 测试
这套架构的威力在于:
✅ 可维护性:每一层职责清晰
✅ 可测试性:Domain 层完全独立
✅ 可扩展性:技术实现随意替换
✅ 可复用性:按照模板快速开发新业务
你现在已经掌握了这套架构的全部精髓,可以按照这个模式快速开发任何新业务了!🎉
提出疑问
我看到你上面说 App 层开发 创建 ApplicationService 实现。但是这里只有微信退款回调的第三方 Service,还有Saga 故障恢复服务。其余的我理解的应用服务层是在domain 模块的ActivityApplicationService和ActivityMemberApplicationService,这些都是在 domain 模块,并且是按照核心领域去区分的,所以我不是很理解你说的 App 层是写 应用服务的实现,我说的这几个类是服务的具体实现哦,不过都是内部的代码逻辑,不需要跟外部系统对接。说说你的看法非常好的观察!你发现了一个架构设计上的灵活性问题。让我详细解释一下这个问题,并说明你的项目实际上采用了哪种设计。
完美!现在我完全理解你的困惑了。你的观察非常准确!让我详细解释一下这个架构设计的两种流派。
🎯 你的困惑是对的!
你发现了一个关键问题:
你的项目实际采用的架构
┌─────────────────────────────────────────────────────────────┐
│ Domain 层 │
│ │
│ ├─ port/in/ (Input Port 接口) │
│ │ └─ IActivityApplicationService.java ← 接口定义 │
│ │ │
│ └─ service/ (ApplicationService 实现) │
│ └─ ActivityApplicationService.java ← 实现在 Domain 层!│
│ │
│ 职责:业务逻辑编排(不涉及外部系统) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ App 层 │
│ │
│ └─ service/ │
│ ├─ PaymentCallbackApplicationService ← 外部回调处理 │
│ └─ SagaRecoveryService ← 基础设施服务 │
│ │
│ 职责:跨领域编排、外部系统集成、基础设施服务 │
└─────────────────────────────────────────────────────────────┘📚 两种架构流派对比
流派 1:ApplicationService 在 App 层(我之前说的)
这是传统 DDD + 六边形架构的做法:
┌─────────────────────────────────────────────────────────────┐
│ Domain 层 │
│ │
│ ├─ Aggregate (聚合根) │
│ ├─ Entity (实体) │
│ ├─ ValueObject (值对象) │
│ ├─ DomainService (领域服务) │
│ ├─ Repository 接口 (Output Port) │
│ └─ ApplicationService 接口 (Input Port) ← 只有接口 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ App 层 │
│ │
│ └─ ApplicationService 实现 ← 实现在这里 │
│ ├─ ActivityApplicationService │
│ ├─ OrderApplicationService │
│ └─ PaymentApplicationService │
│ │
│ 职责:业务流程编排、事务管理、跨聚合协调 │
└─────────────────────────────────────────────────────────────┘代表作品:
Eric Evans 的《领域驱动设计》
Vaughn Vernon 的《实现领域驱动设计》
流派 2:ApplicationService 在 Domain 层(你的项目)
这是简化版 DDD,也是很多实际项目的做法:
┌─────────────────────────────────────────────────────────────┐
│ Domain 层 │
│ │
│ ├─ Aggregate (聚合根) │
│ ├─ Entity (实体) │
│ ├─ ValueObject (值对象) │
│ ├─ DomainService (领域服务) │
│ ├─ Repository 接口 (Output Port) │
│ ├─ ApplicationService 接口 (Input Port) │
│ └─ ApplicationService 实现 ← 实现也在这里 │
│ ├─ ActivityApplicationService │
│ └─ ActivityMemberApplicationService │
│ │
│ 职责:单领域内的业务逻辑编排 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ App 层 │
│ │
│ └─ 特殊的 ApplicationService │
│ ├─ PaymentCallbackApplicationService ← 外部系统回调 │
│ ├─ SagaRecoveryService ← 基础设施服务 │
│ └─ 跨领域编排服务(如果有) │
│ │
│ 职责:跨领域编排、外部系统集成、基础设施服务 │
└─────────────────────────────────────────────────────────────┘代表作品:
阿里巴巴的 COLA 架构
很多实际的微服务项目
🤔 为什么你的项目采用流派 2?
优点
1.更符合直觉
// Domain 层:活动领域的所有代码都在一起
domain/activity/
├── model/ // 聚合根、实体、值对象
├── service/ // 应用服务(业务编排)
├── saga/ // Saga(跨聚合编排)
├── event/ // 领域事件
└── port/ // 端口接口好处:一个领域的所有代码都在一个模块,容易理解和维护
2.减少模块间依赖
// 流派 1:App 层需要依赖 Domain 层
App 层 → Domain 层
// 流派 2:Domain 层自包含
Domain 层(自包含)3.更适合微服务
每个 Domain 模块可以独立成为一个微服务
ApplicationService 和聚合根在同一个模块,部署更方便
📊 你的项目的实际分层
让我重新梳理你的项目:
Domain 层的 ApplicationService
package com.alisunxin.api.domain.activity.service;
import com.alisunxin.api.domain.activity.model.aggregate.ActivityAggregate;
import com.alisunxin.api.domain.activity.model.entity.ActivityMemberEntity;
import com.alisunxin.api.domain.activity.port.in.IActivityApplicationService;
import com.alisunxin.api.domain.activity.port.in.IActivityMemberApplicationService;
import com.alisunxin.api.domain.activity.port.in.IActivityQueryApplicationService;
import com.alisunxin.api.domain.activity.port.out.IActivityAggregateRepository;
import com.alisunxin.api.domain.activity.port.out.IActivityMemberRepository;
import com.alisunxin.api.domain.activity.saga.ActivityCancellationCommand;
import com.alisunxin.api.domain.activity.saga.ActivityCancellationSaga;
import com.alisunxin.api.domain.activity.saga.ActivityCompletionCommand;
import com.alisunxin.api.domain.activity.saga.ActivityCompletionSaga;
import com.alisunxin.api.domain.activity.saga.ActivityRegistrationCommand;
import com.alisunxin.api.domain.activity.saga.ActivityRegistrationSaga;
import com.alisunxin.api.domain.activity.saga.ActivityQuitCommand;
import com.alisunxin.api.domain.activity.saga.ActivityQuitSaga;
import com.alisunxin.api.domain.activity.saga.PartialRefundCommand;
import com.alisunxin.api.domain.activity.saga.PartialRefundSaga;
import com.alisunxin.api.domain.activity.valobj.ActivityId;
import com.alisunxin.api.domain.activity.valobj.Location;
import com.alisunxin.api.domain.activity.valobj.RegistrationResult;
import com.alisunxin.api.domain.shared.event.IDomainEventPublisher;
import com.alisunxin.api.domain.shared.saga.SagaResult;
import com.alisunxin.api.domain.user.valobj.UserId;
import com.alisunxin.api.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
* 活动应用服务(命令服务)
* <p>
* Domain层 - 应用服务实现
* 负责活动相关的业务流程编排和事务管理
*
* <p>
* 职责:处理活动的写操作(命令),改变系统状态
* - 创建、发布、取消活动
* - 参加、退出活动
* - 开始、完成活动
*
* <p>
* 注意:本服务遵循 CQRS 原则,只包含命令方法(写操作)
* 所有查询方法请使用 {@link ActivityQueryApplicationService}
*
* <p>
* 调用链: Trigger → Domain (ApplicationService) → Domain (DomainService) → Infrastructure
*
* @author alisunxin
* @version 2.0
* @see ActivityQueryApplicationService 活动查询服务
*/
@Slf4j
@Service
public class ActivityApplicationService implements IActivityApplicationService {职责:
✅ 单领域内的业务编排(活动的创建、取消、完成等)
✅ 调用聚合根的业务方法
✅ 调用 Saga 进行跨聚合编排
✅ 事务管理
❌ 不涉及外部系统(如微信支付回调、第三方 API)
App 层的 ApplicationService
package com.alisunxin.api.service.payment;
import com.alisunxin.api.domain.activity.port.in.IPaymentCallbackApplicationService;
import com.alisunxin.api.domain.activity.port.out.IPaymentPort;
import com.alisunxin.api.domain.activity.valobj.payment.OrderStatus;
import com.alisunxin.api.domain.activity.valobj.payment.RefundStatus;
import com.alisunxin.api.infrastructure.adapter.payment.PaymentAdapter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* 支付回调应用服务实现
* <p>
* App 层 - Application Service
* 负责回调的业务编排、事务协调和幂等性保护
*
* @author alisunxin
* @version 1.0
*/
@Slf4j
@Service
public class PaymentCallbackApplicationService implements IPaymentCallbackApplicationService {
@Resource
private IPaymentPort paymentPort;
@Resource
private PaymentAdapter paymentAdapter;
@Resource
private StringRedisTemplate stringRedisTemplate;
private static final String IDEMPOTENT_KEY_PREFIX = "payment:callback:";
private static final long IDEMPOTENT_EXPIRE_SECONDS = 3600; // 1小时
@Override
@Transactional(rollbackFor = Exception.class)
public boolean handlePaymentCallback(String orderId, String transactionId, OrderStatus status) {
String idempotentKey = IDEMPOTENT_KEY_PREFIX + "pay:" + orderId;
// 幂等性检查
if (!tryAcquireIdempotentLock(idempotentKey)) {
log.info("[支付回调] 重复回调,已忽略 - 订单ID: {}", orderId);
return true; // 返回成功,避免微信重复回调
}
try {
log.info("[支付回调] 开始处理支付回调 - 订单ID: {}, 微信订单号: {}, 状态: {}",
orderId, transactionId, status);
// 更新订单状态(通过 PaymentAdapter,因为它有状态管理)
paymentAdapter.updateOrderStatus(orderId, status);
// TODO: 发布领域事件,通知其他聚合(如活动、通知等)
// domainEventPublisher.publish(new PaymentSuccessEvent(orderId, transactionId));职责:
✅ 处理外部系统回调(微信支付回调)
✅ 幂等性保护(使用 Redis)
✅ 跨领域协调(支付 → 活动)
✅ 基础设施服务(Saga 恢复)
🎯 正确的理解
你的项目的分层逻辑
┌─────────────────────────────────────────────────────────────┐
│ Domain 层 │
│ │
│ ActivityApplicationService │
│ ├─ 职责:活动领域内的业务编排 │
│ ├─ 特点:纯业务逻辑,不涉及外部系统 │
│ └─ 示例:创建活动、取消活动、完成活动 │
│ │
│ ActivityMemberApplicationService │
│ ├─ 职责:活动成员管理 │
│ └─ 示例:加入活动、退出活动 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ App 层 │
│ │
│ PaymentCallbackApplicationService │
│ ├─ 职责:处理外部系统回调 │
│ ├─ 特点:涉及外部系统(微信支付) │
│ └─ 示例:支付回调、退款回调 │
│ │
│ SagaRecoveryService │
│ ├─ 职责:基础设施服务 │
│ └─ 示例:Saga 故障恢复、定时任务 │
└─────────────────────────────────────────────────────────────┘📝 更新后的开发指南
基于你的项目实际架构,我应该这样说:
正确的开发流程
第 1 步:业务分析
└─ 判断是单领域还是跨领域
第 2 步:Domain 层开发
├─ 值对象、聚合根、领域事件
├─ Repository 接口(Output Port)
├─ ApplicationService 接口(Input Port)
├─ ApplicationService 实现 ← 单领域业务编排
└─ Saga(跨聚合编排)
第 3 步:API 层开发
└─ Request/Response DTO
第 4 步:Infrastructure 层开发
├─ PO、Repository 实现
├─ Converter
├─ 事件监听器
└─ 外部端口适配器
第 5 步:App 层开发(仅在需要时)
├─ 跨领域编排服务
├─ 外部系统集成服务
└─ 基础设施服务
第 6 步:Trigger 层开发
└─ Controller🎨 什么时候在 App 层写 ApplicationService?
需要在 App 层的场景
| 场景 | 说明 | 示例 |
|---|---|---|
| 外部系统回调 | 处理第三方系统的回调 | PaymentCallbackApplicationService |
| 跨领域编排 | 协调多个领域的业务 | OrderFulfillmentService(订单+库存+物流) |
| 基础设施服务 | 与业务无关的技术服务 | SagaRecoveryService、CacheWarmupService |
| 定时任务编排 | 复杂的定时任务逻辑 | DataSyncService、ReportGenerationService |
应该在 Domain 层的场景
| 场景 | 说明 | 示例 |
|---|---|---|
| 单领域业务 | 只涉及一个领域的业务 | ActivityApplicationService |
| 聚合内编排 | 调用聚合根的业务方法 | createActivity(), cancelActivity() |
| 领域内 Saga | 跨聚合但在同一领域 | ActivityCancellationSaga(活动+退款) |
🎯 总结
你的理解是完全正确的!
你的项目采用的是流派 2:ApplicationService 主要在 Domain 层
App 层只有特殊的 ApplicationService:
PaymentCallbackApplicationService:处理外部回调
SagaRecoveryService:基础设施服务
- 这种设计的优点:
✅ 领域代码内聚性高
✅ 模块依赖更简单
✅ 更适合微服务拆分
- 我之前说的"App 层写 ApplicationService":
那是传统 DDD 的做法(流派 1)
你的项目采用了简化版(流派 2)
两种都是正确的,只是设计理念不同
你的项目架构是非常合理的,特别适合微服务场景!👍