DDD分层架构之我见
DDD分层架构之我见
前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示。下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持。你不一定要使用DDD这样的架构,使用单层架构和普通三层架构一样可以,不过你如果希望获得更进一步的复用性和封装度,使用更加面向对象的技术是必经之程。
我在2010年以前还在使用古老的ASP.NET WebForm和原始的Ado.Net。之前我有个观念:.NET技术发展太快,跟着微软屁股后面跑太累,所以只使用它一些原始的东西,自己封装一下也能满足工作上的需求。对于像Linq这样的技术只是随便看了下,特别是当时很多人告诉我Linq已死,千万别学,我当时很喜欢这样的言论,因为不学习新知识就有了充分的理由。
到了2010年,我有一次上博客园,浏览了一些文章,发现充满各种缩写和名词,什么Dto、工作单元一类,我才知道新一代的.Net技术已经开始普及,我已经Out了。之后我开始学习EF和MVC,在刚开始接触EF的时候,我从一些博客了解到,为了发挥EF的威力,必须使用DDD进行设计。为了扫清拦路虎,我购买了几本DDD的书来学习,学习过程中才发现面向对象和敏捷开发才是关键,于是开始大量购书,一发不可收拾,四年时间买了接近两百本后,终于把基础补起来一点。
DDD的核心思想是描述如何使用面向对象的方法对业务领域建模,怎样获得更好的领域模型。虽然看了不少DDD的资料,但还是感觉它异常抽象,另外面向对象的思想也很难进步,可能和前些年的编程习惯有关,已经习惯于从数据的角度考虑问题,形成了思维定势。
DDD虽然抽象,但它还是提供了一些技术上的支持。大部分人都是从DDD分层架构入手来进行学习和实践,当然,DDD并不是分层架构,分层架构只是DDD的一个技术构造。下面简单介绍一下我对DDD分层架构的理解,由于我使用DDD的时间不长,我所描述的观点都是我自己的一些开发经验,不一定正确,欢迎各位高手批评指正,共同进步。
DDD分层架构总体上和三层架构相似,不过对各层提出了更具体的职责和构造块。我也经常与一些在使用DDD分层架构的朋友交流,我问他们DDD分层架构与普通三层架构有何区别,大部分人都感觉差不多,除了一些名词术语有所变化。如果你也是这个感觉,那么可能本文对你是有帮助的,因为我明显感觉出它们之间有所不同。
DDD分层架构与传统三层架构示意图如下。

(领域驱动设计分层架构示意图)

