一、前言
事件是基于委托的发布-订阅机制(基于windows消息处理机制)通过定义事件,订阅事件、发布事件并且关联业务逻辑,实现系统业务的解耦。发布-订阅机制提供了一种天然的业务解耦方式,所以在系统中使用事件定义业务状态、业务场景,让关联的业务订阅事件,当事件产生时事件订阅者执行业务,这样发布者不必知道订阅者具体的细节、订阅者也不必了解发布者。但是简单的事件无法满足实际生产的使用,因为生产中的业务是复杂的,多样的,所以就要设计一种框架来统一管理事件设计成事件总线。
二、设计
事件总线进行抽象成如下接口关系图,IEventBus作为事件总线进行协调管理事件的发布订阅,作为事件中心承担的职责除了事件的发布和订阅之外,还包括对事件存储、容错、路由、异步、重试等。
事件总线类说明 | |
IEvent | 事件载体的接口,所有事件继承该接口,定义一个事件,并且赋予事件相应的业务逻辑,比如这个事件要产生的业务操作 |
IEventHandler | 事件处理接口,每一个事件都必须关联相应的事件操作 |
IEventSubscriber | 发布订阅机制的订阅事件接口,主要功能订阅事件关联事件处理器 |
IEventPublisher | 发布订阅机制的发布事件接口,主要功能对产生的事件进行发布 |
IEventBus | 事件总线接口,通过聚合事件的发布和订阅接口,在内部抽象成一个总线的方式统一管理事件,并且引入事件消息存储结构 |
三、代码
依据上述的事件总线接口关系图,在代码上定义各接口类,然后定义接口的关系创建一个初步的事件总线,通过事件总线接口来实现具体事件总线框架。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TQF.EventBus.EventFramwork
{
/// <summary>
/// 事件消息接口,定义一个事件,事件载体
/// </summary>
public interface IEvent
{
/// <summary>
/// 事件Id
/// </summary>
Guid EventId { get; }
/// <summary>
/// 事件时间
/// </summary>
DateTime EventDate { get; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TQF.EventBus.EventFramwork
{
/// <summary>
/// 事件处理接口,定义一个事件对应的处理逻辑
/// </summary>
public interface IEventHandler
{
/// <summary>
/// 定义事件异步的处理器
/// </summary>
/// <param name="event">使用@区分关键字</param>
/// <param name="cancellationToken">任务取消令牌,取消任务</param>
/// <returns></returns>
Task<bool> HandlerAsync(IEvent @event, CancellationToken cancellationToken = default);
bool CanHandle(IEvent @event);
}
/// <summary>
/// 泛型事件处理器
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IEventHandler<in T> : IEventHandler where T : IEvent
{
Task<bool> HandleAsync(T @event, CancellationToken cancellationToken = default);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TQF.EventBus.EventFramwork
{
/// <summary>
/// 事件发布器,发布消息
/// </summary>
public interface IEventPublisher:IDisposable
{
/// <summary>
/// 事件发布器,发布事件
/// </summary>
/// <typeparam name="TEvent"></typeparam>
/// <param name="event">事件</param>
/// <param name="cancellationToken">任务取消令牌,取消任务</param>
/// <returns></returns>
Task PublishAsync<TEvent>(TEvent @event, CancellationToken cancellationToken = default)
where TEvent : IEvent;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TQF.EventBus.EventFramwork
{
/// <summary>
/// 事件订阅器订阅消息
/// </summary>
public interface IEventSubscriber:IDisposable
{
void Subscriber();
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TQF.EventBus.EventFramwork
{
/// <summary>
/// 事件总线接口,定义事件通讯渠道,消息订阅功能,消息派发功能(消息的路由,过滤,选择)等。
/// 通过事件总线管理两者
/// </summary>
public interface IEventBus:IEventPublisher,IEventSubscriber
{
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TQF.EventBus.EventFramwork
{
/// <summary>
/// 定义事件传递的参数
/// </summary>
public class EventProcessedEventArgs:EventArgs
{
public IEvent Event { get; }
public EventProcessedEventArgs(IEvent @event)
{
this.Event = @event;
}
}
}
四、总结
1、事件总线的目的是对业务的解耦,通过设计合理的架构来达到这个目标,既然是合理的框架,就涉及到存储、容错、路由、异步、重试等问题,所以上述提供是一个事件总线雏形。
2、对于模式和架构,一个模式,一个架构其构成的最小单位可以理解为对象,通过对对象的划分、割裂,单独成个体,通过关系连接这些个体,建立起一个模型->模式->架构。