当前位置: 首页 > news >正文

DDD-015:领域事件(Domain Event

DDD-015:领域事件(Domain Event)

15.1 领域事件的定义与用途

15.1.1 什么是领域事件?

【原理】
领域事件(Domain Event)是领域中已经发生的、对业务有重要意义的事实。它表示领域内发生的某件事情,通常会导致系统状态的改变或触发其他业务操作。

领域事件的核心特征:

  • 过去时态命名:OrderCreated、PaymentReceived(表示已发生)
  • 不可变性:事件一旦发生,其内容不可更改
  • 业务含义:携带业务相关的数据
  • 时效性:包含事件发生的时间
  • 因果链:一个事件可能触发其他事件

15.1.2 领域事件 vs 系统事件

【对比分析】

维度领域事件系统事件
来源业务领域技术层
命名业务术语(OrderCreated)技术术语(CacheInvalidated)
消费者业务模块技术模块
数据业务数据技术数据
目的业务解耦、流程协调技术解耦、性能优化
// ✅ 领域事件:业务含义明确publicclassOrderCreatedEventextendsDomainEvent{privatefinalOrderIdorderId;privatefinalCustomerIdcustomerId;privatefinalMoneytotalAmount;privatefinalLocalDateTimecreatedAt;}// ❌ 系统事件:技术层面publicclassCacheInvalidatedEvent{privatefinalStringcacheKey;// 这是技术事件,不是领域事件}

15.1.3 领域事件的用途

【核心用途】

用途说明示例
解耦聚合聚合间不直接调用,通过事件通信下单后扣库存
状态同步跨聚合、跨系统状态同步订单完成后更新用户积分
触发流程启动后续业务流程支付成功后触发发货
审计追踪记录业务操作历史记录所有订单状态变更
通知通知外部系统或用户订单取消后发送短信

15.2 领域事件的命名与设计

15.2.1 命名规范

【代码示例】

// ✅ 正确命名:过去时 + 业务对象 + 动作OrderCreatedEvent// 订单已创建PaymentReceivedEvent// 支付已收到OrderShippedEvent// 订单已发货CustomerUpgradedEvent// 客户已升级InventoryReservedEvent// 库存已预留// ❌ 错误命名CreateOrderEvent// 应该是过去时OrderCreateEvent// 应该是过去时OrderEvent// 太模糊,不明确发生了什么OrderStatusChangedEvent// 可以接受,但更具体更好

15.2.2 事件内容设计

【代码示例】

/** * 订单已创建事件 */publicclassOrderCreatedEventextendsDomainEvent{// ========== 事件标识 ==========privatefinalStringeventId;// 事件唯一IDprivatefinalLocalDateTimeoccurredOn;// 事件发生时间privatefinalStringeventType="OrderCreated";// 事件类型// ========== 业务数据 ==========privatefinalOrderIdorderId;// 聚合IDprivatefinalCustomerIdcustomerId;// 客户IDprivatefinalMoneytotalAmount;// 订单金额privatefinalList<OrderItemInfo>items;// 商品列表摘要// ========== 元数据 ==========privatefinalStringtriggeredBy;// 触发者privatefinalintversion=1;// 事件版本// 构造函数publicOrderCreatedEvent(OrderIdorderId,CustomerIdcustomerId,MoneytotalAmount,List<OrderItemInfo>items,StringtriggeredBy){this.eventId=UUID.randomUUID().toString();this.occurredOn=LocalDateTime.now();this.orderId=orderId;this.customerId=customerId;this.totalAmount=totalAmount;this.items=newArrayList<>(items);this.triggeredBy=triggeredBy;}// Getters(不提供Setters,保证不可变)publicStringgetEventId(){returneventId;}publicLocalDateTimegetOccurredOn(){returnoccurredOn;}publicOrderIdgetOrderId(){returnorderId;}publicCustomerIdgetCustomerId(){returncustomerId;}publicMoneygetTotalAmount(){returntotalAmount;}publicList<OrderItemInfo>getItems(){returnCollections.unmodifiableList(items);}publicStringgetTriggeredBy(){returntriggeredBy;}publicStringgetEventType(){returneventType;}publicintgetVersion(){returnversion;}}/** * 订单商品信息(值对象) */publicclassOrderItemInfoimplementsSerializable{privatefinalProductIdproductId;privatefinalStringproductName;privatefinalintquantity;// 构造函数、Getters...}

15.3 事件的发布与订阅

15.3.1 事件基类设计

【代码示例】

/** * 领域事件基类 */publicabstractclassDomainEventimplementsSerializable{privatestaticfinallongserialVersionUID=1L;// 事件唯一标识protectedStringeventId;// 事件发生时间protectedLocalDateTimeoccurredOn;// 事件类型(默认取类名)protectedStringeventType;// 事件版本(用于兼容性)protectedintversion=1;protectedDomainEvent(){this.eventId=UUID.randomUUID().toString();this.occurredOn=LocalDateTime.now();this.eventType=this.getClass().getSimpleName();}// GetterspublicStringgetEventId(){returneventId;}publicLocalDateTimegetOccurredOn(){returnoccurredOn;}publicStringgetEventType(){returneventType;}publicintgetVersion(){returnversion;}/** * 转换为JSON字符串(用于序列化) */publicStringtoJson(){try{ObjectMappermapper=newObjectMapper();mapper.registerModule(newJavaTimeModule());returnmapper.writeValueAsString(this);}catch(JsonProcessingExceptione){thrownewRuntimeException("Failed to serialize event",e);}}}/** * 领域事件发布者接口 */publicinterfaceDomainEventPublisher{/** * 发布单个事件 */voidpublish(DomainEventevent);/** * 批量发布事件 */voidpublishAll(List<DomainEvent>events);}/** * 领域事件订阅者接口 */publicinterfaceDomainEventSubscriber<TextendsDomainEvent>{/** * 处理事件 */voidhandle(Tevent);/** * 订阅的事件类型 */Class<T>subscribedToEventType();}

15.3.2 聚合根支持事件发布

【代码示例】

/** * 聚合根基类 - 支持领域事件 */publicabstractclassAggregateRoot<IDextendsIdentifier>extendsEntity<ID>{// 暂存的领域事件privatefinalList<DomainEvent>domainEvents=newArrayList<>();protectedAggregateRoot(IDid){super(id);}/** * 注册领域事件 */protectedvoidregisterEvent(DomainEventevent){this.domainEvents.add(event);}/** * 获取所有领域事件(只读) */publicList<DomainEvent>getDomainEvents(){returnCollections.unmodifiableList(domainEvents);}/** * 是否有领域事件 */publicbooleanhasDomainEvents(){return!domainEvents.isEmpty();}/** * 清空领域事件(发布后调用) */publicvoidclearDomainEvents(){this.domainEvents.clear();}}/** * 订单聚合根 - 发布事件 */publicclassOrderextendsAggregateRoot<OrderId>{privateOrderIdid;privateOrderStatusstatus;privateMoneytotalAmount;// 创建订单publicstaticOrdercreate(CustomerIdcustomerId){Orderorder=newOrder(OrderId.generate(),customerId);order.registerEvent(newOrderCreatedEvent(order.id,customerId,order.totalAmount,Collections.emptyList(),"system"));returnorder;}
http://www.rkmt.cn/news/1458575.html

相关文章:

  • 百色市2026年最新黄金回收白银回收铂金回收门店排行榜及联系方式电话推荐 - 盛世金银回收
  • 13000黄大年茶思屋榜文第130期——珠峰会战第七期:五大技术难题全量整理
  • 用MiniMax M2.7替代BI工程师:真实业务场景下的低代码数据查询实践
  • Claude 3.7 vs GPT-4o真实数据管道实战对比
  • SRAM加速LLM推理:LUT-GEMV算法与硬件架构设计
  • SpringBoot+Vue大学生英语学习平台源码+论文
  • 保姆级教程:手把手教你修改FFmpeg源码,让ffplay也能播H265的RTMP直播流
  • 莫瑶教育AI全域课程:重构AI时代竞争力,从职场提效到商业变现的系统化成长方案 - 全国职业学校推荐官
  • 从 ChatMemory 到 Mem0:我终于理解了 Agent 里的“记忆”到底是什么
  • 通达信缠论插件:3分钟掌握专业级K线分析技术
  • 摆脱无效内卷,做好项目管理的实用思路
  • 华为AI眼镜深度解析:31克无感终端与豆包AI引擎的技术突破
  • 告别重复造轮子:用快马高效生成unet变体,加速你的图像分割模型迭代
  • QQ空间历史说说一键导出终极指南:免费获取你的青春回忆
  • Halcon 23.11实战:用自带果汁瓶图片5分钟搞定你的第一个深度学习缺陷检测模型
  • 告别裸机延时!在STM32CubeIDE里用HAL库定时器给DS18B20写个优雅的驱动
  • 零基础本地运行Gemma 4B:Ollama+GGUF极简部署指南
  • LoRa模块功耗优化实战:让SX1261在电池供电下多跑一年(含睡眠、CAD唤醒配置)
  • Claude Code 完全实战指南 - 第一章:安装配置与本地大模型
  • 别再只玩ChatGPT了!手把手教你用AutoGen搭建你的第一个AI Agent(附完整代码)
  • OpenClaw ACPX 配置实战:打通 OpenCode 调用的上下文绑定关键路径
  • 别再只盯着M.2了!老设备升级4G上网,用MiniPCIe接口的4G模块真香(附AM400P实测)
  • 踩坑实录:poi-tl处理Word模板分页与图片时,我遇到的3个坑及解决方案
  • 【Azure App Service】应用服务中的SNAT (Source Network Address Translation 源网络地址转化)
  • 【深入理解计算机系统】第一章(计算机系统漫游)笔记
  • ssm员工在线知识培训考试平台(10153)
  • 从Copilot到Agent:我的团队如何用ChatDev在3天内“自动化”了一个内部工具
  • ESP8266从联网到传数据:一条AT指令搞定WiFi连接与TCP通信(实战避坑)
  • Android混合开发避坑指南:WebView与H5通信的5种姿势与安全实践
  • DDD-013:仓储(Repository)