距离上一篇DDD系列完结已经过了很长一段时间,项目也搁置了一段时间,想想还是继续完善下去。

DDD领域驱动之干货(三)完结篇!

上一篇说到了如何实现uow配合Repository在autofac和automapper下实现的功能,今天完善一下事件驱动也就是领域驱动。

领域驱动的概念网上一搜一大推,我就不一一累赘,本文主要讲解如何实现领域事件和事件总线。

事件一共提供三个方法去完成事件的实现-----------注册事件、卸载事件、发布事件

那么在注册事件的时候我们怎么样是定义一个事件呢?

如下图:

图中的Events为事件,handler为事件的处理,bus为事件总线。

这么一来思路就清晰多了。

首先我们为事件定义一个标识事件的接口。

public interface IEvent
{ // 获取产生事件的时间
DateTime Time { get; set; }
//事件源
object Source { get; set; } }

所有的事件类都应该实现该接口。

public class Event : IEvent
{
public DateTime Time { get; set; }
public object Source { get; set; }
public Event() {
Time = DateTime.Now;
} }

可以把这个Event看过是domianEvent的根事件,所有的领域事件应该继承根事件。

 public class UserEvent :Event
{
public User info { get; set; }
}

事件我们写完了,接下来是需要写事件要执行的处理。

/// <summary>
/// 标志接口
/// </summary>
public interface IHandler
{ } /// <summary>
/// 事件处理器接口,所有事件处理器都要实现该接口。
/// </summary>
public interface IEventHandler<TEvent> : IHandler where TEvent:IEvent
{
// 处理给定的事件
void Handle(TEvent Event);
}

然后是写一个委托事件处理器。

public class ActionHandler<TEvent> : IEventHandler<TEvent> where TEvent : IEvent
{
public Action<TEvent> Action { get; private set; } public ActionHandler() { } public ActionHandler(Action<TEvent> handler) {
Action = handler;
} public void Handle(TEvent Event)
{
throw new NotImplementedException();
}
}

处理事件的方法定义完成后,我们需要完成领域的处理。

 public class UserHandler :IEventHandler<UserEvent>
{
public void Handle(UserEvent Event)
{
Event.Source = Event.info;
}
}

所有的事件我们定义完成后,接下来就是事件总线出场了。

public interface IEventBus
{
//注册事件
void RegisterAllHandler(IEnumerable<Assembly> assembles);
void Register<THandle>(IHandler handle);
void Register(Type eventType, Type handler);
void Register<THandle>(Action<THandle> action) where THandle : IEvent; //反注册事件
void UnRegisiter<THandle>(Type handleType) where THandle : IEvent;
void UnRegisterAllHandler<THandle>(); //触发事件
void TiggerEvent<THandle>(THandle eventData) where THandle : IEvent;
void TiggerEvent<THandle>(Type eventHandlerType, THandle eventData) where THandle : IEvent; Task TiggerEventAsync<THandle>(THandle eventData) where THandle : IEvent; Task TiggerEventAsycn<THandle>(Type eventHandlerType, THandle eventData) where THandle : IEvent;
}

