何时使用领域驱动设计 (4)

事件驱动型架构通过采用一种发布者-订阅者(Publisher-Subscriber)或者事件流的模型,以异步的形式表达组件之间的关系。在这种架构中,事件产生方生成并发布事件到事件总线(Event Bus),而事件消费方则侦听事件总线并处理它所关心的事件,事件可以被一个或多个消费者所订阅和消费。因此,在事件驱动型架构中,事件产生方并不依赖于事件消费方,事件消费方之间也没有依赖关系。通常情况下,如果你的软件系统需要执行一些比较耗时的任务,而同时又要保证系统响应度的情况下,可以考虑采用事件驱动型架构。比如,IoT系统通常会采用这种架构,因为数据采集与分析都是比较耗时的操作,客户端可以首先发起一个创建数据处理任务的操作,然后通过轮询的方式获得任务的执行状态。 由于在这种架构中,各组件都是相互独立的,因此,这种架构具有很好的延展性(Scalability)和分布式部署的特性;但是,它也有一些实践上的难点,比如:如何确保事件能够被准确、稳定地分发;如何确保事件能够按照一定的顺序被消费方消费;如何确保事件仅被同一消费方消费一次等等。举个例子:在命令查询职责分离(CQRS)体系结构模式的实践中,当一个聚合需要被创建的时候,比如当需要创建一个Student聚合时,从Command这一方可能会产生并发布两个事件:StudentCreatedEvent和StudentNameChangedEvent,分别表示有一个Student聚合已经被创建,并修改了它的Name属性。那么对于事件的订阅方,肯定是希望首先处理StudentCreatedEvent,然后处理StudentNameChangedEvent,如果顺序反了,那就不对了:Student还没有被创建出来,又谈何修改它的Name属性呢?如果你的消息订阅方只有一个实例在运行,你或许可以通过事件的时间戳或者序列号来确定它们的顺序,然后引入一些类似有限状态机(FSM)的机制来保证消息的顺序消费。但如果(其实是绝大多数情况下)你的消息订阅方有多个实例同时运行,那么类似这样的问题就会变得更加复杂。再比如,很多事件驱动系统中,会通过引入成熟的第三方解决方案来确保事件分发的准确性,以保证当消费方没有确切给出一个信号的时候,事件一直都能够被保存在事件总线上以待下一次派发;而对于事件消费方,也会采用一些幂等设计,来保证事件仅被有效处理一次。 接下来我们看一个案例:一个基于命令查询职责分离(Command Query Responsibility Seggregation)体系结构模式所实现的分布式事件驱动型架构,在这个案例中,你可以了解到领域驱动设计是如何指导其设计并被运用在CQRS体系结构模式当中。CQRS体系结构模式最早是由领域驱动设计先锋Greg Young提出,它的架构图大致如下:

何时使用领域驱动设计

(上图来自2018年1月我在微软MVP论坛上的讲义,主题是《ASP.NET Core下领域驱动设计的实践》)

 

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zysxxd.html