谈谈它

终于有些眉目了,搜刮了很多牛人的资料,英文的,中文的,民国文的,终于小有成就了,同时也做了个DEMO,领域事件这东西好,但需要你明白它之后才会说好,而对于明白领域事件这件事来说,它的门槛有点高,居然花了我三天的时间才把它搞定,嗨!

占占给它的定义

领域事件:Domain Event,是针对某个业务来说的,或者说针对某个聚合的业务来说的,例如订单生成这种业务,它可以同时对应一种事件,比如叫做OrderGeneratorEvent,而你的零散业务可能随时会变,加一些业务,减一些业务,而对于订单生成这个事件来说,它是唯一不变的,而我们需要把这些由产生订单而发生变化的事情拿出来,而拿出来的这些业务就叫做"领域事件".其中的领域指的就是订单生成这个聚合;而事件指的就是那些零散业务的统称.

它的主要几个抽象

在面向对象的编程世界里,做这种事情我们需要几个抽象:

领域对象事件标示:(标示接口,接口的一种,用来约束一批对象)IEvent

领域对象的处理方法,行为:(需要让那些零散的模块重写的方法,起个听起来熟悉的名字,叫它handle吧)IEventHandler=>Handle

事件总线:事件处理核心类,承载了事件的发布,订阅与取消订阅的逻辑,EventBus

某个领域对象:为了实现某个业务,而创建的实体类,它里面有事件所需要的数据,它继承了IEvent

某个领域对象的事件:它是一个事件处理类,它实现了IEventHandler,它所处理的事情需要在Handle里去完成

我的Demo的实现

一 结果图:

二 核心类:

IEvent接口,标示接口往往都是空的,呵呵

    /// <summary>/// 事件实体基类/// </summary>public interface IEvent{}

IEventHandler接口,只有一个行为方法Handle

    /// <summary>/// 事件处理接口/// </summary>/// <typeparam name="TEvent">继承IEvent对象的事件源对象</typeparam>public interface IEventHandler<TEvent>where TEvent : IEvent{/// <summary>/// 处理程序/// </summary>/// <param name="evt"></param>void Handle(TEvent evt);}

