一缕阳光:DDD(领域驱动设计)应对具体业务场景,如何聚焦 Domain Model(领域模型)?

写在前面

  阅读目录:

  在上一篇《我的“第一次”,就这样没了:DDD(领域驱动设计)理论结合实践》博文中,简单介绍了领域驱动设计的一些理念,并简单完成基于领域驱动设计的具体项目 MessageManager,本人在设计 MessageManager 项目之前,并没有看过 Eric Evans 的《Domain-Driven Design –Tackling Complexity in the Heart of Software》和 Martin Fowler 的《Patterns of Enterprise Application Architecture》,《企业应用架构模式》这本书正在阅读,关于领域驱动设计,主要学习来源是园中的 netfocus、dax.net、以及清培兄的部分博文(小弟先在此谢过各位大神的无私奉献),还有就是解道中的领域驱动设计专题,当然还有一些来自搜索引擎的部分资料,再加上自己的一些揣摩和理解,也就成为了属于自己的“领域驱动设计”。

  MessageManager 项目是对自己所理解领域驱动设计的检验,如果你仔细看过上一篇博文,你会发现 MessageManager 其实只是领域驱动设计的“外壳”,就像我们种黄瓜,首先要搭一个架子,以便黄瓜的生长,MessageManager 项目就相当于这个架子,核心的东西“黄瓜”并不存在,当时在设计完 MessageManager 项目的时候,其实已经发现问题的存在,所以在博文最后留下了下面两个问题:

  • Domain Model(领域模型):领域模型到底该怎么设计?你会看到,MessageManager 项目中的 User 和 Message 领域模型是非常贫血的,没有包含任何的业务逻辑,现在网上很多关于 DDD 示例项目多数也存在这种情况,当然项目本身没有业务,只是简单的“CURD”操作,但是如果是一些大型项目的复杂业务逻辑,该怎么去实现?或者说,领域模型完成什么样的业务逻辑?什么才是真正的业务逻辑?这个问题很重要,后续探讨。
  • Application(应用层):应用层作为协调服务层,当遇到复杂性的业务逻辑时,到底如何实现,而不使其变成 BLL(业务逻辑层)?认清本质很重要,后续探讨。

  另外再贴一些园友们在上一篇的问题评论:

  关于以上的问题,本篇博文只是做一些解读,希望可以对那些痴迷于领域驱动设计的朋友们一些启示,写得有不当之处,也欢迎指出。

