zoukankan      html  css  js  c++  java
  • [译]Domain Events Pattern Example

    原文

    完整源码

    本文展示的是一个关于网上调查的项目。想象下,当用户完成了一个调查,我们想通知所有人调查已经结束,分配一个人去检查调用问卷。

    领域对象

    public class Survey
    {
        public Guid Id { get; private set; }
        public DateTime EndTime { get; private set; }
        public string QualityChecker { get; set; }
    
        public Survey()
        {
            this.Id = Guid.NewGuid();
        }
    
        public void EndSurvey()
        {
            EndTime = DateTime.Now;
            DomainEvent.Raise(new EndOfSurvey() { Survey = this });
        }
    }
    

    这个领域对象非常简单,只有一个行为:EndSurvey().

    那么这里的DomainEvent是个什么东西呢?它是一个静态类,它发布了一个EndOfSurvey事件。从项目源码中可以看到所有的事件都放在名为Events的文件夹下面。领域对象放在Domain文件夹下面。

    EndOfSurvey事件

    现在Survey对象希望发布一个EndOfSurvey事件。这个事件的代码如下:

    public class EndOfSurvey : IDomainEvent
    {
        public Survey Survey { get; set; }
    }
    

    EndOfSurvey包含一个Survey实例。它继承自IDomainEvent,这样我们知道他是一个领域事件。本例中所有的事件都要继承自IDomainEvent
    这个接口的定义很简单:

    public interface IDomainEvent { }  
    

    DomainEvent类

    public static class DomainEvent
    {
        public static IEventDispatcher Dispatcher { get; set; }
    
        public static void Raise<T>(T @event) where T : IDomainEvent
        {
            Dispatcher.Dispatch(@event);
        }
    
    }
    

    源码中的DomainEvent比这个要复杂点,但最重要的便是上面的代码了。

    IEventDispatcher是一个ioc容器。它负责找到正确的handler来处理EndOfSurvey事件。

    public interface IEventDispatcher
    {
        void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : IDomainEvent;
    }
    

    泛型方法Raise<T>能让我们发布无数的事件,Dispatcher自动找出对应的handler。

    下面定义一个处理所有事件的handler接口:

    public interface IDomainHandler<T> where T : IDomainEvent
    {
        void Handle(T @event);
    }
    

    我将IEventDispatcher.csIDomainHandler.cs都放在一个名为Services的文件夹下面。其他的项目必须提供具体的实现。

    domain程序集的代码就是这些了。

    定义domain事件handler

    我创建了另外一个项目用来写event handler。

    EndOfSurveyHandler用来处理EndOfSurvey事件:

    public class EndOfSurveyHandler:IDomainHandler<EndOfSurvey>
    {
        public void Handle(EndOfSurvey args)
        {
            args.Survey.QualityChecker = "Ivan Amalo";
            // 发送邮件给Ivan,通知他来检查调查问卷
        }
    }
    

    如果想使用repository进行一些数据持续化的工作,或者使用一些其他的服务,可以将这些repository和服务通过构造函数注入进来。

    EndOfSurveyHandlerEndOfSurvey事件是怎么联系起来的呢?

    将所有的代码集成起来

    下面要讲Survey.FrontEnd是一个MVC + WebApi应用,这个应用将DomainEvent,Dispatcher,Handler都结合了起来。

    这个项目依赖于Ninject.MVC3

    现在我们需要来实现在之前定义的IEventDispatcher

    public class NinjectEventContainer : IEventDispatcher
    {
        private readonly IKernel _kernel;
    
        public NinjectEventContainer(IKernel kernel)
        {
            _kernel = kernel;
        }
    
        public void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : IDomainEvent
        {
            foreach (var handler in _kernel.GetAll<IDomainHandler<TEvent>>())
            {
                handler.Handle(eventToDispatch);
            }
        }
    }
    

    Dispatch方法使用kernel来查找所有实现了IDomainHandler的handler。在我们的例子中查找的是EndOfSurveyHanlder,然后执行它的Handle()方法。

    NinjectWebCommon.cs中我们定义了handler和event的对应关系。

    private static void RegisterServices(IKernel kernel)
    {
        DomainEvent.Dispatcher = new NinjectEventContainer(kernel);
        kernel.Bind<IDomainHandler<EndOfSurvey>>().To<EndOfSurveyHandler>();
    }   
    

    这就是我们将所有东西集成起来需要做的事情。

    测试

    我在EndOfSurveyHandler.cs中发布事件的代码那设置了一个断点,来测试事件已经发布,其对应的handler也被执行。

    控制器的代码非常简单,如下:

    public ActionResult Index()
    {
        var survey = new Core.Domain.Survey();
        survey.EndSurvey();
    
        return View(survey);
    }
    

    执行这个action, Ivan Amalo应该被分配成为这个调查问卷的检查者,并且将EndDate设为当前时间。

  • 相关阅读:
    [洛谷P2184]贪婪大陆
    [BJOI2006]狼抓兔子
    [JSOI2007]重要的城市(x)
    [NOIP2011提高组]Mayan游戏
    gitee 使用
    部分激光打印机清零方法
    django2.0内置分页
    django上下文处理器
    jquery键盘事件
    类视图装饰器
  • 原文地址:https://www.cnblogs.com/irocker/p/domain-events-pattern-example.html
Copyright © 2011-2022 走看看