EventBus是实现事件的核心,在这版里,它支持异步事件机制,使用Task实现,所以它需要运行在.net4.5平台之上

   /// <summary>/// 事件总线/// 发布与订阅处理逻辑/// 核心功能代码/// </summary>public class EventBus{private EventBus() { }private static EventBus _eventBus = null;private readonly object sync = new object();/// <summary>/// 对于事件数据的存储,目前采用内存字典/// </summary>private static Dictionary<Type, List<object>> eventHandlers = new Dictionary<Type, List<object>>();/// <summary>// checks if the two event handlers are equal. if the event handler is an action-delegated, just simply// compare the two with the object.Equals override (since it was overriden by comparing the two delegates. Otherwise,// the type of the event handler will be used because we don't need to register the same type of the event handler// more than once for each specific event./// </summary>private readonly Func<object, object, bool> eventHandlerEquals = (o1, o2) =>{var o1Type = o1.GetType();var o2Type = o2.GetType();if (o1Type.IsGenericType &&o1Type.GetGenericTypeDefinition() == typeof(ActionDelegatedEventHandler<>) &&o2Type.IsGenericType &&o2Type.GetGenericTypeDefinition() == typeof(ActionDelegatedEventHandler<>))return o1.Equals(o2);return o1Type == o2Type;};/// <summary>/// 初始化空的事件总件/// </summary>public static EventBus Instance{get{return _eventBus ?? (_eventBus = new EventBus());}}/// <summary>/// 通过XML文件初始化事件总线,订阅信自在XML里配置/// </summary>/// <returns></returns>public static EventBus InstanceForXml(){if (_eventBus == null){XElement root = XElement.Load(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "EventBus.xml"));foreach (var evt in root.Elements("Event")){List<object> handlers = new List<object>();Type publishEventType = Type.GetType(evt.Element("PublishEvent").Value);foreach (var subscritedEvt in evt.Elements("SubscribedEvents"))foreach (var concreteEvt in subscritedEvt.Elements("SubscribedEvent"))handlers.Add(Type.GetType(concreteEvt.Value));eventHandlers[publishEventType] = handlers;}_eventBus = new EventBus();}return _eventBus;}#region 事件订阅&取消订阅,可以扩展/// <summary>/// 订阅事件列表/// </summary>/// <param name="type"></param>/// <param name="subTypeList"></param>public void Subscribe<TEvent>(IEventHandler<TEvent> eventHandler)where TEvent : class, IEvent{lock (sync){var eventType = typeof(TEvent);if (eventHandlers.ContainsKey(eventType)){var handlers = eventHandlers[eventType];if (handlers != null){if (!handlers.Exists(deh => eventHandlerEquals(deh, eventHandler)))handlers.Add(eventHandler);}else{handlers = new List<object>();handlers.Add(eventHandler);}}elseeventHandlers.Add(eventType, new List<object> { eventHandler });}}/// <summary>/// 订阅事件实体/// </summary>/// <param name="type"></param>/// <param name="subTypeList"></param>public void Subscribe<TEvent>(Action<TEvent> eventHandlerFunc)where TEvent : class, IEvent{Subscribe<TEvent>(new ActionDelegatedEventHandler<TEvent>(eventHandlerFunc));}public void Subscribe<TEvent>(IEnumerable<IEventHandler<TEvent>> eventHandlers)where TEvent : class, IEvent{foreach (var eventHandler in eventHandlers)Subscribe<TEvent>(eventHandler);}/// <summary>/// 取消订阅事件/// </summary>/// <param name="type"></param>/// <param name="subType"></param>public void Unsubscribe<TEvent>(IEventHandler<TEvent> eventHandler)where TEvent : class, IEvent{lock (sync){var eventType = typeof(TEvent);if (eventHandlers.ContainsKey(eventType)){var handlers = eventHandlers[eventType];if (handlers != null&& handlers.Exists(deh => eventHandlerEquals(deh, eventHandler))){var handlerToRemove = handlers.First(deh => eventHandlerEquals(deh, eventHandler));handlers.Remove(handlerToRemove);}}}}public void Unsubscribe<TEvent>(IEnumerable<IEventHandler<TEvent>> eventHandlers)where TEvent : class, IEvent{foreach (var eventHandler in eventHandlers)Unsubscribe<TEvent>(eventHandler);}public void Unsubscribe<TEvent>(Action<TEvent> eventHandlerFunc)where TEvent : class, IEvent{Unsubscribe<TEvent>(new ActionDelegatedEventHandler<TEvent>(eventHandlerFunc));}#endregion#region 事件发布/// <summary>/// 发布事件,支持异步事件/// </summary>/// <typeparam name="TEvent"></typeparam>/// <param name="evnt"></param>public void Publish<TEvent>(TEvent evnt)where TEvent : class, IEvent{if (evnt == null)throw new ArgumentNullException("evnt");var eventType = evnt.GetType();if (eventHandlers.ContainsKey(eventType)&& eventHandlers[eventType] != null&& eventHandlers[eventType].Count > 0){var handlers = eventHandlers[eventType];foreach (var handler in handlers){var eventHandler = handler as IEventHandler<TEvent>;if (eventHandler.GetType().IsDefined(typeof(HandlesAsynchronouslyAttribute), false)){Task.Factory.StartNew((o) => eventHandler.Handle((TEvent)o), evnt);}else{eventHandler.Handle(evnt);}}}}public void Publish<TEvent>(TEvent evnt, Action<TEvent, bool, Exception> callback, TimeSpan? timeout = null)where TEvent : class, IEvent{if (evnt == null)throw new ArgumentNullException("evnt");var eventType = evnt.GetType();if (eventHandlers.ContainsKey(eventType) &&eventHandlers[eventType] != null &&eventHandlers[eventType].Count > 0){var handlers = eventHandlers[eventType];List<Task> tasks = new List<Task>();try{foreach (var handler in handlers){var eventHandler = handler as IEventHandler<TEvent>;if (eventHandler.GetType().IsDefined(typeof(HandlesAsynchronouslyAttribute), false)){tasks.Add(Task.Factory.StartNew((o) => eventHandler.Handle((TEvent)o), evnt));}else{eventHandler.Handle(evnt);}}if (tasks.Count > 0){if (timeout == null)Task.WaitAll(tasks.ToArray());elseTask.WaitAll(tasks.ToArray(), timeout.Value);}callback(evnt, true, null);}catch (Exception ex){callback(evnt, false, ex);}}elsecallback(evnt, false, null);}#endregion}

一个具体的领域对象,它继承IEvent

    /// <summary>/// 添加订单的事件/// </summary>public class OrderGeneratorEvent : IEvent{public int OrderID { get; set; }}

一个为OrderGeneratorEvent工作的领域事件,它用来为客户发邮件

   /// <summary>/// 发邮件功能/// </summary>public class OrderAddedEventHandler_SendEmail : IEventHandler<OrderGeneratorEvent>{public void Handle(OrderGeneratorEvent evt){Console.WriteLine("Order_Number:{0},Send a Email.", evt.OrderID);}}

下面看一个主程序:

   static void Main(string[] args){EventBus.Instance.Subscribe(new OrderAddedEventHandler_SendEmail());var entity = new OrderGeneratorEvent { OrderID = 1 };Console.WriteLine("生成一个订单,单号为{0}", entity.OrderID);EventBus.Instance.Publish(entity);Console.ReadKey();}

下面是运行结果:

