原文

完整源码

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

领域对象

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设为当前时间。

[译]Domain Events Pattern Example的更多相关文章

  1. ABP(现代ASP.NET样板开发框架)系列之14、ABP领域层——领域事件(Domain events)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之14.ABP领域层——领域事件(Domain events) ABP是“ASP.NET Boilerplate P ...

  2. ABP领域层——领域事件(Domain events)

    ABP领域层——领域事件(Domain events) 基于DDD的现代ASP.NET开发框架--ABP系列之14.ABP领域层——领域事件(Domain events) ABP是“ASP.NET B ...

  3. ABP的事件总线和领域事件(EventBus & Domain Events)

    http://www.aspnetboilerplate.com/Pages/Documents/EventBus-Domain-Events EventBus EventBus是个单例,获得Even ...

  4. [译]C#7 Pattern Matching

    原文 public struct Square { public double Side { get; } public Square(double side) { Side = side; } } ...

  5. 领域驱动设计(Domain Driven Design)参考架构详解

    摘要 本文将介绍领域驱动设计(Domain Driven Design)的官方参考架构,该架构分成了Interfaces.Applications和Domain三层以及包含各类基础设施的Infrast ...

  6. [转载]领域驱动设计(Domain Driven Design)参考架构详解

    摘要 本文将介绍领域驱动设计(Domain Driven Design)的官方参考架构,该架构分成了Interfaces.Applications和Domain三层以及包含各类基础设施的Infrast ...

  7. 领域驱动设计常见问题FAQ

    本文出处:http://www.cqrs.nu/Faq What is a domain? The field for which a system is built. Airport managem ...

  8. CQRS FAQ (翻译)

    我从接触ddd到学习cqrs有6年多了, 其中也遇到了不少疑问, 也向很多的前辈牛人请教得到了很多宝贵的意见和建议. 偶尔的机会看到国外有个站点专门罗列了ddd, cqrs和事件溯源的常见问题. 其中 ...

  9. A JavaFX based Game Authoring System

    http://www.mirkosertic.de/doku.php/javastuff/javafxgameauthoring ——————————————————————————————————— ...

随机推荐

  1. 群福利:Redis云服务器免费领取(附Redis安装和连接远程连接Redis案例)

    Redis安装:在线体验:https://try.redis.io Ubuntu:sudo apt-get install redis CentOS:yum install redis (root权限 ...

  2. SQL查询语句的进阶使用

    MySQL的进阶使用 sql语句一些功能的使用 导入现有大量数据文件步骤 1) 把*.sql文件拷贝到Linux某一位置(例如Desktop) 2) Linux命令行进入该位置 cd ~/Deskto ...

  3. kafka 流式计算

    http://www.infoq.com/cn/articles/kafka-analysis-part-7/ Kafka设计解析(七)- 流式计算的新贵 Kafka Stream

  4. Transactional ejb 事务陷阱

    对应ejb,默认是对整个类使用事务.所以所有方法都开启事务. 而对于用TransactionAttribute注释来引用容器管理的事务,只能在第一级的方法中使用.对应类中的方法再调用其它类中方法,注释 ...

  5. P3378 堆の模板

    如果不是可并堆/带修堆/卡常题,一般都用优先队列实现. 很多O(nlogn)过不了的题都可以用蚯蚓的套路来实现!!! 优先队列带修用延迟删除法. 堆,可以简单的用优先队列来实现,也可以自己手打. #i ...

  6. 浏览器直接显示html代码,不解析代码

    在某些时候,我们可能因为某些特殊的原因,不想让浏览器解析html代码. 1,把代码放到js中,如下 <script type='text/html' style='display:block'& ...

  7. 关于react-native项目在MacBookPro环境下打包成IPA

    苹果开发者打包是需要接入公司的开发者账户里面的.看是企业账户还是什么,具体我不太清楚. 不过打包的方法倒是大同小异. 我们一起新建项目,先跑起来这个项目 npm install -g yarn rea ...

  8. python基础-守护进程、守护线程、守护非守护并行

    守护进程 1.守护子进程 主进程创建守护进程  其一:守护进程会在主进程代码执行结束后就终止  其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic pro ...

  9. Spring3 (事务管理)

    简介: 1.事务管理.2.整合Junit.3.整和Web 1       事务管理 1.1   回顾事务 l  事务:一组业务操作ABCD,要么全部成功,要么全部不成功. l  特性:ACID 原子性 ...

  10. pytest 3.fixture介绍一 conftest.py

    前言: 前面一篇pytest2 讲到用例加setup和teardown可以实现在测试用例之前或之后加入一些操作,但这种是整个脚本全局生效的,如果我想实现以下场景: 用例1需要先登录,用例2不需要登录, ...