zoukankan      html  css  js  c++  java
  • DDD~领域事件与事件总线

    回到目录

    谈谈它

    终于有些眉目了,搜刮了很多牛人的资料,英文的,中文的,民国文的,终于小有成就了,同时也做了个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);
                        }
                    }
                    else
                        eventHandlers.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());
                            else
                                Task.WaitAll(tasks.ToArray(), timeout.Value);
                        }
                        callback(evnt, true, null);
                    }
                    catch (Exception ex)
                    {
                        callback(evnt, false, ex);
                    }
                }
                else
                    callback(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的贡献非常大...

    回到目录

  • 相关阅读:
    DotNet的JSON序列化与反序列化
    DotNet指定文件显示的尺寸
    将文本文件的内容存储在DataSet中的方法总结
    Apple的LZF算法解析
    DotNet常用排序算法总结
    C#创建安全的字典(Dictionary)存储结构
    C#创建安全的栈(Stack)存储结构
    转化代码:添加在您网页中用户触发转化行为之后的地方。添加方法
    nginx配置ThinkPHP5二级目录访问
    好久没写原生的PHP调用数据库代码了分享个
  • 原文地址:https://www.cnblogs.com/lori/p/3476703.html
Copyright © 2011-2022 走看看