DDD(六)【领域事件与事件总线】相关推荐

  1. DDD~领域事件与事件总线

    DDD~领域事件与事件总线 回到目录 谈谈它 终于有些眉目了,搜刮了很多牛人的资料,英文的,中文的,民国文的,终于小有成就了,同时也做了个DEMO,领域事件这东西好,但需要你明白它之后才会说好,而对于 ...

  2. Lind.DDD.Events领域事件介绍

    闲话多说 领域事件大叔感觉是最不好讲的一篇文章,所以拖欠了很久,但最终还是在2015年年前(阴历)把这个知识点讲一下,事件这个东西早在C#1.0时代就有了,那时学起来也是一个费劲,什么是委托,哪个是事 ...

  3. ABP学习实践(十六)--领域驱动设计(DDD)回顾

    ABP框架并没有实现领域驱动设计(DDD)的所有思想,但是并不妨碍用领域驱动的思想去理解ABP库框架. 1.领域驱动设计(DDD)与微服务(MicroService)的关系? 领域驱动设计(DDD)是 ...

  4. CQRS体系结构模式实践案例:Tiny Library:领域仓储与事件存储

    领域仓储(Domain Repository)与事件存储(Event Store)是CQRS体系结构应用系统中C部分(Command部分)的重要组件.虽然都是存储机制,但两者有着本质的区别:领域仓储是 ...

  5. java事件溯源_领域事件与事件溯源 - 解道Jdon

    领域事件.事件溯源.事件风暴建模 事件代表过去发生的事件,事件既是技术架构概念,也是业务概念.以事件为驱动的编程模型称为事件驱动架构EDA,事件驱动与事件溯源EventSourcing是两种概念,ED ...

  6. Vue | 使用Vue脚手架 【脚手架的基本使用+ref属性+props属性+mixin混入+插件scoped样式+TodoList+浏览器本地存储+组件的自定义事件+全局事件总线+过度与动画】

    文章目录 脚手架的基本使用 初始化脚手架 分析脚手架结构 render函数 修改默认配置 ref属性 props属性 mixin混入 插件 scoped样式 Todo-list案例 组件化编码流程(通 ...

  7. Noah Mt4跟单系统制作第六篇 Mt4TradeApi交易事件篇

    Noah Mt4跟单系统制作第六篇 Mt4TradeApi交易事件篇 using System; using System.Collections.Generic; using System.Linq ...

  8. libevent学习笔记六:libevent核心事件event

    libevent学习笔记六:libevent核心事件event 前面对reactor模式.事件处理流程.libevent源代码结构等有了高层的认识后,接下来将详细介绍libevent的核心结构even ...

  9. Java程序员从笨鸟到菜鸟之(九十)跟我学jquery(六)jquery中事件详解

    由于jQuery本身就是web客户端的有力帮手,所以事件对于它来说就显得尤为重要了,事件是脚本编程的灵魂. 所以此内容也是jQuery学习的重点. 在传统的JavaScript中,注册一个事件也是非常 ...

最新文章

  1. iOS-实际项目中用到的第三方库
  2. python运行慢-提高python运行速度的几个技巧
  3. 《Linux内核设计与实现》课程学习重点问题总结
  4. 关于Layout Weight一些使用技巧
  5. 转: MinGw离线安装方法集合
  6. i-doIT 0.9.9-7发布 CMDB配置管理数据库
  7. HDU 2896 病毒侵袭【AC自动机】
  8. 全局修改elementui message 右边弹出_ElementUI 只允许 $message 提示一次
  9. 华为手机怎么下载linux命令,在linux命令
  10. ObjectOutputStream 和 ObjectInputStream类的简单介绍,及运用。
  11. 阿里云php-7.2.12 安装
  12. Linux下安装whl文件
  13. java 删 除文件操作_Java File文件处理 删除文件
  14. 画图软件Microsoft visio下载安装及使用
  15. 【Docker】MySQL 主从配置(一主一从)
  16. 英特尔最新超级计算机,全球超级计算机500强三分之二使用英特尔的处理器
  17. SSS1629USB麦克风方案设计原理
  18. python广义矩估计_《利用Python进行数据分析》13章(中二)建模库介绍
  19. 使用AVProVideo的一点小坑
  20. 2022年数字科技前沿应用趋势

热门文章

  1. 声音播放装置及其补偿方法 电容模式, 无电容模式(capless mode)
  2. 【书法字识别】余弦形状相似度书法字识别【含Matlab源码 1356期】
  3. Python 临时设置LD_LIBRARY_PATH
  4. TED-2-拖延症患者的大脑
  5. 名创优品在海外还能“嚣张”吗?
  6. android 实现圆形头像
  7. pipelines php,Azure DevOps 2020(五)使用 Pipelines 自动化发布 NUGET 包
  8. 运筹学问题用matlab解答,运筹学课程设计(论文)-用matlab和lingo求解生产问题
  9. [ vulhub漏洞复现篇 ] Jetty WEB-INF 文件读取复现CVE-2021-34429
  10. bp神经网络时间序列预测,bp神经网络有几个阶段