概述
本文将介绍领域驱动设计(DDD)战术模式中另一个非常重要的概念 - 领域服务。在前面两篇博文中,我们已经学习到了什么是值对象和实体,并且能够比较清晰的定位它们自身的行为。但是在某些时候,你会发现某一些业务行为好像不容易落到单个实体或者值对象身上,并且会为放置这一部分业务逻辑而困惑。此时,你可能需要一个领域服务来完成操作。
那么,到底什么是领域服务呢?怎么发现领域中的领域服务呢?领域服务和传统的应用服务又有什么区别呢?本文将从不同的角度来带大家重新认识一下“领域服务”这个概念,并且给出相应的代码片段(本教程的代码片段都使用的是C#,后期的实战项目也是基于 DotNet Core 平台)。
什么是领域服务
在开始之前,还是说一点题外话吧:如果大家读过这个系列的前几篇文章,可能都会发现该系列的风格都是从原著的解析开始,然后结合了自身的一些案例和实际场景来为大家解读领域驱动中的一些概念。我也不知道这样的写作方式能不能让大家更清楚的理解,所以如果大家有什么建议的话可以在评论区留言,我一定会认真的听取大家的意见和建议。
在文章中,我会尽可能避免各类名称的简写(比如事件溯源,有些同学喜欢简写为ES),虽然简写有时候确实会很方便,但是会让人与人之间的沟通成本无形的增大,所以在我的博文中只要能不用简写的地方我都不会使用简写。
另外还有一点就是,可能前期属于概念性的东西比较多,所以就没有现成的github代码供大家参考,不多大家不用担心,在完成这几次的概念学习之后我们就开始我们的code time(●’◡’●)。
回到正题吧,什么是领域服务呢?看看原著原著《领域驱动设计:软件核心复杂性应对之道》中所提及到的领域服务的概念:
在某些情况下,最清楚、最实用的设计会包含一些特殊的操作,这些操作从概念上讲不属于任何对象。与其把它们强制地归于哪一类,不如顺其自然地在模型中引入一种新的元素,这就是Service(服务)。
当领域中的某个要的过程或转换操作不属于实体或值对象的自然职责时,应该在模型中添加一个作为独立接口的操作,并将其声明为Service.定义接口时要使用模型语言,并确保操作名称是UBIQUITOUS LANGUAGE中的术语。此外,应该将Service定义为无状态的。
额。。。。“李姐万岁”。这个概念不好理解的原因是因为:首先它假设我们寻找到了领域中一些“包含特殊的操作”,也就是说我们在此时已经具备了划分领域中各种对象以及其对应行为的能力,然后我们再来考虑提取出这个传说中的“service”(也就是我们本次的主题领域服务)。而往往现实则是,作为一个初学者,我们并不能合理的抽象出各个对象,并且也没有一个好的案例来进行体验性的思考。所以在读这个概念的时候就很迷惑,我们无法找到概念中的“这些操作”是什么东西,也就更不能理解这个Service是什么了。
“在自己的私人飞机里面玩儿电子游戏是什么感觉呢? 呃.....好像前提是我得有钱买一架飞机吧?”
从实际场景下手
我思考了很多种方法来表述“领域服务”,但是想了半天好像都不太容易能让人理解。所以该篇博文采用先从案例入手的思路,希望大家能从这个案例能够理解出领域服务的用处。
来回顾一下上一篇文章 《如何运用DDD - 实体》 中我们所提炼出来的一个实体对象:
public class Itinerary
{
public int ID { get; set; }
public List<Person> Participants { get; set; }
public List<Address> Places { get; set; }
public ItineraryNote Note { get; set; }