应用程序框架实战十三: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吗?不是,我们的目标是把业务逻辑做得更稳定,更好维护。所以不用在意自己使用的技术正不正宗,标不标准,只要比以前更好,就应该坚持下去。

http://www.cnblogs.com/xiadao521/p/4098100.html

时间: 2024-09-20 20:55:37

应用程序框架实战十三:DDD分层架构之我见(转)的相关文章

应用程序框架实战三十四:数据传输对象(DTO)介绍及各类型实体比较(转)

  本文将介绍DDD分层架构中广泛使用的数据传输对象Dto,并且与领域实体Entity,查询实体QueryObject,视图实体ViewModel等几种实体进行比较. 领域实体为何不能一统江湖? 当你阅读我或其它博主提供的示例代码时,会发现几种类型的实体,这几种实体初步看上去区别不大,只是名称不同,特别在这些示例非常简单的情况下更是如此.你可能会疑惑为何要搞得这么复杂,采用一种实体不是更好? 在最理想的情况下,我们只想采用领域实体Entity进行所有的操作. 领域实体是领域层的核心,是业务逻辑的

.NET逻辑分层架构总结

  本人将从另一个角度来解析.NET分层架构的真正奥秘.分层,一些技术功底比较薄弱的程序员听到分层就会联想到三层架构(BLL,DAL之类的),其实不是,分层是一个很大的技术框架思想,三层架构只不过是对普通的信息系统来说,将信息的流转通过三层来分解, 一.基础知识准备: 1.层的原则: (1)每一层以接口方式供上层调用. (2)上层只能调用下层. (3)依赖分为松散交互和严格交互两种. 2.业务逻辑分类: (1)应用逻辑. (2)领域逻辑. 3.采用的层: (1)表示层(用户接口层):领域无关.

.NET逻辑分层架构总结_实用技巧

一.基础知识准备: 1.层的原则: (1)每一层以接口方式供上层调用. (2)上层只能调用下层. (3)依赖分为松散交互和严格交互两种. 2.业务逻辑分类: (1)应用逻辑. (2)领域逻辑. 3.采用的层: (1)表示层(用户接口层):领域无关. (2)服务层(应用层):应用逻辑. (3)业务逻辑层(领域层):领域逻辑. (4)共享层:提供通用代码. (5)实现层:提供接口实现. 4.约定: (1)领域层默认采用领域模型 (2)数据访问层默认需要引用领域模型 二.分层架构 分层架构的三个基本层

基于.NET平台的分层架构实战(九)—数据访问层的第三种实现:基于NBear框架

基于.NET平台的分层架构实战(九)-数据访问层的第三种实现:基于NBear框架的ORM实现 前面的文章讨论了使用SQL语句和存储过程两种数据访问层的实现方式,这一篇里,将讨论使用ORM方式实现数据访问层的方法. 对象-关系映射(Object/Relation Mapping,简称ORM),是随着面向对象的软件开发方法发展而产生的.面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统.对象和关系数据是业务实体的两种表现形式,业务实

基于.NET平台的分层架构实战(七-外一篇)—对数据访问层第一种实现(Access+

基于.NET平台的分层架构实战(七-外一篇)-对数据访问层第一种实现(Access+SQL)的重构 昨天的文章基于.NET平台的分层架构实战(七)--数据访问层的第一种实现:Access+SQL发布后,很多朋友对我的程序提出了意见和建议,在这里先谢谢你们!!!尤其是金色海洋(jyk),对我的程序提出了很多建设性的意见. 我大体总结了一下,昨天程序的主要缺点有: 1.Connection对象没有关闭 2.DataReader对象没有关闭 3.相似代码太多,造成代码冗余. 其中第一点问题,目前还没有

基于.NET平台的分层架构实战

基于.NET平台的分层架构实战(十一)-表示层的实现 基于.NET平台的分层架构实战(十)-业务逻辑层的实现 基于.NET平台的分层架构实战(九)-数据访问层的第三种实现:基 基于.NET平台的分层架构实战(八)-数据访问层的第二种实现:SQ 基于.NET平台的分层架构实战(七-外一篇)-对数据访问层第一种 基于.NET平台的分层架构实战(七)-数据访问层的第一种实现:Acc 基于.NET平台的分层架构实战(六)-依赖注入机制及IoC的设计 基于.NET平台的分层架构实战(五)-接口的设计与实现

基于.NET平台的分层架构实战(八)—数据访问层的第二种实现:SQLServer+存储

基于.NET平台的分层架构实战(八)-数据访问层的第二种实现:SQLServer+存储过程 在上一篇中,讨论了使用SQL构建数据访问层的方法,并且针对的是Access数据库.而这一篇中,将要创建一个针对SQLServer数据库的数据访问层,并且配合存储过程实现. 曾经有朋友问我使用SQL和存储过程在效率上的差别,惭愧的是我对这方面没有研究,也没有实际做过测试.通过查阅资料,发现在一般情况下,存储过程的效率由于使用SQL,但是也不绝对,也发现有的朋友测试时发现在特定情况下SQL的效率优于存储过程,

基于.NET平台的分层架构实战(六)—依赖注入机制及IoC的设计

我们设计的分层架构,层与层之间应该是松散耦合的.因为是单向单一调用, 所以,这里的"松散耦合"实际是指上层类不能具体依赖于下层类, 而应该依赖于下层提供的一个接口.这样,上层类不能直接实例化下层中的类, 而只持有接口,至于接口所指变量最终究竟是哪一个类,则由依赖注入机制决定 . 之所以这样做,是为了实现层与层之间的"可替换"式设计 ,例如,现在需要换一种方式实现数据访问层,只要这个实现遵循了前面定义的 数据访问层接口,业务逻辑层和表示层不需要做任何改动,只需要改一下

基于.NET平台的分层架构实战(五)—接口的设计与实现

接下来,将进行接口的设计.这里包括数据访问层接口和业务逻辑层接口.在 分层架构中,接口扮演着非常重要的角色,它不但直接决定了各层中的各个操作 类需要实现何种操作,而且它明确了各个层次的职责.接口也是系统实现依赖注 入机制不可缺少的部分. 本项目的接口设计将按如下顺序进行: 1.首先由前文的需求分析,列出主要的UI部分. 2.分析各个UI需 要什么业务逻辑支持,从而确定业务逻辑层接口. 3.分析业务逻辑层接口 需要何种数据访问操作,从而确定数据访问层接口. 另外,为保证完全的 面向对象特性,接口之