世界驱动设计

写在前头

图片 1

  阅读目录:

  在上一篇《自个儿的“第三次”,就像此没了:DDD(领域驱动设计)理论结合履行》博文中,简单介绍了世界驱动设计的片段眼光,并简短已毕基于领域驱动设计的切实可行品种 MessageManager,本身在统筹
MessageManager 项目事先,并没有看过 埃里克埃文思 的《Domain-Driven Design
–Tackling Complexity in the Heart of Software》和 马丁 Fowler的《Patterns of Enterprise Application
Architecture》,《集团应用架构情势》那本书正在读书,关于世界驱动设计,首要学习来源是园中的
netfocus、dax.net、以及清培兄的一些博文(堂弟先在此谢过各位大神的无私贡献),还有就是解道中的领域驱动设计专题,当然还有一些出自搜索引擎的有的资料,再增加本人的局地衡量和明白,也就成为了属于本身的“领域驱动设计”。

  MessageManager
项目是对协调所精晓领域驱动设计的查验,假如你仔细看过上一篇博文,你会发现 MessageManager
其实只是小圈子驱动设计的“外壳”,似乎大家种黄瓜,首先要搭1个作风,以便黄瓜的发育,MessageManager
项目就相当于那一个架子,大旨的东西“黄瓜”并不设有,当时在统筹完 MessageManager
项目标时候,其实已经意识难点的留存,所以在博文最终留给了上边五个难点:

  • Domain
    Model(领域模型):领域模型到底该怎么设计?你会看到,MessageManager
    项目中的 User 和 Message
    领域模型是卓殊贫血的,没有包括其余的作业逻辑,今后网上广大关于 DDD
    示例项目超过半数也设有那种状态,当然项目自个儿没有工作,只是简单的“CUEnclaveD”操作,但是尽管是部分大型项目标复杂工作逻辑,该怎么去贯彻?恐怕说,领域模型达成什么样的工作逻辑?什么才是的确的作业逻辑?那个题材很关键,后续商量。
  • Application(应用层):应用层作为协调服务层,当遇到复杂的作业逻辑时,到底怎么兑现,而不使其成为
    BLL(业务逻辑层)?认清本质很重大,后续商讨。

  其余再贴一些园友们在上一篇的题材评论:

图片 2

图片 3

图片 4

  关于以上的题材,本篇博文只是做一些解读,希望得以对那个迷恋于天地驱动设计的意中人们某个启发,写得有不当之处,也欢迎提议。

难点源于是如何?

图片 5

  出现上述难题的因由是什么样?需要不难?设计不客观?准确的来说,应该都不是,我认为题材的来自是没有真的去明白领域驱动设计,或许说没有真的用世界驱动设计的看法去规划或落到实处,领域驱动设计的概念网上一找一大堆,你看过几篇文章之后也能写出来之类的作品,为啥?因为都以泛泛而论,诸如:领域模型是世界驱动的为主;领域驱动基本分为四层(用户层、应用层、领域层和基础层);领域涵盖实体、值对象和服务;还有一些集合和聚合根之类的定义等等,文中也会给您列出一些有关那几个概念的代码落成,让你弹指间感觉到原来领域驱动设计是这么的铁汉上。

  但假若拿这么些概念去履行吧?却根本不是那么回事,未来应用世界驱动设计去开发的店铺实际是太少了,原因有广大种,上面大概列出一些:

  • 开发开销太高,换句话说,就是假设利用领域驱动设计开发,需求聘请高级程序员、高级架构师和建模专家,一般那种开发人士薪俸都相比较高,首席营业官确实舍得啊?
  • 开发周期长,花在须要分析的时刻比较长,甚至比程序已毕还要长,这几个对总老板来说是可怜的,开发周期长,一般会代表公司的赢利下跌,公司利润下跌,COO的钱包就瘪了,主管会甘愿呢?
  • 支出考虑转变难点,使用领域驱动设计开发,须要集团里的程序员明白领域驱动设计,要对面向对象(OO)设计有一定的了解,现真实情况形是,一大半的程序员纵然应用的是面向对象语言(比如
    Java、C#),却做着面向进度的事(类似 C
    语言函数式的支付)。将来让商家的程序员使用世界驱动设计开发,就好比之前是用手间接开饭,未来让您使用筷子吃饭,你会习惯吗?那须要一种变更,很多程序员会很不习惯,那也是圈子驱动设计执行难的主要原因。
  • 至于世界驱动设计实践经验实在太少,我们脑子中只有模模糊糊的概念,却绝非如实的施行,像
    dax.net
    那样去已毕多少个完整基于领域驱动设计项目标大神实在太少了,很多都是像自个儿同一,了解一些定义后,放出一个简易的以身作则
    Demo,然后就没有然后了。

  埃里克 埃文思 在二〇〇四年指出 DDD(领域驱动设计)的见解,于今已经十年了,推广却裹足不前,确实值得大家程序员去反省。

  扯得有点远了,回到那一个副标题:题材的源于是何等?答案大概会不令你称心,就是从未当真驾驭领域驱动设计。那您恐怕会问:那的确的世界驱动设计是什么?那些作者想唯有埃里克 Evans可以回答,但也绝不把世界驱动设计看得这么相对,领域驱动设计只是一种指点,具体的贯彻要用具体的主意,正如有句古话:师傅领进门,修行在个人。每种人有逐个人的有血有肉悟道,但再转变也决不忘了师出同门。

  还有少数就是,有对象提议简单的政工逻辑是展示不出领域驱动设计的,关于那或多或少第一自个儿是比较协理的,但万一去拿一些大型业务场景去做领域驱动设计的言传身教,作者个人觉得也不太现实,终归时间资产太高了。小编个人认为小的事务场景和大的事情场景都可以运用领域驱动设计落成,只是工作逻辑的复杂度差异,还有就是适费用也不比,小的业务场景用脚本驱动形式去贯彻,只怕会比世界驱动设计区已毕更简便易行、快捷,不过但凡是业务场景(不论高低),必然包括业务逻辑(CRUD
除外),那也就足以采用世界驱动设计去开发,照旧那句话,只是不太相符,但做示范示例依然得以的。

  业务逻辑的复杂度主要显示在领域模型中,复杂性的作业逻辑,领域模型也就越复杂,但与不难性的领域模型实质是同等的。关于如何真正驾驭领域驱动设计?那点本身个人认为方法就是“迭代”,唯有时时刻刻的去履行,不断的去体会,才能真正的去掌握领域驱动设计,就像MessageManager
