Skip to content

新业务开发完整指南 ​

从识别聚合根到各层落地,把「在 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 │
│ - 参数校验 │
│ - 异常处理 │
└─────────────────────────────────────────────────────────────┘

🎨 完整代码示例:订单业务 ​

让我给你展示一个完整的开发流程(简化版):

业务场景:用户下单购买商品 ​

业务流程:

  1. 创建订单

  2. 检查库存

  3. 锁定库存

  4. 调用支付

  5. 支付成功后确认订单

  6. 发送通知

是否需要 Saga? ✅ 是(跨聚合:订单+库存+支付)


开发步骤演示 ​

Step 1: Domain 层 - 值对象 ​

java
// 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 层 - 聚合根 ​

java
// 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 层 - 领域事件 ​

java
// 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) ​

java
// 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 层 - 外部端口接口 ​

java
// 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 ​

java
// 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 层 - 事件监听器 ​

java
// 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 层 - 通知适配器 ​

java
// 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 ​

java
// 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 层)

🎯 总结 ​

你已经完全理解了这套架构的精髓:

  1. 防腐层设计:Domain 层定义接口,Infrastructure 层实现技术细节

  2. 领域事件:用于聚合内的状态通知,异步处理非关键业务

  3. Saga 模式:用于跨聚合的业务编排,保证最终一致性

  4. 端口适配器:技术实现随意替换,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 ​

java
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 ​

java
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(活动+退款)

🎯 总结 ​

你的理解是完全正确的!

  1. 你的项目采用的是流派 2:ApplicationService 主要在 Domain 层

  2. App 层只有特殊的 ApplicationService:

  • PaymentCallbackApplicationService:处理外部回调

  • SagaRecoveryService:基础设施服务

  1. 这种设计的优点:
  • ✅ 领域代码内聚性高

  • ✅ 模块依赖更简单

  • ✅ 更适合微服务拆分

  1. 我之前说的"App 层写 ApplicationService":
  • 那是传统 DDD 的做法(流派 1)

  • 你的项目采用了简化版(流派 2)

  • 两种都是正确的,只是设计理念不同

你的项目架构是非常合理的,特别适合微服务场景!👍

Powered by VitePress