接口定义好了之后是实现接口。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using KuRuMi.Mio.DoMain.Events.Events;
using KuRuMi.Mio.DoMain.Events.Handler;
using System.Reflection;
using System.Collections.Concurrent; namespace KuRuMi.Mio.DoMain.Events.Bus
{
/// <summary>
/// 事件总线
/// </summary>
public class EventBus : IEventBus
{
private object locker = new object();
public static EventBus bus => new EventBus();
private static IEnumerable<Assembly> assemly { get; set; } private static readonly ConcurrentDictionary<Type, List<Type>> EventMapping = new ConcurrentDictionary<Type, List<Type>>();
/// <summary>
/// 注册所有事件
/// </summary>
/// <param name="assembles"></param>
public void RegisterAllHandler(IEnumerable<Assembly> assembles)
{
assemly = assembles;
foreach (Assembly assembly in assembles)
{
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{
Type handlerInterfaceType = type.GetInterface("IEventHandler`1");
if (handlerInterfaceType != null)
{
Type eventType = handlerInterfaceType.GetGenericArguments()[];
if (!EventMapping.Keys.Contains(eventType))
{
Register(eventType, type);
}
}
}
}
}
/// <summary>
/// 注册到事件总线
/// </summary>
/// <param name="eventType"></param>
/// <returns></returns>
private List<Type> GetOrCreateHandlers(Type eventType)
{
return EventMapping.GetOrAdd(eventType, (type) => new List<Type>());
} #region 注册事件
/// <summary>
/// 手动绑定事件
/// </summary>
/// <typeparam name="THandle"></typeparam>
/// <param name="handle"></param>
public void Register<THandle>(IHandler handle)
{
Register(typeof(THandle), handle.GetType());
}
public void Register(Type eventType, Type handler)
{
lock (locker)
{
GetOrCreateHandlers(eventType).Add(handler);
}
}
/// <summary>
/// 通过委托注册
/// </summary>
/// <typeparam name="THandle"></typeparam>
/// <param name="action"></param>
public void Register<THandle>(Action<THandle> action) where THandle : IEvent
{
ActionHandler<THandle> ActionHandler = new ActionHandler<THandle>(action);
Register<THandle>(ActionHandler);
} #endregion #region 卸载事件
/// <summary>
/// 手动卸载单个事件
/// </summary>
/// <typeparam name="THandle"></typeparam>
/// <param name="handleType"></param>
public void UnRegisiter<THandle>(Type handleType) where THandle : IEvent
{
lock (locker)
{
GetOrCreateHandlers(typeof(THandle)).RemoveAll(t => t == handleType);
}
} /// <summary>
/// 卸载所有事件
/// </summary>
/// <typeparam name="THandle"></typeparam>
public void UnRegisterAllHandler<THandle>()
{
lock (locker)
{
GetOrCreateHandlers(typeof(THandle)).Clear();
}
}
#endregion #region 触发事件
/// <summary>
/// 根据事件源触发事件
/// </summary>
/// <typeparam name="THandle"></typeparam>
/// <param name="eventData"></param>
public void TiggerEvent<THandle>(THandle eventData) where THandle : IEvent
{
//获取所有的事件处理
List<Type> handlerTypes = GetOrCreateHandlers(typeof(THandle));
if (handlerTypes != null && handlerTypes.Count > )
{
foreach (var handlerType in handlerTypes)
{
var handlerInterface = handlerType.GetInterface("IEventHandler`1");
foreach (Assembly assembly in assemly)
{
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{
Type handlerInterfaceType = type.GetInterface("IEventHandler`1");
if (handlerInterfaceType != null)
{
//判断两个类型是否相等
if (handlerInterface == handlerInterfaceType)
{
var eventType = handlerInterfaceType.GenericTypeArguments[];
EventMapping[eventType].ForEach(s=> {
var obj = Activator.CreateInstance(s) as IEventHandler<THandle>;
obj?.Handle(eventData);
});
}
}
}
}
}
}
}
/// <summary>
/// 指定handler触发事件
/// </summary>
/// <typeparam name="THandle"></typeparam>
/// <param name="eventData"></param>
/// <returns></returns>
public void TiggerEvent<THandle>(Type eventHandlerType, THandle eventData) where THandle : IEvent
{
var handlerInterface = eventHandlerType.GetInterface("IEventHandler`1");
foreach (Assembly assembly in assemly)
{
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{
Type handlerInterfaceType = type.GetInterface("IEventHandler`1");
if (handlerInterfaceType != null)
{
//判断两个类型是否相等
if (handlerInterface == handlerInterfaceType)
{
var eventType = handlerInterfaceType.GenericTypeArguments[];
EventMapping[eventType].ForEach(s => {
var obj = Activator.CreateInstance(s) as IEventHandler<THandle>;
obj?.Handle(eventData);
});
}
}
}
}
}
/// <summary>
/// 根据事件源触发事件(异步)
/// </summary>
/// <typeparam name="THandle"></typeparam>
/// <param name="eventData"></param>
/// <returns></returns>
public Task TiggerEventAsync<THandle>(THandle eventData) where THandle : IEvent
{
return Task.Run(() => TiggerEvent<THandle>(eventData));
}
/// <summary>
/// 指定handler触发事件(异步)
/// </summary>
/// <typeparam name="THandle"></typeparam>
/// <param name="eventHandlerType"></param>
/// <param name="eventData"></param>
/// <returns></returns>
public Task TiggerEventAsycn<THandle>(Type eventHandlerType, THandle eventData) where THandle : IEvent
{
return Task.Run(() => TiggerEvent<THandle>(eventHandlerType, eventData));
}
#endregion
}
}

代码上我都有注释,过多了我就不多做说明。

以上就是我对领域事件和领域总线的理解,欢迎大佬指正。

