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

距离上一篇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()[0];
                        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 > 0)
            {
                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[0];
                                    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[0];
                            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
    }
}

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

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

时间: 2024-10-12 02:26:56

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

(转载)浅谈我对DDD领域驱动设计的理解

原文地址:http://www.cnblogs.com/netfocus/p/5548025.html 从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品.所以,自然而然就想到要做一个普通电商系统,用于实现在线销售自己企业产品的目的. 再比如,我是一家互联网公司,公司有很多系统对外提供服务,面向很多客户端设备.但是最近由于各种原因,导致服务经常出故

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

从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品.所以,自然而然就想到要做一个普通电商系统,用于实现在线销售自己企业产品的目的. 再比如,我是一家互联网公司,公司有很多系统对外提供服务,面向很多客户端设备.但是最近由于各种原因,导致服务经常出故障.所以,我们希望通过各种措施提高服务的质量和稳定性.其中的一个措施就是希望能做一个灰度发布的平台,这个

C#进阶系列——DDD领域驱动设计初探(六):领域服务

前言:之前一直在搭建项目架构的代码,有点偏离我们的主题(DDD)了,这篇我们继续来聊聊DDD里面另一个比较重要的知识点:领域服务.关于领域服务的使用,书中也介绍得比较晦涩,在此就根据博主自己的理解谈谈这个知识点的使用. DDD领域驱动设计初探系列文章: C#进阶系列——DDD领域驱动设计初探(一):聚合 C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上) C#进阶系列——DDD领域驱动设计初探(三):仓储Repository(下) C#进阶系列——DDD领域驱动设计初探

DDD领域驱动设计初探(一):聚合

前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的东西.前面介绍了下MEF和AOP的相关知识,后面打算分享Automapper.仓储模式.WCF等东西的,可是每次准备动手写点什么的时候,就被要写的Demo难住了,比如仓储模式,使用过它的朋友应该知道,如果你的项目不是按照DDD的架构而引入仓储的设计,那么会让它变得很“鸡肋”,用不好就会十分痛苦,之前

DDD领域驱动设计仓储Repository

DDD领域驱动设计初探(二):仓储Repository(上) 前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原因无非以下两点:一是Repository的真实意图没有理解清楚,导致设计的紊乱,随着项目的横向和纵向扩展,到最后越来越难维护:二是赶时髦的为了“模式”而“模式”,仓储并非适用于所有项目,这就像没有任何一种架构能解决所有的设计难题一样.本篇

DDD领域驱动模型设计

背景 使用DDD开发大概也有五个月的时间了,由于当时公司导师的推荐,第一次接触DDD领域驱动到现在彻底迷恋这种开发的模式,为其思想的奥妙所折服,一直以来,总想花一点时间来总结一下,正直光棍节(天猫狂欢购物节)当天,"静下心来"(PS:没有人民币)总结一下. 说起DDD不得不说一篇文章:http://www.cnblogs.com/netfocus/archive/2011/10/10/2204949.html 第一次接触,就是这篇文章,当时看起来晦涩难懂,后来慢慢的读起来,每一次都有精

基于DDD领域驱动设计的WCF+EF+WPF分层框架

目录置顶: 关于项目--------------------基于DDD领域驱动设计的WCF+EF+WPF分层框架(1) 架构搭建--------------------基于DDD领域驱动设计的WCF+EF+WPF分层框架(2) WCF服务端具体实现---------基于DDD领域驱动设计的WCF+EF+WPF分层框架(3) WCF客户端配置以及代理-----基于DDD领域驱动设计的WCF+EF+WPF分层框架(4) Domain具体实现------------基于DDD领域驱动设计的WCF+EF

NMock学习系列(三)--- NMock在DDD领域驱动的单元测试中的应用

介绍 领域驱动设计涵盖的知识点比较多,其中代码的架构.设计.编写基本上只占到其中的很小一部分,其它的大部分讲解的是需求的获取方式.项目的管理方式等知识.本篇就是针对这一小部分的知识点位来展开的.所以本篇的学习前提是只需要了解DDD的架构分层即可. 应用场景 DDD领域驱动设计中一旦领域驱动层模型建立完毕,就会产生出数据库持久化的接口即仓储的接口供其它层来做具体实现,所以要想建立领域层的单元测试,就必须实现这些仓储接口或者模拟出这些接口实现.我们可以采用NMock来进行模拟仓储的实现.下面开始学习

C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)

前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原因无非以下两点:一是Repository的真实意图没有理解清楚,导致设计的紊乱,随着项目的横向和纵向扩展,到最后越来越难维护:二是赶时髦的为了“模式”而“模式”,仓储并非适用于所有项目,这就像没有任何一种架构能解决所有的设计难题一样.本篇通过这个设计的Demo来谈谈博主对仓储的理解,有不对的地方还望