开始DDD
连续写了两篇文章,这一篇我想是序的完结篇了。结合用户注册的例子再将他简单丰富一下。在这里只添加一个简单需求,就是用户注册成功后给用户发一封邮件。补充一下之前的代码

public class DomainService
{
public void Register(User user)
{
if (_userRepository.IsLoginIdExist(user.LoginId)) {
throw new Exception("用户名已存在");
} _userRepository.Add(user);
MailService.Send(user.Email, "邮件内容");
}
}

上面的代码是存在一点问题的,了解DDD的人都知道,此时user并没有持久化或者持久化是否成功是不确定的,假设此时持久化user失败了,但邮件却发送出去了,这显然不是我们想要的结果。怎么办?我能想到的是两种办法。
第一种:创建一个发送邮件的model。

public class MailMessage
{
public MailMessage(string receiver, string content)
{
this.Receiver = receiver;
this.Content = content;
} public string Receiver { get; private set; }
public string Content { get; private set; }
} public class DomainService
{
public void Register(User user)
{
if (_userRepository.IsLoginIdExist(user.LoginId)) {
throw new Exception("用户名已存在");
} _userRepository.Add(user);
_mailRepository.Add(new MailMessage(user.Email, "邮件内容"));
}
}

在添加用户的时候同时添加一条邮件消息,这样他们将会在同一个事务中,要么一起成功,要么一起失败。最后再设计个计划任务,从邮件记录表中取出记录依次发送邮件,发送成功的可以标记一下,至于怎么做就不细讲了。
第二种:就是这一篇我要介绍的使用事件。

public class UserRegistered : IEvent
{
public UserRegistered(string name, string email)
{
this.Name = name;
this.Email = email;
} public string Name { get; private set; }
public string Email { get; private set; }
} public class UserRegisteredHandler : IEventHandler<UserRegistered>
{
public void Handle(UserRegistered @event)
{
//TODO.. 发送邮件
}
} public class User : IEventPublisher
{
private readonly IList<IEvent> _uncommittedEvents = new List<IEvent>();
IEnumerable<IEvent> IEventPublisher.Events
{
get { return this._uncommittedEvents; }
} public User(string name, string password, string email)
{
this.Name = name;
this.Password = password;
this.Email = email; _uncommittedEvents.Add(new UserRegistered(name, email));
} public string Name { get; private set; }
public string Password { get; private set; }
public string Email { get; private set; }
}

这样用户注册会产生一个事件。持久化成功后,会将事件发布出去,这样EventBus就会监听并处理此事件。上面的代码可能阅读理解起来不是那么的直白,具体的实现起来也并非就这么简单,只是提出一种方法。具体实现我的开源代码里也有相关例子http://thinknet.codeplex.com
总结
以上三篇文章我也主要是从写代码的角度去介绍如何DDD,强调一下我不是在教你如何写代码,只是为了展示用DDD如何实现,领域里的模型更应该能表达业务,他的价值更并不仅于此。而且以上的描述不一定完全正确,也不是告诉你一定要如何做,这也需要你自己的思考,如果有不对的地方欢迎你的指正,毕竟DDD我在学习过程中,也能从中受益。
如果我们过多的精力花在如何写代码上,可能是收集的工具类库还不强大,或者是还没有一个能够方便快捷开发的框架,当然一个好的框架带来的好处会很多。一个框架终究是有办法和技术能力去实现完成的,但是如何分析和理解业务,然后从中挖掘出便于阅读和表达业务的模型确定一件不容易的事情,他并不是通过某种技术办法就能实现的。所以我个人觉得设计模型,划分界限上下文是需要不断的积累领域业务知识才能做到的。
“领域驱动设计”和“实现领域驱动”这两本书应该是最经典的了,知识点也很多,阅读此书你会得到更多的收获!
随笔分类 - ThinkNet
开始DDD的更多相关文章
- 如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑
		阅读目录 前言 场景1的思考 场景2的思考 避坑方式 实践 结语 一.前言 在上一篇中(如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成),有一行注释的代码: public interfa ... 
- 如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成
		阅读目录 前言 建模 实现 结语 一.前言 前面几篇已经实现了一个基本的购买+售价计算的过程,这次再让售价丰满一些,增加一个会员价的概念.会员价在现在的主流电商中,是一个不大常见的模式,其带来的问题是 ... 
- 如何一步一步用DDD设计一个电商网站(十)—— 一个完整的购物车
		阅读目录 前言 回顾 梳理 实现 结语 一.前言 之前的文章中已经涉及到了购买商品加入购物车,购物车内购物项的金额计算等功能.本篇准备把剩下的购物车的基本概念一次处理完. 二.回顾 在动手之前我对之 ... 