(传统三层架构示意图)
DDD分层架构与传统三层架构最重要的区别可能是重心不同,即传统三层架构的重心在业务逻辑层,而DDD分层架构的重心在领域层。
面向对象设计的核心是基于业务概念建模,并映射到代码中,这样的好处是减轻程序员将业务概念转换到技术的负担,因为更容易理解。传统三层架构虽然也把业务概念转换到实体层的Model对象,但实体层只是一个辅助设施,这些Model只是用来装数据的容器,作用并不显著。DDD分层架构把领域层提到核心地位,这些Model成为业务逻辑的一个主要放置场所。
使用DDD分层架构的第一个好处就是业务逻辑高度内聚到领域层,换句话说,如果有逻辑问题,找领域层就对了。对于这一点,有些人认为传统三层架构也可以,找业务逻辑层不是一样吗?这可能是大多数对领域驱动设计分层架构认识无法突破的关键。
虽然你可以按照分层架构的要求,把全部业务逻辑都写到BLL层,但你无法精确定位你需要的业务逻辑究竟处于什么位置,换句话说,你需要业务逻辑的一个唯一访问点。由于你无法轻易找到业务逻辑的访问点,所以产生冗余代码就再所难免,一段相同或相似的冗余代码会在多个地方产生,从而导致可维护性的降低。通过强制约束代码和目录规范以及提取公共方法可以缓解部分问题,但要从根本上解决,你还得向面向对象求救。
那么,哪里是业务逻辑最好的落脚点,最直观,最容易被大家想到的唯一访问点在哪呢?比如你要处理一个订单,让你到其它地方去找处理订单的代码,你自然找起来困难。那么如果这段代码处于订单实体的内部,情况就大不相同了,你可以在最短的时间内找到它。在领域实体中内聚业务逻辑,可以为你创建一个业务逻辑的唯一访问点。大家以后需要某个逻辑的时候,先看看实体中有没有自己需要的,这样就能显著降低代码冗余,从而更好维护。
所以,我的第一条DDD使用经验就是,使用充血模型,将业务逻辑尽量放到领域实体中。充血模型有很多争论,不过你大可不必理会别人的说法,自己实践才能出真知。用得不爽,你后面不用就是了,对你基本没啥影响。目前我使用充血模型,感觉它主要的问题是,如果采用分布式架构,比如中间采用WCF远程调用,需要通过一层专门的DTO来进行传输,而且需要增加一个远程外观的服务,会导致工作量上升。
当把充血模型用起来以后,下一步是要把聚合用起来。聚合这个概念很好理解,就是包含关系。在UML中有两种包含关系,第一种叫聚合,表示比较弱的包含关系,聚合内部的东西在外面可以直接访问。第二种叫组合,即组成聚合,是很强的包含关系,表示外部的对象由内部的多个子对象组成,内部的子对象在外面不能直接访问,必须通过外部的对象间接引用。DDD虽然用了聚合这个词,但它表示UML中的组成聚合,所以它把外部的对象称为根,即聚合根,要访问内部对象,必须先访问聚合根。
概念上的理解,除了能吹吹牛以外,没多大帮助。我在刚接触DDD的时候,也能理解聚合的概念,说起来一样口沫横飞,但真正用起来过了差不多一年。除了我反应比较迟钝以外,还有一个原因是被之前以数据为中心的思维定势所束缚。
我也经常下载一些DDD的Demo来学习,但是这些例子大多都非常简单,所以我主要还是依靠看书和自己摸索。我刚开始的用法是一个表对应一个领域实体,每个领域实体对应一个仓储。我在使用的过程中,隐隐发现哪里不对,但是无法找出具体的原因。经过大半年,我也使用DDD开发了几个简单的项目,逐步积累了一些经验,在一次看书的时候,我突然领悟到我的DDD用法主要毛病是依赖关系混乱,而解决这些依赖关系的手段就是聚合。
聚合的主要影响是显著减少仓储数量,以及集中管理高度依赖的相关实体。把高度相关的实体内聚到一个聚合中,可以把这些依赖关系封装到一个更小的空间,外部只与聚合根打交道,与聚合内部子对象的依赖关系就会明显降低。一个聚合对应一个仓储,而不是一个实体对象一个仓储,可以减少仓储数量,从而进一步降低依赖关系。
后面我重新阅读了一些博客和书籍,发现别人其实都说清楚了,只是自己当时看过去没有理解而已,这真是纸上得来终觉浅 绝知此事要躬行。
我的第二条DDD使用经验是,把高度相关的实体封装到聚合中,为每个聚合根创建一个仓储。
观察上面的DDD分层架构示意图,会发现领域层只依赖于应用程序框架服务,仓储采用了接口分离模式将实现和接口分离到不同的程序集,领域层中只包含仓储的接口,这个设计让领域层非常纯净,和外部的依赖关系降到最低。这对我们意味着什么?更低的依赖让我们可以方便的对业务逻辑进行单元测试,特别是采用了TDD方式的话,这一点将显得尤其重要。我们可以在单元测试中使用模拟框架对仓储以及外部依赖进行模拟测试,从而大幅度提升业务逻辑的稳定性和健壮性。
另外,观察传统三层架构,业务逻辑层一般直接依赖数据访问层,让单元测试变得困难,从而转向更粗粒度的集成测试。
通过上面的分析,可以看到采用DDD分层架构可以获得比传统三层架构更好的可复用性、可维护性、可测试性等。
当然不可能把所有业务逻辑全部放入领域实体中,有些功能需要操作多个实体,或者需要使用某些设计模式,这时候需要使用领域服务。这里的要点是尽量把领域服务的操作委托给领域实体,因为这样业务逻辑可以更加集中。
DDD分层架构还有一些构造块,我会在后面的文章详细介绍。如果没有介绍到的,说明我还处于学习和摸索阶段,还没有多少心得,等我有些经验以后再告诉大家。
现在来总结一下。
使用DDD分层架构有哪些好处
- 帮你更集中的管理业务逻辑。
- 帮你降低各层间,以及各业务模块间的依赖关系。
- 帮你更方便的进行单元测试。
我的DDD分层架构使用经验
- 使用充血模型,将业务逻辑尽量放到领域实体中,领域实体为业务逻辑提供一个唯一访问点。
- 不能放入领域实体的逻辑,尽量放到领域服务,总之,业务逻辑应该高度内聚到领域层。
- 把高度相关的实体封装到聚合中,为每个聚合根创建一个仓储。
最后,提醒一下,我们使用一些DDD分层架构构造块,可能并不算真正用上了DDD。但是,我们的目标是使用DDD吗?不是,我们的目标是把业务逻辑做得更稳定,更好维护。所以不用在意自己使用的技术正不正宗,标不标准,只要比以前更好,就应该坚持下去。
.Net应用程序框架交流QQ群: 386092459,欢迎有兴趣的朋友加入讨论。
谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/xiadao521/
DDD分层架构之我见的更多相关文章
- 应用程序框架实战十三:DDD分层架构之我见
前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示.下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持.你不一定要使用DDD这样的架构 ...
- 应用程序框架实战十三:DDD分层架构之我见(转)
前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示.下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持.你不一定要使用DDD这样的架构 ...
- 应用程序框架实战十八:DDD分层架构之聚合
前面已经介绍了DDD分层架构的实体和值对象,本文将介绍聚合以及与其高度相关的并发主题. 我在之前已经说过,初学者第一步需要将业务逻辑尽量放到实体或值对象中,给实体“充血”,这样可以让业务逻辑高度内聚, ...
- 应用程序框架实战十七:DDD分层架构之值对象(层超类型篇)
上一篇介绍了值对象的基本概念,得到了一些朋友的支持,另外也有一些朋友提出了不同意见.这其实是很自然的事情,设计本来就充满了各种可能性,没有绝对正确的做法,只有更好的实践.但是设计与实践的好与坏,对于不 ...
- 应用程序框架实战十六:DDD分层架构之值对象(介绍篇)
前面介绍了DDD分层架构的实体,并完成了实体层超类型的开发,同时提供了验证方面的支持.本篇将介绍另一个重要的构造块——值对象,它是聚合中的主要成分. 如果说你已经在使用DDD分层架构,但你却从来没有使 ...
- 应用程序框架实战十五:DDD分层架构之领域实体(验证篇)
在应用程序框架实战十四:DDD分层架构之领域实体(基础篇)一文中,我介绍了领域实体的基础,包括标识.相等性比较.输出实体状态等.本文将介绍领域实体的一个核心内容——验证,它是应用程序健壮性的基石.为了 ...
- 应用程序框架实战十四:DDD分层架构之领域实体(基础篇)
上一篇,我介绍了自己在DDD分层架构方面的一些感想,本文开始介绍领域层的实体,代码主要参考自<领域驱动设计C#2008实现>,另外参考了网上找到的一些示例代码. 什么是实体 由标识来区分的 ...
- DDD分层架构的进化
.NET逻辑分层架构演示:DDD分层架构的进化 概述: 架构是高层的设计,如果设计和理解有误,必将在实现时带来各种问题.架构又是最稳定的,不会因为各种具体技术的依赖,如各种UI框架.ORM框架.I ...
- DDD分层架构之仓储
DDD分层架构之仓储(层超类型基础篇) 前一篇介绍了仓储的基本概念,并谈了我对仓储的一些认识,本文将实现仓储的基本功能. 仓储代表聚合在内存中的集合,所以仓储的接口需要模拟得像一个集合.仓储中有很多操 ...
随机推荐
- Linux概念架构的理解(转)
英文原文:Conceptual Architecture of the Linux Kernel 摘要 Linux kernel成功的两个原因:(1)架构设计支持大量的志愿开发者加入到开发过程中:(2 ...
- HTTP 长连接 使用场景
offer 80 非常多应用譬如监控.即时通信.即时报价系统都须要将后台发生的变化实时传送到client而无须client不停地刷新.发送请求. 在 多好科技的那位技术指导问我这个是由于他们做物连网, ...
- UOJ #5. 【NOI2014】动物园 扩大KMP
第一次NOI称号. ... 扩展假设知道KMP如果. .. . 就是水题了. ... #5. [NOI2014]动物园 统计提交情况 描写叙述 提交 近日.园长发现动物园中好吃懒做的动物越来越多了.比 ...
- Https 客户端与服务器交互过程梳理(转)
本文试图以通俗易通的方式介绍Https的工作原理,不纠结具体的术语,不考证严格的流程.我相信弄懂了原理之后,到了具体操作和实现的时候,方向就不会错,然后条条大路通罗马.阅读文本需要提前大致了解对称加密 ...
- javascript中常用坐标属性offset、scroll、client
原文:javascript中常用坐标属性offset.scroll.client 今天在学习js的时候觉得这个问题比较容易搞混,所以自己画了一个简单的图,并且用js控制台里面输出测试了下,便于理解. ...
- 第9章 组合模式(Composite Pattern)
原文 第9章 组合模式(Composite Pattern) 概述: 组合模式有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理 ...
- Firefox firebug and xpath checker
From http://blog.sina.com.cn/s/blog_5aefba9a0100csy8.html
- Host和Server的开发
Host和Server的开发 对于开发人员来说,代码就是最好的文档,如上一篇博文所说,下面我们就会基于Kanata项目的一些具体调用代码,来进一步深入理解OWIN的实现和作用. 今天我们先针对Host ...
- C--指针数组
一个变量有一个地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,他们都有相应的地址,所谓数组的指针是指数组的其实地址,数组元素的指针是数组元素的地址. 一个数组是有连续的一块内存单元组成 ...
- Java-继承特性
继承的特点: 1.提高了代码的复用性. 2.让类与类之间发生了关系,有了这个关系,才有了多态的特性. (注意:千万不要为了获取其他类的功能,简化代码而继承:必须是类与类之间有所属关系才可以继承,所属关 ...