0%

如何开始DDD(完) - young.han - 博客园

Excerpt

连续写了两篇文章,这一篇我想是序的完结篇了。结合用户注册的例子再将他简单丰富一下。在这里只添加一个简单需求,就是用户注册成功后给用户发一封邮件。补充一下之前的代码 上面的代码是存在一点问题的,了解DDD的人都知道,此时user并没有持久化或者持久化是否成功是不确定的,假设此时持久化user失败了,但


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

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
<span>public</span> <span>class</span><span> DomainService
{
</span><span>public</span> <span>void</span><span> Register(User user)
{
</span><span>if</span><span> (_userRepository.IsLoginIdExist(user.LoginId)) {
</span><span>throw</span> <span>new</span> Exception(<span>"</span><span>用户名已存在</span><span>"</span><span>);
}

_userRepository.Add(user);
MailService.Send(user.Email, </span><span>"</span><span>邮件内容</span><span>"</span><span>);
}
}</span>

复制代码

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

第一种:创建一个发送邮件的model。

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<span>public</span> <span>class</span><span> MailMessage
{
</span><span>public</span> MailMessage(<span>string</span> receiver, <span>string</span><span> content)
{
</span><span>this</span>.Receiver =<span> receiver;
</span><span>this</span>.Content =<span> content;
}

</span><span>public</span> <span>string</span> Receiver { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>string</span> Content { <span>get</span>; <span>private</span> <span>set</span><span>; }
}

</span><span>public</span> <span>class</span><span> DomainService
{
</span><span>public</span> <span>void</span><span> Register(User user)
{
</span><span>if</span><span> (_userRepository.IsLoginIdExist(user.LoginId)) {
</span><span>throw</span> <span>new</span> Exception(<span>"</span><span>用户名已存在</span><span>"</span><span>);
}

_userRepository.Add(user);
_mailRepository.Add(</span><span>new</span> MailMessage(user.Email, <span>"</span><span>邮件内容</span><span>"</span><span>));
}
}</span>

复制代码

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

第二种:就是这一篇我要介绍的使用事件。

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<span>public</span> <span>class</span><span> UserRegistered : IEvent
{
</span><span>public</span> UserRegistered(<span>string</span> name, <span>string</span><span> email)
{
</span><span>this</span>.Name =<span> name;
</span><span>this</span>.Email =<span> email;
}

</span><span>public</span> <span>string</span> Name { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>string</span> Email { <span>get</span>; <span>private</span> <span>set</span><span>; }
}

</span><span>public</span> <span>class</span> UserRegisteredHandler : IEventHandler&lt;UserRegistered&gt;<span>
{
</span><span>public</span> <span>void</span><span> Handle(UserRegistered @event)
{
</span><span>//</span><span>TODO.. 发送邮件 </span>
<span> }
}

</span><span>public</span> <span>class</span><span> User : IEventPublisher
{
</span><span>private</span> <span>readonly</span> IList&lt;IEvent&gt; _uncommittedEvents = <span>new</span> List&lt;IEvent&gt;<span>();
IEnumerable</span>&lt;IEvent&gt;<span> IEventPublisher.Events
{
</span><span>get</span> { <span>return</span> <span>this</span><span>._uncommittedEvents; }
}

</span><span>public</span> User(<span>string</span> name, <span>string</span> password, <span>string</span><span> email)
{
</span><span>this</span>.Name =<span> name;
</span><span>this</span>.Password =<span> password;
</span><span>this</span>.Email =<span> email;

_uncommittedEvents.Add(</span><span>new</span><span> UserRegistered(name, email));
}

</span><span>public</span> <span>string</span> Name { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>string</span> Password { <span>get</span>; <span>private</span> <span>set</span><span>; }
</span><span>public</span> <span>string</span> Email { <span>get</span>; <span>private</span> <span>set</span><span>; }
}</span>

复制代码

这样用户注册会产生一个事件。持久化成功后,会将事件发布出去,这样EventBus就会监听并处理此事件。上面的代码可能阅读理解起来不是那么的直白,具体的实现起来也并非就这么简单,只是提出一种方法。具体实现我的开源代码里也有相关例子https://github.com/imyounghan/thinknet[
](http://thinknet.codeplex.com/)

总结

以上三篇文章我也主要是从写代码的角度去介绍如何DDD,强调一下我不是在教你如何写代码,只是为了展示用DDD如何实现,领域里的模型更应该能表达业务,他的价值更并不仅于此。而且以上的描述不一定完全正确,也不是告诉你一定要如何做,这也需要你自己的思考,如果有不对的地方欢迎你的指正,毕竟DDD我在学习过程中,也能从中受益。

如果我们过多的精力花在如何写代码上,可能是收集的工具类库还不强大,或者是还没有一个能够方便快捷开发的框架,当然一个好的框架带来的好处会很多。一个框架终究是有办法和技术能力去实现完成的,但是如何分析和理解业务,然后从中挖掘出便于阅读和表达业务的模型确定一件不容易的事情,他并不是通过某种技术办法就能实现的。所以我个人觉得设计模型,划分界限上下文是需要不断的积累领域业务知识才能做到的。

“领域驱动设计”和“实现领域驱动”这两本书应该是最经典的了,知识点也很多,阅读此书你会得到更多的收获!