- 如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念
		一.前言 DDD(领域驱动设计)的一些介绍网上资料很多,这里就不继续描述了.自己使用领域驱动设计摸滚打爬也有2年多的时间,出于对知识的总结和分享,也是对自我理解的一个公开检验,介于博客园这个平 ... 
- 如何一步一步用DDD设计一个电商网站(七)—— 实现售价上下文
		阅读目录 前言 明确业务细节 建模 实现 结语 一.前言 上一篇我们已经确立的购买上下文和销售上下文的交互方式,传送门在此:http://www.cnblogs.com/Zachary-Fan/p/D ... 
- 如何一步一步用DDD设计一个电商网站(六)—— 给购物车加点料,集成售价上下文
		阅读目录 前言 如何在一个项目中实现多个上下文的业务 售价上下文与购买上下文的集成 结语 一.前言 前几篇已经实现了一个最简单的购买过程,这次开始往这个过程中增加一些东西.比如促销.会员价等,在我们的 ... 
- 如何一步一步用DDD设计一个电商网站(五)—— 停下脚步,重新出发
		阅读目录 前言 单元测试 纠正错误,重新出发 结语 一.前言 实际编码已经写了2篇了,在这过程中非常感谢有听到观点不同的声音,借着这个契机,今天这篇就把大家提出的建议一个个的过一遍,重新整理,重新出发 ... 
- 如何一步一步用DDD设计一个电商网站(四)—— 把商品卖给用户
		阅读目录 前言 怎么卖 领域服务的使用 回到现实 结语 一.前言 上篇中我们讲述了“把商品卖给用户”中的商品和用户的初步设计.现在把剩余的“卖”这个动作给做了.这里提醒一下,正常情况下,我们的每一步业 ... 
- 如何一步一步用DDD设计一个电商网站(三)—— 初涉核心域
		一.前言 结合我们本次系列的第一篇博文中提到的上下文映射图(传送门:如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念),得知我们这个电商网站的核心域就是销售子域.因为电子商务是以信息网络 ... 
- 如何一步一步用DDD设计一个电商网站(二)—— 项目架构
		阅读目录 前言 六边形架构 终于开始建项目了 DDD中的3个臭皮匠 CQRS(Command Query Responsibility Segregation) 结语 一.前言 上一篇我们讲了DDD的 ... 
随机推荐
- Intent常用使用汇总
			方法一:调用默认的短信程序Intent intent = new Intent(Intent.ACTION_VIEW);intent.setType("vnd.android-dir/mms ... 
- VisualC++2012 Compiler Warning C4566
			现象: 今天敲代码突然遇到这样一个警告: warning C4566: ユニバーサル文字名 '\u0642' によって表示されている文字は.現在のコード ページ (932) で表示できません 意思是说 ... 
- ListView单选的实现总结(转)
			今天在智能停车场项目中需要实现PullToRefreshListView的单选功能,考虑到分页,刷新等,以前的实现方式是采用自己维护一个集合保存选中位置的选中状态,但这个方式比较繁琐,今天采用了lis ... 
- Oracle性能优化学习笔记WHERE在连接顺序的条款
			ORACLE自下而上分析顺序WHERE条款,根据这一原理,表之间的连接必须写在其它WHERE先决条件, 这些条件可以过滤掉要被写入记录的最大数目WHERE在条款结束. 比如: (低效, ... 
- HDU 2082-找单词(母函数)
			找单词 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submis ... 
- ASP.NET MVC:Razor 引入命名空间
			原文:ASP.NET MVC:Razor 引入命名空间 页面中引用 c# @using MvcApplication83.Models @using MvcApplication83.Common 行 ... 
- 实现双8bit数据指定的位置0要么1
			<pre name="code" class="cpp"> 方法一 #include<stdio.h> #include<math ... 
- SpringMVC一路总结(
			SpringMVC一路总结(三) 在博文<SpringMVC一路总结(一)>和<SpringMVC一路总结(二)>中,该框架的应用案例都是是基于xml的形式实现的.然而,对于大 ... 
- 教你一步一步部署.net免费空间OpenShift系列之二------创建应用
			接上回书,注册完毕后需要在Openshift上创建一个应用空间,如如何创建空间呢,Openshift本身是不直接支持Mono来部署ASP.Net程序的,需要借助openshift-community- ... 
- linux Packet socket (1)简单介绍
			本文主要来自于linux自带的man packet手冊: http://man7.org/linux/man-pages/man7/packet.7.html 平时常常使用的INET套接字提供的是7层 ... 
