DDD(六)--领域事件
< 返回列表时间: 2019-09-06来源:OSCHINA
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
1、引言
领域事件就字面理解为,领域内已发生的活动,这是一个有具体状态的事件。
2、了解领域事件 将领域中所发生的活动建模成一系列的离散事件。每个事件都用领域对象来表示。领域事件是领域模型的组成部分,表示领域中所发生的事情。
要点:“领域事件是领域模型的组成部分”,通过这句话可以得知,领域事件不会是领域模型,那么在具体代码中领域事件是作为实体还是值对象呢?
事件直接的理解是“已经”发生的活动,是不变,所以值对像是比较好的选择。 领域事件是一个领域模型中极其重要的部分,用来表示领域中发生的事件。忽略不相关的领域活动,同时明确领域专家要跟踪或希望被通知的事情,或与其他模型对象中的状态更改相关联。
要点: 领域事件作为领域模型的重要部分,是领域建模的工具之一。
领域建模即分析业务,而领域事件是领域中一种状态的反应,例如肚子饿了,会通过神经感觉反馈给身体饿了这个信息。而这个信息可以让我们得知领域会做哪些业务。 用来捕获领域中已经发生的事情。
已发生的事情才被称之为事件。 并不是领域中所有发生的事情都要建模为领域事件,要忽略无业务价值的事件。
例如人的身体不会告诉人本身指甲太长了,但是却会通知身体肚子饿了,因为指甲长不影响人的存货,而饿肚子却会被饿死。即有价值才值得建模事件。 领域事件是领域专家所关心的(需要跟踪的、希望被通知的、会引起其他模型对象改变状态的)发生在领域中的一些事情。
同第三点所述一样,有价值才会被关心才能作为领域事件。
简而言之,领域事件是用来捕获领域中发生的具有业务价值的一些事情。它的本质就是事件,不要将其复杂化。在DDD中,领域事件作为通用语言的一种,是为了清晰表述领域中产生的事件概念,帮助我们深入理解领域模型。
3、领域事件的作用
上述中 领域事件是领域专家所关心的(需要跟踪的、希望被通知的、会引起其他模型对象改变状态的)发生在领域中的一些事情。
从这句话可以知道,领域事件所做的是跟踪事件和通知,也就是当发生了某件事情,才去做某一个操作。
例如:
当用户下单,扣除库存。
当用户支付,扣除用户余额。
当用户签收包裹,订单状态变更已签收。
那么,这些都是领域事件吗?回到刚才的话中, “需要跟踪的、希望被通知的、会引起其他模型对象改变状态的” 用户下单扣除库存是即时的操作,不满足加粗字体句中的三个条件。 用户支付扣除余额同理。用户签收包裹变更订单状态,满足会引起其他模型对象改变状态这个条件,所以是领域事件。
所以只有需要被跟踪、被通知、会改变其他模型状态的业务,才需要创建领域事件。
接上面签收例子,当订单状态变更后,商家从第三方平台收到货款,这个时候领域中是最终一致性的。所以部分领域事件是可以保持领域最终一致性。
上述多个例子中可以分析出,无论是变更订单状态,还是商家收到货款,这些都不是具体业务,既然如此,在使用领域事件时应该是能够保持原代码不做业务改变的,保证业务不变,只是对业务结果做出处理。
再者应当是可以解决领域的聚合性问题。DDD中的聚合有一个原则是,在单个事务中,只允许对一个聚合对象进行修改,由此产生的其他改变必须在单独的事务中完成。如果一个业务跨多个聚合对象,领域事件会是一个不错的工具来解决这个问题。通过领域事件的方式可以达到各个组件之间的数据一致性,通过最终一致性取代事务一致性。
4、领域事件建模
那么如何建模领域事件呢? 首先抽象事件源,事件源应该至少包含事件发生的时间和触发事件的对象。 抽象事件处理,事件处理要与事件源进行绑定。 领域事件不是无缘无故产生的,它有一个发布方。同理,它也要有一个订阅方。领域事件的发布可以使用 发布--订阅模式 来实现。
5、事件存储
事件存储,顾名思义,即事件的持久化。那为什么要持久化事件?
简单来说就是记录当时的领域状态。
当事件发布失败时,可用于重新发布。
通过消息中间件去分发事件,提高系统的吞吐量。
用于事件溯源。
源代码管理工具我们都用过,如Git、SVN等,通过记录文件每一次的修改记录,以便我们跟踪每一次对源代码的修改,从而我们可以随时回滚到文件的指定修改版本。
事件溯源的本质亦是如此,不过它存储的并非聚合每次变化的结果,而是存储应用在该聚合上的历史领域事件。当需要恢复某个状态时,需要把应用在聚合的领域事件按序“重放”到要恢复状态对应的领域事件为止。
6、小结
经过上面的分析,我们知道引入领域事件的目的主要有两个,一是解耦,二是实现数据的最终一致性。
最后,对于领域事件,我们可以这样理解:
通过将领域中所发生的活动建模成一系列的离散事件,跟踪事件执行相应的业务逻辑。
也可以简要理解为:领域事件 = 事件发布 + 事件存储 + 事件分发 + 事件处理。
热门排行