它的主要任务就是将事件消息发送到消息通信渠道

时间:2022-03-13 07:09:17

很长一段时间以来,我都在思考如安在ASP.NET Core的框架下,实现一套完整的事件驱动型架构。这个问题看上去有点大,其实主要方针是为了实现一个基于ASP.NET Core的微处事,它能够非常简单地订阅来自于某个渠道的事件动静,并对接收到的动静进行措置惩罚惩罚,于此同时,它还能够向该渠道发送事件动静,以便订阅该事件动静的消费者能够对动静数据做进一步措置惩罚惩罚。让我们回顾一下微处事之间通信的几种方法,分为同步和异步两种。同步通信最常见的就是RESTful API,而且非常简单轻量,一个Request/Response回环就结束了;异步通信最常见的就是通过动静渠道,将载有特殊意义的数据的事件动静发送到动静渠道,而对某种类型动静感兴趣的消费者,就可以获打动静中所带信息并执行相应操纵,这也是我们对照熟知的事件驱动架构的一种表示形式。虽然事件驱动型架构看起来非常庞大,从微处事的实现来看显得有些沉重,但它的应用范畴确实很广,也为处事间通信供给了新的思路。了解DDD的伴侣相信必然知道CQRS体系布局模式,它就是一种事件驱动型架构。事实上,实现一套完整的、安适的、不变的、正确的事件驱动架构并不简单,由于异步特性带来的一致性问题会非常棘手,甚至需要借助一些根本布局层工具(好比关系型数据库,不错!只能是关系型数据库)来解决一些特殊问题。本文就筹算辅导大家一起探探路,基于ASP.NET Core Web API实现一个相比拟较简单的事件驱动架构,然后引出一些有待深入思考的问题,留在此后的文章中继续讨论。或许,本文所引入的源代码无法直接用于出产环境,但我但愿本文介绍的内容能够给到读者一些启发,并能够辅佐解决实际中遇到的问题。

术语约定

本文会涉及一些相关的专业术语,在此先作约定:

事件:在某一特按时刻产生在某件事物上的一件工作,例如:在我撰写本文的时候,电话铃响了

动静:承载事件数据的实体。事件的序列化/反序列化和传输都以动静的形式进行

动静通信渠道:一种带有动静路由成果的数据传输机制,用以在动静的派发器和订阅器之间进行数据传输

注意:为了迎合描述的需要,不才文中可能会混用事件和动静两个观点。

一个简单的设计

先从简单的设计开始,根基上事件驱动型架构会有事件动静(Events)、事件订阅器(Event Subscriber)、事件派发器(Event Publisher)、事件措置惩罚惩罚器(Event Handler)以及事件总线(Event Bus)等主要组件,它们之间的关系大抵如下:

它的主要任务就是将事件消息发送到消息通信渠道

首先,IEvent接口界说了事件动静(更确切地说,数据)的根基布局,几乎所有的事件城市有一个独一标识符(Id)和一个事件产生的时间(Timestamp),这个时间凡是使用UTC时间作为标准。IEventHandler界说了事件措置惩罚惩罚器接口,显而易见,它包罗两个要领:CanHandle要领,用以确定传入的事件东西是否可被当前措置惩罚惩罚器所措置惩罚惩罚,以及Handle要领,它界说了事件的措置惩罚惩罚过程。IEvent和IEventHandler组成了事件措置惩罚惩罚的根基元素。

然后就是IEventSubscriber与IEventPublisher接口。前者暗示实现该接口的类型为事件订阅器,它卖力事件措置惩罚惩罚器的注册,并侦听来自事件通信渠道上的动静,一旦所获得的动静能够被某个措置惩罚惩罚器措置惩罚惩罚,它就会指派该措置惩罚惩罚器对接收到的动静进行措置惩罚惩罚。因此,IEventSubscriber会连结着对事件措置惩罚惩罚器的引用;而对付实现了IEventPublisher接口的事件派发器而言,它的主要任务就是将事件动静发送到动静通信渠道,以便订阅端能够获得动静并进行措置惩罚惩罚。

IEventBus接口暗示动静通信渠道,也就是大家所熟知的动静总线的观点。它不只具有动静订阅的成果,而且还具有动静派发的能力,因此,它会同时担任于IEventSubscriber和IEventPublisher接口。在上面的设计中,通过接口疏散动静总线的订阅器和派发器的角色是很有须要的,因为两种角色的各自职责不一样,这样的设计同时满足SOLID中的SRP和ISP两个准则。

基于以上根本模型,我们可以很快地将这个东西关系模型转换为C#代码:

public interface IEvent { Guid Id { get; } DateTime Timestamp { get; } } public interface IEventHandler { Task<bool> HandleAsync(IEvent @event, CancellationToken cancellationToken = default); bool CanHandle(IEvent @event); } public interface IEventHandler<in T> : IEventHandler where T : IEvent { Task<bool> HandleAsync(T @event, CancellationToken cancellationToken = default); } public interface IEventPublisher : IDisposable { Task PublishAsync<TEvent>(TEvent @event, CancellationToken cancellationToken = default) where TEvent : IEvent; } public interface IEventSubscriber : IDisposable { void Subscribe(); } public interface IEventBus : IEventPublisher, IEventSubscriber { }

短短30行代码,就把我们的根基东西关系描述清楚了。对付上面的代码我们需要注意以下几点:

这段代码使用了C# 7.1的新特性(default关键字)

Publish以及Handle要领被替换为撑持异步挪用的PublishAsync和HandleAsync要领,它们会返回Task东西,这样可以便利使用C#中async/await的编程模型