项目,每四遍某个体会作者就会觉得那样做不客观,那就推倒重建,或者这么做又不创立,那就推倒再重建。。。

  闲话少说,来看本次的“迭代”:

《领域驱动设计-软件基本复杂性应对之道》分层概念

  注:这一节点是本人背后添加的,真是造化,在本身写那篇博客的时候,正好有位不盛名的爱侣,发新闻说他看到自身事先的一篇博文,我在文中跪求《领域驱动设计-软件基本复杂性应对之道》那本书,因为网上没得买。正好他有
Word
版,固然内容有点错别字,不过真诚谢谢这位不出名的朋友。大概阅读了下目录结构,确实是自己想要的,接下去会认真的拜读,有实质书的话当然更好,上边是摘自那本书的分段概念。

图片 6

  在面向对象的次第中,用户界面(UI)、数据库和别的接济代码,经常被平素写到业务对象中去。在UI和数据库脚本的一坐一起中嵌入额外的事体逻辑。出现那种状态是因为从长期的眼光看,它是使系统运营起来的最简单的法子。当与世界有关的代码和多量的其他代码混在一齐时,就很难阅读并知道了。对UI的粗略改动就会变动工作逻辑。改变工作规则只怕需求翼翼小心地跟踪UI代码、数据库代码只怕其余的主次成分。已毕平等的模型驱动对象变得不切实际,而且自动化测试也难以使用。假诺在程序的种种行为中包罗了有着的技能和逻辑,那么它必须很简短,否则会难以知晓。

  将2个扑朔迷离的次序进行层次划分。为每一层开展统筹,每层都以内聚的还要只依靠于它的下层。选用正式的架构形式来完毕与上层的涣散关联。将兼具与天地模型相关的代码都集中在一层,并且将它与用户界面层、应用层和底蕴结构层的代码分离。天地对象可以将首要放在表达领域模型上,不须求关心它们自身的来得、存储和治本采用任务等情节。那样使模型发展得充裕丰盛和清晰,足以吸引实质的业务知识并贯彻它。

用户界面层(表示层) 负责向用户显示信息,并且解析用户命令。外部的执行者有时可能会是其他的计算机系统,不一定非是人。
应用层 定义软件可以完成的工作,并且指挥具有丰富含义的领域对象来解决问题。这个层所负责的任务对业务影响深远,对跟其他系统的应用层进行交互非常必要这个层要保持简练。它不包括处理业务规则或知识,只是给下一层中相互协作的领域对象协调任务、委托工作。在这个层次中不反映业务情况的状态,但反映用户或程序的任务进度的状态
领域层(模型层) 负责表示业务概念、业务状况的信息以及业务规则。尽管保存这些内容的技术细节由基础结构层来完成,反映业务状况的状态在该层中被控制和使用。这一层是业务软件的核心。
基础结构层 为上层提供通用的技术能力:应用的消息发送、领域持久化,为用户界面绘制窗口等。通过架构框架,基础结构层还可以支持这四层之间的交互模式。

  一个目的所代表的东西是四个富有三番五次性和标识的定义(可以跟踪该事物经历的不等的图景,甚至可以让该事物跨越不相同的贯彻),如故只是二个用来讲述事物的某种意况的习性?那就是实体与值对象最大旨的不一样。明确地选拔那二种形式中的一种来定义对象,可以使对象的含义更鲜明,并得以指引大家组织出三个健康的陈设性。

  其它,领域中还设有重重的地点,要是用行止或操作来叙述它们会比用对象来叙述特别清晰。固然与面向对象建模理念稍有龃龉,但那个不过是用劳动来描述,而不是将以此操作的任务强加到一点实体或值对象身上。服务用来为客户请求提供劳务。在软件的技术层中就有过多劳务。服务也会在圈子中出现,它们用于对软件必须形成的一些平移拓展建模,可是与气象非亲非故。有时大家必须在目的模型中釆取一些低头的点子——那是不可逆袭的,例如使用关周到据库进行仓储时就会现出那种状态。本章将会付给一些条条框框,当蒙受那种复杂情状时,遵从那些规则可以使大家维持正确的主旋律。

  最终,我们对模块(Module)的议论可以资助了然这样的理念:逐个规划决策都应当是基于对世界的正确领会来做出。高内聚、低关联那种思考往往被当做是优异的技术标准,它们对于概念本身也是适用的。在模型驱动的规划中,模块是模型的一局地,它们应该力所能及反映出世界中的概念。