DDD领域驱动之干活(四)补充篇!的更多相关文章

  1. DDD领域驱动之干货(四)补充篇!

    距离上一篇DDD系列完结已经过了很长一段时间,项目也搁置了一段时间,想想还是继续完善下去. DDD领域驱动之干货(三)完结篇! 上一篇说到了如何实现uow配合Repository在autofac和au ...

  2. C#进阶系列——DDD领域驱动设计初探(四):WCF搭建

    前言:前面三篇分享了下DDD里面的两个主要特性:聚合和仓储.领域层的搭建基本完成,当然还涉及到领域事件和领域服务的部分,后面再项目搭建的过程中慢慢引入,博主的思路是先将整个架构走通,然后一步一步来添加 ...

  3. DDD领域驱动设计初探(四):WCF搭建

    前言:前面三篇分享了下DDD里面的两个主要特性:聚合和仓储.领域层的搭建基本完成,当然还涉及到领域事件和领域服务的部分,后面再项目搭建的过程中慢慢引入,博主的思路是先将整个架构走通,然后一步一步来添加 ...

  4. 基于事件驱动的DDD领域驱动设计框架分享(附源代码)

    原文:基于事件驱动的DDD领域驱动设计框架分享(附源代码) 补充:现在再回过头来看这篇文章,感觉当初自己偏激了,呵呵.不过没有以前的我,怎么会有现在的我和现在的enode框架呢?发现自己进步了真好! ...

  5. 浅谈我对DDD领域驱动设计的理解

    从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...

  6. DDD 领域驱动设计-领域模型中的用户设计

    上一篇:<DDD 领域驱动设计-如何控制业务流程?> 开源地址:https://github.com/yuezhongxin/CNBlogs.Apply.Sample(代码已更新,并增加了 ...

  7. DDD 领域驱动设计-如何完善 Domain Model(领域模型)?

    上一篇:<DDD 领域驱动设计-如何 DDD?> 开源地址:https://github.com/yuezhongxin/CNBlogs.Apply.Sample(代码已更新) 阅读目录: ...

  8. DDD 领域驱动设计-看我如何应对业务需求变化,领域模型调整?

    写在前面 上一篇:DDD 领域驱动设计-看我如何应对业务需求变化,愚蠢的应对? "愚蠢的应对",这个标题是我后来补充上的,博文中除了描述需求变化.愚蠢应对和一些思考,确实没有实质性 ...

  9. C#进阶系列——DDD领域驱动设计初探(一):聚合

    前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的 ...

随机推荐

  1. 使用fontawesome图标

     我每次找图标时都是在阿里的开源图标库中找的,但是使用起来不是很方便.而我发现了fontawesome之后,觉得实在不错,所以分享给大家.  这是一些参考的文档. fontawesome下载与使用介绍 ...

  2. Docker - 容器直连

    本文是在原文基础上的实际操作验证记录和细节补充. 默认情况下,容器连接到虚拟网桥docker0提供的虚拟子网中,容器之间通过默认网关(虚拟网桥docker0接口地址)建立连接. 如果不使用虚拟网桥,用 ...

  3. linux优化之SElinux关闭

    查看selinux状态: # getenforce   注:Enforcing表示开启,Permissive表示禁用 临时关闭或开启selinux: # setenforce  [1|0]  注:1是 ...

  4. CSS3特效----图片动态提示效果

    需掌握的知识点: 1.掌握两个HTML5新标签figure以及figcaption的用法 2.掌握transform的属性特点,并能熟练运用 3.学会通过transition及transform配合, ...

  5. Python爬虫学习之获取网页源码

    偶然的机会,在知乎上看到一个有关爬虫的话题<利用爬虫技术能做到哪些很酷很有趣很有用的事情?>,因为强烈的好奇心和觉得会写爬虫是一件高大上的事情,所以就对爬虫产生了兴趣. 关于网络爬虫的定义 ...

  6. php如何应对秒杀抢购高并发思路

    我们常用QPS(Query Per Second,每秒处理请求数)来衡量一个web应用的吞吐率,解决每秒数万次的高并发场景,这个指标非常关键. 举个栗子:假设一个业务请求平均为100ms,同时系统内有 ...

  7. redis 清空缓存

    redis 清空缓存 Redis 命令: flushall --> 清空整个 Redis 服务器的数据(删除所有数据库的所有 key ) flushdb --> 清空当前数据库中的所有 k ...

  8. Java微服务框架

    Java的微服务框架dobbo.spring boot.redkale.spring cloud 消息中间件RabbitMQ.Kafka.RocketMQ

  9. python脚本 随机定位坐标

    import mathimport randomdef rad(dg):    return (dg * math.pi / 180)def deg(rd):    return (rd * 180 ...

  10. 小K的H5之旅-HTML的基本结构与基本标签

    一.什么是HTML HTML是超文本标签语言,即网页的源码.而浏览器就是翻译解释HTML源码的工具. 二.HTML文档的结构 HTML文档主要包括三大部分:文档声明部分.<head>头部部 ...