问题根源是什么?

  出现上述问题的原因是什么?需求简单?设计不合理?准确的来说,应该都不是,我觉得问题的根源是没有真正去理解领域驱动设计,或者说没有真正用领域驱动设计的理念去设计或实现,领域驱动设计的概念网上一找一大堆,你看过几篇文章之后也能写出来之类的文章,为什么?因为都是泛泛之谈,诸如:领域模型是领域驱动的核心;领域驱动基本分为四层(用户层、应用层、领域层和基础层);领域包含实体、值对象和服务;还有一些聚合和聚合根之类的概念等等,文中也会给你列出一些关于这些概念的代码实现,让你瞬间感觉原来领域驱动设计是这么的高大上。

  但如果拿这些概念去实践呢?却根本不是那么回事,现在使用领域驱动设计去开发的企业实在是太少了,原因有很多种,下面大致列出一些:

  • 开发成本太高,换句话说,就是如果使用领域驱动设计开发,需要聘请高级程序员、高级架构师和建模专家,一般这种开发人员薪资都比较高,老板真的舍得吗?
  • 开发周期长,花在需求分析的时间比较长,甚至比程序实现还要长,这个对老板来说是要命的,开发周期长,一般会意味着公司的利润降低,公司利润降低,老板的钱包就瘪了,老板会愿意吗?
  • 开发思维转变问题,使用领域驱动设计开发,需要公司里的程序员懂得领域驱动设计,要对面向对象(OO)设计有一定的理解,现实情况是,大部分的程序员虽然使用的是面向对象语言(比如 Java、C#),却做着面向过程的事(类似 C 语言函数式的开发)。现在让公司的程序员使用领域驱动设计开发,就好比以前是用手直接吃饭,现在让你使用筷子吃饭,你会习惯吗?这需要一种转变,很多程序员会很不习惯,这也是领域驱动设计推行难的主要原因。
  • 关于领域驱动设计实践经验实在太少,大家脑子中只有模模糊糊的概念,却没有实实在在的实践,像 dax.net 这样去完成几个完整基于领域驱动设计项目的大神实在太少了,很多都是像我一样,理解一些概念后,放出一个简单的示例 Demo,然后就没有然后了。

  Eric Evans 在2004年提出 DDD(领域驱动设计)的理念,距今已经十年了,推广却停滞不前,确实值得我们程序员去反思。

  扯得有点远了,回到这个副标题:问题的根源是什么?答案或许会不令你满意,就是没有真正理解领域驱动设计。那你或许会问:那真正的领域驱动设计是什么?这个我想只有 Eric Evans 可以回答,但也不要把领域驱动设计看得这么绝对,领域驱动设计只是一种指导,具体的实现要用具体的方法,正如有句古话:师傅领进门,修行在个人。每个人有每个人的具体悟道,但再变化也不要忘了师出同门。

  还有一点就是,有朋友指出简单的业务逻辑是体现不出领域驱动设计的,关于这一点首先我是比较赞同的,但如果去拿一些大型业务场景去做领域驱动设计的示例,我个人觉得也不太现实,毕竟时间成本太高了。我个人认为小的业务场景和大的业务场景都可以使用领域驱动设计实现,只是业务逻辑的复杂度不同,还有就是适用度也不同,小的业务场景用脚本驱动模式去实现,可能会比领域驱动设计区实现更简单、快速,但是但凡是业务场景(不论大小),必然包含业务逻辑(CRUD 除外),那也就可以使用领域驱动设计去开发,还是那句话,只是不太适合,但做演示示例还是可以的。

  业务逻辑的复杂度主要体现在领域模型中,复杂性的业务逻辑,领域模型也就越复杂,但与简单性的领域模型实质是一样的。关于如何真正理解领域驱动设计?这一点我个人觉得方式就是“迭代”,只有不断的去实践,不断的去体会,才能真正的去理解领域驱动设计,就像 MessageManager 项目,每一次有些体会我就会觉得这样做不合理,那就推倒重建,可能这样做又不合理,那就推倒再重建。。。

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

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

  注:这一节点是我后面添加的,真是天意,在我写这篇博客的时候,正好有位不知名的朋友,发消息说他看到我之前的一篇博文,我在文中跪求《领域驱动设计-软件核心复杂性应对之道》这本书,因为网上没得买。正好他有 Word 版,虽然内容有些错别字,但是真心感谢这位不知名的朋友。大致阅读了下目录结构,确实是我想要的,接下来会认真的拜读,有实质书的话当然更好,下面是摘自这本书的分层概念。

  在面向对象的程序中,用户界面(UI)、数据库和其他支持代码,经常被直接写到业务对象中去。在UI和数据库脚本的行为中嵌入额外的业务逻辑。出现这种情况是因为从短期的观点看,它是使系统运行起来的最容易的方式。当与领域相关的代码和大量的其他代码混在一起时,就很难阅读并理解了。对UI的简单改动就会改变业务逻辑。改变业务规则可能需要小心翼翼地跟踪UI代码、数据库代码或者其他的程序元素。实现一致的模型驱动对象变得不切实际,而且自动化测试也难以使用。如果在程序的每个行为中包括了所有的技术和逻辑,那么它必须很简单,否则会难以理解。

  将一个复杂的程序进行层次划分。为每一层进行设计,每层都是内聚的而且只依赖于它的下层。采用标准的架构模式来完成与上层的松散关联。将所有与领域模型相关的代码都集中在一层,并且将它与用户界面层、应用层和基础结构层的代码分离。领域对象可以将重点放在表达领域模型上,不需要关心它们自己的显示、存储和管理应用任务等内容。这样使模型发展得足够丰富和清晰,足以抓住本质的业务知识并实现它。

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

  一个对象所代表的事物是一个具有连续性和标识的概念(可以跟踪该事物经历的不同的状态,甚至可以让该事物跨越不同的实现),还是只是一个用来描述事物的某种状态的属性?这就是实体与值对象最基本的区别。明确地选用这两种模式中的一种来定义对象,可以使对象的意义更清晰,并可以引导我们构造出一个健壮的设计。

  另外,领域中还存在很多的方面,如果用行为或操作来描述它们会比用对象来描述更加清晰。尽管与面向对象建模理念稍有抵触,但这些最好是用服务来描述,而不是将这个操作的职责强加到某些实体或值对象身上。服务用来为客户请求提供服务。在软件的技术层中就有许多服务。服务也会在领域中出现,它们用于对软件必须完成的一些活动进行建模,但是与状态无关。有时我们必须在对象模型中釆取一些折衷的措施——这是不可避免的,例如利用关系数据库进行存储时就会出现这种情况。本章将会给出一些规则,当遇到这种复杂情况时,遵守这些规则可以使我们保持正确的方向。

  最后,我们对模块(Module)的讨论可以帮助理解这样的观点:每个设计决策都应该是根据对领域的正确理解来做出。高内聚、低关联这种思想往往被看成是理想的技术标准,它们对于概念本身也是适用的。在模型驱动的设计中,模块是模型的一部分,它们应该能够反映出领域中的概念。

Repository(仓储)职责所在?

  言归正题。

  Repository(仓储)的概念可以参考:http://www.cnblogs.com/dudu/archive/2011/05/25/repository_pattern.html,我个人比较赞同 dudu 的理解:Repository 是一个独立的层,介于领域层与数据映射层(数据访问层)之间。它的存在让领域层感觉不到数据访问层的存在,它提供一个类似集合的接口提供给领域层进行领域对象的访问。Repository 是仓库管理员,领域层需要什么东西只需告诉仓库管理员,由仓库管理员把东西拿给它,并不需要知道东西实际放在哪。

  关于 Repository 的定义,在《企业应用架构模式》书中也有说明:协调领域和数据映射层,利用类似于集合的接口来访问领域对象。书中把 Repository 翻译为资源库,其实是和仓储是一个意思,关于 Repository这一节点的内容,我大概阅读了两三篇才理解了部分内容(这本书比较抽象难理解,需要多读几遍,然后根据自己的理解进行推敲揣摩),文中也给出了一个示例:查找一个人所在的部门(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 messageR
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值