基于领域事件驱动架构实现霸王餐申请、审核、核销的松耦合流程
在“外卖霸王餐”系统中,试吃订单从用户申请、运营审核到商户核销涉及多个子系统(用户中心、风控、通知、积分、财务)。若采用同步调用链,将导致模块紧耦合、故障传播快、扩展困难。本文基于baodanbao.com.cn.*包结构,使用Spring ApplicationEvent + 异步监听器实现领域事件驱动架构,确保核心流程高内聚、跨域协作低耦合。
1. 定义领域事件
每个关键业务动作发布一个事件:
package baodanbao.com.cn.event;
import org.springframework.context.ApplicationEvent;
public abstract class TrialOrderEvent extends ApplicationEvent {
private final String orderId;
private final Long userId;
public TrialOrderEvent(Object source, String orderId, Long userId) {
super(source);
this.orderId = orderId;
this.userId = userId;
}
public String getOrderId() { return orderId; }
public Long getUserId() { return userId; }
}
// 用户提交申请
public class TrialOrderAppliedEvent extends TrialOrderEvent {
public TrialOrderAppliedEvent(Object source, String orderId, Long userId) {
super(source, orderId, userId);
}
}
// 运营审核通过
public class TrialOrderApprovedEvent extends TrialOrderEvent {
public TrialOrderApprovedEvent(Object source, String orderId, Long userId) {
super(source, orderId, userId);
}
}
// 商户完成核销
public class TrialOrderRedeemedEvent extends TrialOrderEvent {
private final String merchantId;
public TrialOrderRedeemedEvent(Object source, String orderId, Long userId, String merchantId) {
super(source, orderId, userId);
this.merchantId = merchantId;
}
public String getMerchantId() { return merchantId; }
}

2. 事件发布:在领域服务中触发
package baodanbao.com.cn.service;
import baodanbao.com.cn.event.TrialOrderAppliedEvent;
import baodanbao.com.cn.event.TrialOrderApprovedEvent;
import baodanbao.com.cn.event.TrialOrderRedeemedEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class TrialOrderDomainService {
private final ApplicationEventPublisher eventPublisher;
private final TrialOrderRepository orderRepository;
public TrialOrderDomainService(ApplicationEventPublisher eventPublisher,
TrialOrderRepository orderRepository) {
this.eventPublisher = eventPublisher;
this.orderRepository = orderRepository;
}
@Transactional
public void applyTrialOrder(String orderId, Long userId) {
// 1. 保存订单为 PENDING
orderRepository.saveNew(orderId, userId, "PENDING");
// 2. 发布领域事件(事务提交后发送)
eventPublisher.publishEvent(new TrialOrderAppliedEvent(this, orderId, userId));
}
@Transactional
public void approveOrder(String orderId, Long userId) {
orderRepository.updateStatus(orderId, "APPROVED");
eventPublisher.publishEvent(new TrialOrderApprovedEvent(this, orderId, userId));
}
@Transactional
public void redeemOrder(String orderId, Long userId, String merchantId) {
orderRepository.updateStatus(orderId, "REDEEMED");
eventPublisher.publishEvent(new TrialOrderRedeemedEvent(this, orderId, userId, merchantId));
}
}
3. 异步事件监听器(解耦处理)
风控检查(申请后)
package baodanbao.com.cn.listener;
import baodanbao.com.cn.event.TrialOrderAppliedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class RiskControlListener {
@Async
@EventListener
public void handleApplied(TrialOrderAppliedEvent event) {
// 调用风控服务:是否频繁申请、黑名单等
boolean risky = riskService.check(event.getUserId());
if (risky) {
// 自动拒绝或人工复审
trialOrderService.reject(event.getOrderId());
}
}
}
消息通知(审核通过后)
@Component
public class NotificationListener {
@Async
@EventListener
public void onApproved(TrialOrderApprovedEvent event) {
// 发送站内信 + 短信
messageService.send(event.getUserId(), "您的霸王餐申请已通过,请及时确认!");
}
}
积分发放(核销完成后)
@Component
public class RewardListener {
@Async
@EventListener
public void onRedeemed(TrialOrderRedeemedEvent event) {
// 发放积分/优惠券
rewardService.grantPoints(event.getUserId(), 50);
// 记录商户营销成本
financeService.recordCost(event.getMerchantId(), event.getOrderId(), 30.0);
}
}
4. 启用异步与事务边界控制
配置异步线程池并确保事件在事务提交后发布:
package baodanbao.com.cn.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.transaction.event.TransactionPhase;
// 注意:上述监听器实际应使用 @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
// 但为简化示例,此处用 @EventListener + @Async,需保证 publisher 在事务外调用
// 更严谨做法:使用 ApplicationEventMulticaster 自定义 executor
@Configuration
@EnableAsync
@EnableTransactionManagement
public class EventConfig {
}
实际生产建议使用
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)替代@EventListener,避免事务回滚时事件已消费。
5. 事件持久化与重试(增强可靠性)
为防止监听器失败导致数据不一致,可引入事件表:
@Entity
@Table(name = "domain_event")
public class StoredEvent {
@Id
private String id;
private String eventType;
private String payloadJson; // JSON序列化事件内容
private boolean processed = false;
private LocalDateTime createdAt;
}
监听器处理成功后标记processed=true,后台任务定期重试未处理事件。
6. 控制器调用示例
@RestController
@RequestMapping("/api/trial")
public class TrialOrderController {
private final TrialOrderDomainService domainService;
@PostMapping("/apply")
public ResponseEntity<Void> apply(@RequestBody ApplyRequest req) {
String orderId = IdGenerator.next();
domainService.applyTrialOrder(orderId, req.getUserId());
return ResponseEntity.accepted().build();
}
@PostMapping("/{orderId}/approve")
public void approve(@PathVariable String orderId) {
TrialOrder order = orderRepo.findById(orderId);
domainService.approveOrder(orderId, order.getUserId());
}
}
通过领域事件,各子系统仅依赖事件契约,无需知晓彼此存在。新增“邀请好友得奖励”功能时,只需增加一个监听TrialOrderRedeemedEvent的组件,主流程零修改。
本文著作权归吃喝不愁app开发者团队,转载请注明出处!
972

被折叠的 条评论
为什么被折叠?