Repository(仓储)义务所在?

  言归正题。

  Repository(仓储)的定义可以参照:http://www.cnblogs.com/dudu/archive/2011/05/25/repository_pattern.html,小编个人比较赞同
dudu 的了解:Repository
是1个独自的层,在于领域层与数码映射层(数据访问层)之间。它的留存让世界层备感不到多少访问层的存在,它提供一个像样集合的接口提必要世界层开展领域对象的拜会。Repository
是仓库管理员,领域层需要什么东西只需告诉仓库管理员,由仓库管理员把东西拿给它,并不须求知道东西实际放在哪。

  关于 Repository
的概念,在《集团应用架构形式》书中也有认证:协调领域和数目映射层,利用类似于聚集的接口来拜会领域对象。书中把
Repository 翻译为能源库,其实是和储存是二个意味,关于 Repository 这一节点的情节,作者大体阅读了两三篇才晓得了部分内容(那本书比较空虚难精晓,必要多读几次,然后根据本身的领会举行推敲切磋),文中也交由了2个演示:查找一位所在的机关(Java),以便于强化对
Repository 的通晓。

  我们先看一下 Repository
的定义前半句:协调领域和多少映射层,也等于 dudu
所说的在于领域层和数据映射层之间,明白这点很首要,相当关键。然后大家再来看
MessageManager 项目中关于 Repository
的施用(完结没反常),在哪应用呢?依照定义大家相应要辞世界层去找 Repository 的选用,但是大家在
MessageManager.Domain 项目中找不到关于 Repository
的半毛应用,却在 MessageManager.Application
项目中找到了:

  1 /**
  2 * author:xishuai
  3 * address:https://www.github.com/yuezhongxin/MessageManager
  4 **/
  5 
  6 using System;
  7 using System.Collections.Generic;
  8 using AutoMapper;
  9 using MessageManager.Application.DTO;
 10 using MessageManager.Domain;
 11 using MessageManager.Domain.DomainModel;
 12 using MessageManager.Domain.Repositories;
 13 
 14 namespace MessageManager.Application.Implementation
 15 {
 16     /// <summary>
 17     /// Message管理应用层接口实现
 18     /// </summary>
 19     public class MessageServiceImpl : ApplicationService, IMessageService
 20     {
 21         #region Private Fields
 22         private readonly IMessageRepository messageRepository;
 23         private readonly IUserRepository userRepository;
 24         #endregion
 25 
 26         #region Ctor
 27         /// <summary>
 28         /// 初始化一个<c>MessageServiceImpl</c>类型的实例。
 29         /// </summary>
 30         /// <param name="context">用来初始化<c>MessageServiceImpl</c>类型的仓储上下文实例。</param>
 31         /// <param name="messageRepository">“消息”仓储实例。</param>
 32         /// <param name="userRepository">“用户”仓储实例。</param>
 33         public MessageServiceImpl(IRepositoryContext context,
 34             IMessageRepository messageRepository,
 35             IUserRepository userRepository)
 36             :base(context)
 37         {
 38             this.messageRepository = messageRepository;
 39             this.userRepository = userRepository;
 40         }
 41         #endregion
 42 
 43         #region IMessageService Members
 44         /// <summary>
 45         /// 通过发送方获取消息列表
 46         /// </summary>
 47         /// <param name="userDTO">发送方</param>
 48         /// <returns>消息列表</returns>
 49         public IEnumerable<MessageDTO> GetMessagesBySendUser(UserDTO sendUserDTO)
 50         {
 51             //User user = userRepository.GetUserByName(sendUserDTO.Name);
 52             var messages = messageRepository.GetMessagesBySendUser(Mapper.Map<UserDTO, User>(sendUserDTO));
 53             if (messages == null)
 54                 return null;
 55             var ret = new List<MessageDTO>();
 56             foreach (var message in messages)
 57             {
 58                 ret.Add(Mapper.Map<Message, MessageDTO>(message));
 59             }
 60             return ret;
 61         }
 62         /// <summary>
 63         /// 通过接受方获取消息列表
 64         /// </summary>
 65         /// <param name="userDTO">接受方</param>
 66         /// <returns>消息列表</returns>
 67         public IEnumerable<MessageDTO> GetMessagesByReceiveUser(UserDTO receiveUserDTO)
 68         {
 69             //User user = userRepository.GetUserByName(receiveUserDTO.Name);
 70             var messages = messageRepository.GetMessagesByReceiveUser(Mapper.Map<UserDTO, User>(receiveUserDTO));
 71             if (messages == null)
 72                 return null;
 73             var ret = new List<MessageDTO>();
 74             foreach (var message in messages)
 75             {
 76                 ret.Add(Mapper.Map<Message, MessageDTO>(message));
 77             }
 78             return ret;
 79         }
 80         /// <summary>
 81         /// 删除消息
 82         /// </summary>
 83         /// <param name="messageDTO"></param>
 84         /// <returns></returns>
 85         public bool DeleteMessage(MessageDTO messageDTO)
 86         {
 87             messageRepository.Remove(Mapper.Map<MessageDTO, Message>(messageDTO));
 88             return messageRepository.Context.Commit();
 89         }
 90         /// <summary>
 91         /// 发送消息
 92         /// </summary>
 93         /// <param name="messageDTO"></param>
 94         /// <returns></returns>
 95         public bool SendMessage(MessageDTO messageDTO)
 96         {
 97             Message message = Mapper.Map<MessageDTO, Message>(messageDTO);
 98             message.FromUserID = userRepository.GetUserByName(messageDTO.FromUserName).ID;
 99             message.ToUserID = userRepository.GetUserByName(messageDTO.ToUserName).ID;
100             messageRepository.Add(message);
101             return messageRepository.Context.Commit();
102         }
103         /// <summary>
104         /// 查看消息
105         /// </summary>
106         /// <param name="ID"></param>
107         /// <returns></returns>
108         public MessageDTO ShowMessage(string ID, string isRead)
109         {
110             Message message = messageRepository.GetByKey(ID);
111             if (isRead == "1")
112             {
113                 message.IsRead = true;
114                 messageRepository.Update(message);
115                 messageRepository.Context.Commit();
116             }
117             return Mapper.Map<Message, MessageDTO>(message);
118         }
119         #endregion
120     }
121 }

