DDD理论学习系列(9)-- 领域事件
1. 引言
针对官方释义,我们可以理出以下几个要点:
简而言之,领域事件是用来捕获领域中发生的具有业务价值的一些事情。它的本质就是事件,不要将其复杂化。在DDD中,领域事件作为通用语言的一种,是为了清晰表述领域中产生的事件概念,帮助我们深入理解领域模型。 2. 认识领域事件
在这个用例中,“订单支付成功”就是一个领域事件。 考虑一下,在你没有接触领域事件或EDA(事件驱动架构)之前,你会如何实现这个用例。肯定是简单直接的方法调用,在一个事务中分别去调用状态更新方法、扣减库存方法、发送捡货通知方法。这无可厚非,毕竟之前都是这样干的。 那这样设计有什么问题?
那如何解决这些问题?我们可以借助领域事件的力量。
下面我们就来一一深入。 3.建模领域事件如何使用领域事件来解耦呢? 而我们要如何封装呢? 3.1. 抽象事件源事件源应该至少包含事件发生的时间和触发事件的对象。我们提取 /// <summary>
通过实现 3.2. 抽象事件处理针对事件处理,我们提取一个 /// <summary>
/// 定义事件处理器公共接口,所有的事件处理都要实现该接口
/// </summary>
IEventHandler
{
事件处理要与事件源进行绑定,所以我们再来定义一个泛型接口: /// <summary>
/// 泛型事件处理器接口
/// </summary>
/// <typeparam name="TEventData"></typeparam>
interface IEventHandler<TEventData> : IEventHandler where TEventData : IEventData
{ ? ? /// <summary>
? ? /// 事件处理器实现该方法来处理事件
? ? /// </summary>
? ? /// <param name="eventData"></param>
? ? void HandleEvent(TEventData eventData);
}
以上,我们就完成了领域事件的抽象。在代码中我们通过实现一个 3.3. 领域事件的发布和订阅领域事件不是无缘无故产生的,它有一个发布方。同理,它也要有一个订阅方。 那如何和订阅和发布领域事件呢? 事件总线是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需要相互依赖,达到一种解耦的目的。Event Bus就相当于一个介于Publisher(发布方)和Subscriber(订阅方)中间的桥梁。它隔离了Publlisher和Subscriber之间的直接依赖,接管了所有事件的发布和订阅逻辑,并负责事件的中转。 这里就简要说明一下事件总线的实现的要点:
最后,我们看下事件总线的接口定义: IEventBus
{ ?
在应用服务和领域服务中,我们都可以直接调用 而关于事件总线的具体实现,可参考我的这篇博文——事件总线知多少。 4. 最终一致性说到一致性,我们要先搞明白下面几个概念。 事务一致性
我们用一张图来理解一下:
数据一致性 领域一致性 回到我们的案例,当支付成功后,更新订单状态,扣减库存,并发送捡货通知。按照我们以往的做法,为了维护订单和库存的数据一致性,我们将这三个操作放到一个应用服务去做(因为应用服务管理事务),事务的一致性可以保证要么全部成功要么全部失败。但是,试想一下,客户支付成功后,订单依旧为待付款状态,这会引起纠纷。另外,由于库存没有及时扣减,很可能会导致库存超卖。怎么办呢? 最终一致性 对于常见于分布式系统的最终一致性工作流中,客户同样在系统中执行一个命令,但这个系统只为维护事务中的领域一致性运行部分的操作,剩余的操作在允许延后执行。针对上图的结果:
而针对我们的案例,我们如何使用领域事件来进行事务拆分呢?我们看下下面这张图你就明白了。 分析一下,针对我们案例,我们发现一个用例需要修改多个聚合根的情况,并且不同的聚合根还处于不同的限界上下文中。其中订单和库存均为聚合根,分别属于订单系统和库存系统。我们可以这样做:
通过这种方式,我们即保证了聚合的原则,又保证了数据的最终一致性。 5. 事件存储和事件溯源关于事件存储(Event Store)和事件溯源(Event Sourcing)是一个比较复杂的概念,我们这里就简单介绍下,不做过多展开,后续再设章节详述。 事件存储,顾名思义,即事件的持久化。那为什么要持久化事件?
源代码管理工具我们都用过,如Git、TFS、SVN等,通过记录文件每一次的修改记录,以便我们跟踪每一次对源代码的修改,从而我们可以随时回滚到文件的指定修改版本。 事件溯源的本质亦是如此,不过它存储的并非聚合每次变化的结果,而是存储应用在该聚合上的历史领域事件。当需要恢复某个状态时,需要把应用在聚合的领域事件按序“重放”到要恢复状态对应的领域事件为止。 6.总结经过上面的分析,我们知道引入领域事件的目的主要有两个,一是解耦,二是使用领域事件进行事务的拆分,通过引入事件存储,来实现数据的最终一致性。 最后,对于领域事件,我们可以这样理解: 以上,仅是个人理解,DDD水很深,剪不断,理还乱,有问题或见解,欢迎指正交流。
相关文章
原文地址:http://www.cnblogs.com/sheng-jie/p/7124727.html .NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |