浅谈「领域事件」实践

领域事件是DDD,Event Sourcing等架构中的一个重要概念。
基于工作环境中的一些实践经验,我总结了一些粗浅的体会。
文中不会有非常高深复杂的定义,只使用一些基本的概念。

领域事件定义

国外书籍中很少使用下定义的方式解释一个名词的含义。一般都会使用作诠释的方式:“领域事件可以怎样怎样”、“领域事件会带来什么什么”。

让我来下定义的话:领域事件是微服务系统中各服务通信的一种方式。和RPC类似,领域事件用于在服务之间交换信息,但领域事件并不追求实时一致性,仅要求数据的最终一致性。

各服务在一些关键有意义的业务节点完成后会发送领域事件,代表一些关键的动作「已完成」。因此领域事件的命名多采用过去分词,例如「订单已发送Order sent」「订单已取消Order canceled」。

领域事件有多种实现方式。我工作中使用到的方式是:将一些关键的、其他服务可能用到的信息,打包成一个结构体,以消息的形式,通过消息队列中间件,异步传输给系统中订阅该消息的服务。

领域事件带来的收益

介绍领域事件的书籍从不吝惜对领域事件优点的赞美。在此我针对各优点谈谈自己的感受。

增强服务自治性

这大概是领域事件最常被提及的优点了。何谓“自治性”?我的理解是:一个服务只需要关注自己,不与其他服务耦合的程度。单体服务不依赖其他任何服务,自治性最高;如果一个服务调用了大量其他服务的RPC,那么它的稳定性和业务逻辑必定受其他服务的影响,自治性稍差。

为什么能提升自治性?个人认为有以下原因:

首先领域事件一般使用消息队列实现,消息队列是异步的,不会阻塞调用。如果因一时网络原因消息无法传达,那么也会由消息组件重试。领域事件可以看做异步的非阻塞的由第三方保证最终送达的RPC,可靠性比普通的RPC更高。

另一点就是领域事件不必指定消息的接收方是谁。假设我们有这样的业务场景,A服务完成了某一动作后,需要通知B。如果我们使用RPC通信,后面业务扩展时增加了C,D服务需要通知,则实现时我们需要修改A的代码,A的业务逻辑可能还要受到C,D处理结果的影响。但是如果我们使用领域事件进行通信,则在新增C,D服务之后,让它们订阅A发出的事件即可,A服务完全不需要修改,不受其他服务的影响,提升了自治性。

回放系统动作

如果我们把系统看做一个状态机,则领域事件可以看做推动状态机转移状态的输入。领域事件是具有业务含义的,回放某一时间段的领域事件可以复现用户操作。通过数据库binlog回放也可以达到类似的效果,但binlog是没有业务语义的,回放结果没有准确的解读方式。

回放领域事件当然需要某种消息存储/检索机制,这里不做展开。

实践领域事件时的一些思考

  • 领域事件实时性
    领域事件可以保证最终一致性,但不能保证数据实时性。产生的时延是否是业务可以接受的?《实现领域驱动设计精粹》中进行了一个精妙的类比:在软件出现前,传统业务的执行流程是怎样的?流程步骤之间是否会有一定的时间差?如果领域事件导致的时延不超过无软件时业务流程的时延,那么这时延就是可以接受的。当然,实际生产环境中,说服用户接受这样的时延通常不是件容易的事情。

  • 领域事件定义
    领域事件有业务语义,这个业务语义所代表的动作,必须要有明确的定义。例如:在一个招聘系统中,目前只有“现场面试签到”这一个场景。那么这个动作完成以后,应该发出「面试已签到」领域事件,还是发出「现场面试已签到」的领域事件呢?如果是前者,那么后续新增的其他类型面试签到,也要发出这个领域事件。领域事件的业务语义必须得到准确的解释,从命名开始。领域事件表示的动作粒度也要在定义前就想好。

  • 领域事件管理
    目前我们有yAPI这样优秀的接口管理工具,但还没有合适的领域事件管理工具。领域事件和接口都是用于通信的手段,所以他们的含义,包括功能、每个字段含义等,必须得到准确的解释。否则当领域事件增加到一定规模,有人员更迭时,了解各个领域事件将成为恼火的事情。

  • 向领域事件改造
    EDA是一种时髦的架构,但并非所有服务一开始就会选用这种架构。一个单纯由RPC通信的系统,想要转型到通过领域事件通信会遇到很多问题。我们系统遇到的一个现实问题是:太多的接口可能会发送同一种领域事件,如果在所有可能的代码后加上领域事件,很可能造成遗漏。我们系统对此的解决方案是:将领域事件发送收敛到数据库上。业务操作,最终的效果是修改数据库,那么我们就监听相应数据库的binglog,发出相应的领域事件。
    这种解决方案可能会有以下问题:
    首先一个领域事件可能对应不只一张表的修改。为了不遗漏领域事件,我们不得不监听多张表。每个表修改时,我们都会发出领域事件,这就导致业务上同一个领域事件可能会重复发送。幂等操作只能交给接收方来做了。
    监听binlog发送领域事件带来的另一个改造问题是消息中各字段的准确性问题。正常发送领域事件时,在业务处理的过程中就能获取领域事件的各个字段。但一张表上可能不会有完整的领域事件信息,需要反查其他表进行字段填充。某一业务操作会修改t1,t2两张表,A,B两个业务操作前后发生。A操作导致的t1 binlog消息到达了,查询t2表上的字段,此时t2却已经被B操作修改了,则反查出来的结果是B操作后的数据结果。那么拼装消息时,部分字段是A导致的,部分字段是B导致的,就会组装出一个没有真实发生的领域事件,可能对接收方有影响,具体影响还需要接收方想办法化解。

  • 领域事件处理
    处理消息逃不开的三个问题:幂等,重试,丢失。真实写代码时有太多细节需要注意,只能case by case的看了。

你可能感兴趣的:(浅谈「领域事件」实践)