对,你已经发现了 Repository
的踪影,Repository
应用在应用层,那样就招致应用层和基础层(小编把数据持久化放在基础层了)通讯,忽略了最重大的圈子层,领域层在内部起到的效益最多相当于传递2个百般贫血的小圈子模型,然后经过
Repository
举行“CRUD”,那样的结果是,应用层不成为所谓的
BLL(常说的业务逻辑层)才怪,别的,因为工作逻辑都坐落应用层了,领域模型也变得更其贫血。

  以上分析可以回复上一篇中残留的难点:应用层作为协调服务层,当碰到复杂的政工逻辑时,到底怎么着贯彻,而不使其改为
BLL(业务逻辑层)?其实关于率先个难点(领域模型怎样设计不贫血)也是可以展开解答的,那么些后一节点有认证,关于这一星罗棋布难点的导致本人认为就是
Repository
设计,现身了严重和申辩偏移,以致于没有把设计重点发在业务逻辑上,在此和大家说声抱歉。

  关于“应用层中的业务逻辑”,比如上边那段代码:

 1         /// <summary>
 2         /// 查看消息
 3         /// </summary>
 4         /// <param name="ID"></param>
 5         /// <returns></returns>
 6         public MessageDTO ShowMessage(string ID, string isRead)
 7         {
 8             Message message = messageRepository.GetByKey(ID);
 9             if (isRead == "1")
10             {
11                 message.IsRead = true;
12                 messageRepository.Update(message);
13                 messageRepository.Context.Commit();
14             }
15             return Mapper.Map<Message, MessageDTO>(message);
16         }

  对,你曾经看出来了,查看新闻,要基于阅读人,然后判断是还是不是已读,尽管是读书人是收件人,并且音讯是未读状态,要把此音信置为已读状态,业务逻辑没什么难点,可是却放错了职务(应用层),应该放在领域层中(领域模型),其实那都以Repository
惹的祸,因为应用层根本未曾和天地层通讯,关于世界模型的设计下面节点有教学。

  看了上述的情节,是或不是稍稍:拨开大雾,见晴天的痛感?不明了您有没有?反正作者是有,关于
Repository
大家再精通的深一点,先看一下后半句的概念:采用类似于聚集的接口来做客领域对象。正如
dudu 驾驭的这么:Repository
是仓库管理员,领域层需求哪些东西只需告诉仓库管理员,由仓库管理员把东西拿给它,并不须求知道东西实际放在哪。可以如此精晓为 Repository
如同3个查询集合,只提供查询给世界层,不过大家发现在骨子里运用中 Repository
也提供了持久化操作,这或多或少的确让 Repository
有点无缘无故了,关于这点本人认为 CQ酷威S(Command Query Responsibility
Segregation)格局可以很好的解决,翻译为命令查询的天任务开,顾名思义,就是命令(持久化)和查询职分进行分离,因为自个儿从未对
CQ景逸SUVS
举行过探究,也并未看到过具体的以身作则,所以那边就不多说,但是本身觉得那是和领域驱动设计的无所不包组合,前面有空子可以探究下。

  说了那么多,那Repository(仓储)职务到底是什么?可以如此答复:Repository,请服务好
Domain,而且只限服务于她(幸免小三),他要怎样您要给哪些,为何?因为他是你二伯,跟着她有肉吃。

图片 7

Domain Model(领域模型)重新设计

  领域模型是小圈子驱动设计的中央,那一点是毋容置疑的,那世界模型中的宗旨是怎样?可能说完结的是怎么样?答案是事情逻辑,那事情逻辑又是何等?只怕说什么样的“业务逻辑”才能称之为真正含义上的作业逻辑,关于那些难题,在上一篇中残留如下:

领域模型到底该怎么统筹?你会看出,MessageManager
项目中的 User 和 Message
领域模型是至极贫血的,没有包涵其他的事情逻辑,以后网上广大有关 DDD
示例项目大多数也设有那种状态,当然项目自个儿失掉工作,只是不难的“CRUD”操作,然则假设是局地大型项目标扑朔迷离工作逻辑,该怎么去贯彻?大概说,领域模
型完毕什么样的业务逻辑?什么才是实在的事务逻辑?这几个题材很重点,后续商量。

  什么才是的确的事情逻辑?CRUD
?持久化?依旧诸如“GetUserByName、GetMessageByID”之类的询问,笔者个人感觉这么些都不是真正意义上的政工逻辑(注意,是个人感觉),因为各种品种会有“CRUD”、持久化,并不只限于某一种业务场景,像“GetUserByName、GetMessageByID”之类的询问只是查询,领悟了上边Repository 的觉得,你会发觉这几个查询工作应有是 Repository
做的,他是为世界模型服务的。

  说了那么多,那什么才是当真含义上的事务逻辑?小编个人感觉改变世界模型状态或行为的工作逻辑,才能称之为真正含义上的作业逻辑(注意,是个人感觉),比如本人在
Repository
节点中说过的2个演示:读取新闻,要依照当下阅读人和近年来新闻的情形来设置当前音信的意况,如果当前读书人为收件人和当前音讯为未读状态,就要把当下音讯状态设置为已读,之前那些工作逻辑的达成是在应用层中:

 1         /// <summary>
 2         /// 查看消息
 3         /// </summary>
 4         /// <param name="ID"></param>
 5         /// <returns></returns>
 6         public MessageDTO ShowMessage(string ID, string isRead)
 7         {
 8             Message message = messageRepository.GetByKey(ID);
 9             if (isRead == "1")
10             {
11                 message.IsRead = true;
12                 messageRepository.Update(message);
13                 messageRepository.Context.Commit();
14             }
15             return Mapper.Map<Message, MessageDTO>(message);
16         }

  这种完结情势就会把应用层变为所谓的
BLL(业务逻辑层)了,正确的格局已毕应有在 Domain
Model(领域模型)中,如下:

 1         /// <summary>
 2         /// 阅读消息
 3         /// </summary>
 4         /// <param name="CurrentUser"></param>
 5         public void ReadMessage(User CurrentUser)
 6         {
 7             if (!this.IsRead && CurrentUser.ID.Equals(ToUserID))
 8             {
 9                 this.IsRead = true;
10             }
11         }

  因为 MessageManager
这些类其余业务场景非凡简单,很多都是简约的 CRUD
操作,可以抽离出真正的事务逻辑实在太少,除了上面阅读音信,还有就是在发送消息的时候,要依据发送用户名和接受用户名,来安装音信的发送用户和收受用户的
ID 值,这么些操作从前大家也是在应用层中达成的,如下:

 1         /// <summary>
 2         /// 发送消息
 3         /// </summary>
 4         /// <param name="messageDTO"></param>
 5         /// <returns></returns>
 6         public bool SendMessage(MessageDTO messageDTO)
 7         {
 8             Message message = Mapper.Map<MessageDTO, Message>(messageDTO);
 9             message.FromUserID = userRepository.GetUserByName(messageDTO.FromUserName).ID;
10             message.ToUserID = userRepository.GetUserByName(messageDTO.ToUserName).ID;
11             messageRepository.Add(message);
12             return messageRepository.Context.Commit();
13         }

  革新在 Domain
Model(领域模型)中的已毕,如下:

 1         /// <summary>
 2         /// 加载用户
 3         /// </summary>
 4         /// <param name="sendUser"></param>
 5         /// <param name="receiveUser"></param>
 6         public void LoadUserName(User sendUser,User receiveUser)
 7         {
 8             this.FromUserID = sendUser.ID;
 9             this.ToUserID = receiveUser.ID;
10         }

  因为简单的 CRUD
操作不会发生变化,而那些事情逻辑会平时暴发变化,比如往消息中加载用户新闻,恐怕以后加载的是
ID
值,以往只怕会添加其他的用户值,比如:用户地理地点等等,那样大家倘诺去修改领域模型就足以了,应用层一点都不要求修改,借使照旧事先的贯彻方式,你会意识我们是必要求修改应用层的,领域模型只是壹个空壳。

Domain Service(领域服务)的投入

  关于 Domain
Service(领域服务)的定义,可以参见:http://www.cnblogs.com/netfocus/archive/2011/10/10/2204949.html#content_15,netfocus
兄关于世界服务讲解的很透彻,以下摘自个人感觉出色的有些:

  • 世界中的一些概念不太符合建模为对象,即归类到实体对象或值对象,因为它们本质上就是某些操作,一些动作,而不是事物。这个操作或动作往往会波及到多少个世界对象,并且要求协调那一个领域对象共同完毕这些操作或动作。如若强行将这一个操作职务分配给其他2个对象,则被分配的目标就是承受部分不应该承担的天职,从而会促成对象的职责不明了很凌乱。可是依照类的面向对象语言规定任何性质或行为都必须放在对象里面。所以咱们须求摸索一种新的格局来表示那种跨多少个对象的操作,DDD认为服务是二个很自然的范式用来对号入座那种跨七个对象的操作,所以就有了世界服务这一个方式。
  • 自个儿觉着模型(实体)与服务(场景)是对天地的一种划分,模型关心世界的个体行为,场景关怀世界的群体行为,模型关心世界的静态结构,场景关怀世界的动态作用。那也切合了切实可行中出现的各类现象,有动有静,有独立有合作。
  • 领域服务还有一个很关键的效果就是足以避免领域逻辑败露到应用层。

  别的还有多个用来表达应用层服务、领域服务、基础服务的义务分配的小示例:

  应用层服务

  1. 赢得输入(如1个XML请求);
  2. 发送消息给世界层服务,须求其完毕转帐的业务逻辑;
  3. 世界层服务处理成功,则调用基础层服务发送Email公告;

  领域层服务

  1. 拿到源帐号和目的帐号,分别通报源帐号和对象帐号进行扣除金额和伸张金额的操作;
  2. 提供再次来到结果给应用层;

  基础层服务

  1. 坚守应用层的伸手,发送Email通知;

  通过上述示范,可以很清晰的明亮应用层服务、领域服务、基础服务的天职,关于那个概念的接头,作者信任 netfocus
兄是通过广大执行得出的,因为未举办看这么些概念和执行过未来再看那一个概念,完全是例外的痛感。

  言归正传,为啥要投入 Domain
Service(领域服务)?领域服务在大家事先设计 MessageManager
项目标时候并不曾,其实作者脑海中一向是有其一概念,因为 Repository
的义务混乱,所以最终领域模型变得这么鸡肋,领域服务也就不曾子加,那为什么今后要投入圈子服务啊?因为
Repository
的职分分开,使得世界模型变成重中之重,因为应用层不和 Repository
通讯,应用层又不能够一贯和世界模型通讯,所以才会有世界服务的进入,也非得有天地服务的投入。通过地点概念的知情,你只怕会对天地服务的成效有早晚的接头,首先领域服务没有动静,唯有行为,他和
Repository
一样,也是为世界模型服务的,只不过他像多个外交官一样,须要和应用层打交道,用来协调领域模型和应用层,而 Repository
只是1个三姑,只是服务于世界模型。

  概念领会的大半了,大家来看一下切实的完结,以下是
MessageDomainService 领域服务中的一段代码:

1         public Message ShowMessage(string ID,User CurrentUser)
2         {
3             Message message = messageRepository.GetByKey(ID);
4             message.ReadMessage(userRepository.GetUser(new User { Name = CurrentUser.Name }));
5             messageRepository.Update(message);
6             messageRepository.Context.Commit();
7             return message;
8         }

  那段代码表示查看音讯,可以看来实际领域服务做的工作就是办事流程的主宰,注意是干活流程处理,并不是业务流程,业务流程
ReadMessage
是天地模型去达成的,领域模型的效果只是和谐。还有个问号就是,你会看出在圈子服务中拔取到了
Repository,在大家前面的授课中,Repository
不是只服务于天地模型呢?其实换个角度来看,领域服务也得以看做是世界模型的一种表现,Repository
未来重中之重提供的是询问集合和持久化,领域模型不可以本人操作,那这个工作只有领域服务去做到,关于那或多或少,就足以看到 Repository
的利用有点不太合理,不掌握使用 CQ中华VS
格局会不会是另一种情景。

  此外,你会看到这一段代码:messageRepository.Context.Commit();,那个是
Unit Of
Work(工作单元)的事体提交,那些工作是天地服务要做的吗?关于那或多或少是有一些疑点,在底下节点中有解读。

MessageManager.Domain.Tests 的加入

  关于单元测试可以参见:http://www.cnblogs.com/xishuai/p/3728576.html,MessageManager.Domain.Tests
单元测试在从前的 MessageManager
项目中并从未增进,不是不想添加,而是添加了不要紧意思,为何?因为前面的世界模型那么贫血,只是一对性质和字段,这添加单元测试有怎样看头?能测出来怎么着事物?当把工作聚焦在圈子模型上的时候,对世界的单元测试将会非凡的有须求。

  来看 DomainTest
单元测试的一些代码:

 1 using MessageManager.Domain.DomainModel;
 2 using MessageManager.Domain.DomainService;
 3 using MessageManager.Repositories;
 4 using MessageManager.Repositories.EntityFramework;
 5 using NUnit.Framework;
 6 using System;
 7 using System.Collections.Generic;
 8 using System.Linq;
 9 using System.Text;
10 
11 namespace MessageManager.Domain.Tests
12 {
13     [TestFixture]
14     public class DomainTest
15     {
16         [Test]
17         public void UserDomainService()
18         {
19             IUserDomainService userDomainService = new UserDomainService(
20                 new UserRepository(new EntityFrameworkRepositoryContext()));
21             List<User> users = new List<User>();
22             users.Add(new User { Name = "小菜" });
23             users.Add(new User { Name = "大神" });
24             userDomainService.AddUser(users);
25             //userDomainService.ExistUser();
26             //var user = userDomainService.GetUserByName("小菜");
27             //if (user != null)
28             //{
29             //    Console.WriteLine(user.Name);
30             //}
31         }
32     }
33 }

  其实上边小编贴的单元测试的代码有个别不客观,你会看出只是测试的持久化操作,这么些本该是基础层已毕的工作,应该由基础层的单元测试举办测试的,那世界层的单元测试测试的是哪些东西?应该是天地模型中的业务逻辑,比如
ReadMessage 内的操作:

1         [Test]
2         public void MessageServiceTest()
3         {
4             IMessageDomainService messageDomainService = new MessageDomainService(
5                 new MessageRepository(new EntityFrameworkRepositoryContext()),
6                 new UserRepository(new EntityFrameworkRepositoryContext()));
7             Message message = messageDomainService.ShowMessage("ID", new User { Name = "小菜" });
8             Console.WriteLine(message.IsRead);
9         }

Application Layer(应用层)的协调?

Application
Layer(应用层):定义软件能够做到的劳作,并且指挥具有丰硕意义的世界对象来缓解难点。这么些层所负责的职责对业务影响浓密,对跟其它系统的应用层举行交互非凡须要以此层要保持简练。它不包括处理工作规则或文化,只是给下一层中相互合作的圈子对象协调义务、委托工作。在这几个层次中不反映工作情况的情景,但反映用户或程序的义务进程的场所。

  以上是《领域驱动设计-软件基本复杂性应对之道》书中有关应用层给出的概念,应用层是很薄的一层,如若你的应用层很“厚”,那你的应用层设计就势必出现了难题。关于 Application
Layer(应用层)的施用,正如 埃里克 埃文思所说:不包括处理业务规则或文化,只是给下一层中相互合作的园地对象协调职责、委托工作。重点就是:不含有业务逻辑,协调职务。

  如若依据自个儿的精晓去设计应用层,很可能会像小编同样把它成为工作逻辑层,所以在陈设进度中肯定要谨记上面两点。不带有业务逻辑很好明白,前提是要明了什么才是确实的事情逻辑(下面有表明),前面一句协调义务又是怎么着意思呢?在印证中前边还有一句:在那些层次中不呈现工作意况的景况,但反映用户或程序的职分进度的处境。也等于工作流程的操纵,比如3个生产流程,应用层的意义似乎这一个生育工艺流程的控制器,具体生产什么它不须求管住,它一旦可以装配零件然后进行结合浮现给用户,仅此而已,画了一张示意图,以便大家的通晓:

图片 8

  别的,应用层因为要对表现层和领域层举行职务协调,那中间会波及到数码的目的转换,约等于DTO(数据传输对象),有关 DTO 的概念和 AutoMapper
的选拔可以参见:http://www.cnblogs.com/xishuai/tag/DTO\_AutoMapper,这一个干活儿是在应用层中开展拍卖的,就像是生产工艺流程,组装完产品后,必要对其进展打包才能开展浮现:

 1         /// 对应用层服务进行初始化。
 2         /// </summary>
 3         /// <remarks>包含的初始化任务有:
 4         /// 1. AutoMapper框架的初始化</remarks>
 5         public static void Initialize()
 6         {
 7             Mapper.CreateMap<UserDTO, User>();
 8             Mapper.CreateMap<MessageDTO, Message>();
 9             Mapper.CreateMap<User, UserDTO>();
10             Mapper.CreateMap<Message, MessageDTO>()
11                 .ForMember(dest => dest.Status, opt => opt.ResolveUsing<CustomResolver>());
12         }
13         public class CustomResolver : ValueResolver<Message, string>
14         {
15             protected override string ResolveCore(Message source)
16             {
17                 if (source.IsRead)
18                 {
19                     return "已读";
20                 }
21                 else
22                 {
23                     return "未读";
24                 }
25             }
26         }

Unit Of Work(工作单元)工作范围及达成?

  关于 Unit Of
Work(工作单元)的定义可以参见:http://www.cnblogs.com/xishuai/p/3750154.html

Unit Of
Work:维护受工作工作影响的目的列表,并协调变化的写入和出现难题的缓解。即管理对象的
CRUD 操作,以及对应的工作与出新难题等。Unit of Work
是用来解决世界模型存储和更改工作,而那些数据层业务并不属于世界模型自己装有的。

  工作单元的概念在《公司应用架构格局》中也有证实,定义如下:维护受工作工作影响的目标列表,并协调变化的写入和出现难题的缓解。概念的敞亮并没有怎么问题,小编想表达的是办事单元的劳作范围及怎么样完结?先说下办事范围,大家看下小编曾经画的一张工作单元的流程图:
图片 9

点击查看大图

  从示意图中得以看出,工作单元的界定是遏制
Repository 的,相当于说工作单元是不大概跨 Repository
提交业务的,只可以在同两个仓储内管理业务的一致性,如同大家采纳的
using(MessageManagerDbContext context = new MessageManagerDbContext())
一样,只是局限于那些 using
块,笔者曾在天地层的单元测试中做如下测试:

 1         [Test]
 2         public void DomainServiceTest()
 3         {
 4             IUserDomainService userDomainService = new UserDomainService(
 5                 new UserRepository(new EntityFrameworkRepositoryContext()));
 6             IMessageDomainService messageDomainService = new MessageDomainService(
 7                 new MessageRepository(new EntityFrameworkRepositoryContext()),
 8                 new UserRepository(new EntityFrameworkRepositoryContext()));
 9             List<User> users = new List<User>();
10             users.Add(new User { Name = "小菜" });
11             users.Add(new User { Name = "大神" });
12             userDomainService.AddUser(users);
13             messageDomainService.DeleteMessage(null);
14         }

  我在 MessageDomain瑟维斯中付出业务,因为事先 UserDomainService已经添加了用户,但是并从未添加用户成功,工作单元中的 Committed 值为
false,其实关于工作单元范围的题材,我今日并不曾强烈的想法,今后是受制在存储中,那提交的工作操作就无法不放在领域服务中,相当于:messageRepository.Context.Commit();,但是又会以为这么略带不创立,工作单元应该是贯通整个项目标,并不一定局限在某一存储中,而且工作的处理液应该放在应用层中,因为那是她的行事,协调工作流的拍卖。

  倘诺这种思维是未可厚非的话,落成起来确实有个别难度,因为未来O揽胜极光M(对象关系映射)使用的是
EntityFramework,所以做事单元的落成是很粗略的,也等于拔取 SaveChanges()
方法来交给业务,我在《集团应用架构情势》中看到工作单元的贯彻,书中列出了2个简易的例证,还只是聚众的保管,假如不使用部分
OHighlanderM 工具,达成起来就不仅是 SaveChanges()
一段代码的事了,太局限于技术了,确实是个难题。

  这一节点的故事情节只是指出某些疑难,并未有缓解的不二法门,希望前面可以探索下。

本子公布

  MessageManager
项目化解方案目录:

图片 10

  注:ASP.NET WebAPI
暂只包涵:获取发送放消息列表和取得接收方新闻列表。

  调用示例:

  WebAPI 客户端调用可以参照
MessageManager.WebAPI.Tests 单元测试项目中的示例调用代码。

  注:因为 GitHub 中对 MessageManager
项目展开了更新,假诺想看上一版本,下载地址:http://pan.baidu.com/s/1gd9WmUB,可以和水土保持版本对照下,方便学习。

  别的,《领域驱动设计.软件基本复杂性应对之道》Word
版本,下载地址:http://pan.baidu.com/s/1bnndOcR

后记

图片 11

  那篇博文不知不觉写二日了(周末),左手也有点不那么灵活了,假设再写下去,我们也该骂作者了(看得太讨厌),那就做一下总括吧:

  关于世界模型的筹划,小编个人感觉是小圈子驱动设计中最难的一些,你会看出眼下本身在 MessageManager
项目中唯有三个办法,一部分缘由是事情场景太简单,另一局地原因大概是自己设计的不创造,复杂性业务场景的园地模型是多少个类之间协调处理,并会有局地设计形式的加入,是特出复杂的。

  要求专注的有个别是,本篇以上内容并不是讲述
Domain
Model(领域模型)到底怎么促成?而是如何聚焦领域模型?唯有聚焦在圈子模型上,才能把世界模型设计的更客观,那也正是下一步需求探索的始末。

  可能这句话,真正了然和行使
DDD(领域驱动设计)的绝无仅有办法,个人感觉照旧“迭代”:不断的去实施,不断的去体会。不创造那就推倒重建,再不客观那就推倒再重建。。。

  如若您以为本篇文章对您具备帮忙,请点击右下部“推荐”,^_^

  参考资料:

相关文章