DDD(领域驱动设计)总结
基本概念:
  领域驱动设计(简称 ddd)概念来源于2004年著名建模专家eric evans发表的他最具影响⼒的书籍:《domain-driven design –tackling complexity in the heart of software》(中⽂译名:领域驱动设计—软件核⼼复杂性应对之道)⼀书。,书中提出了“领域驱动设计(简称 ddd)”的概念。
领域驱动设计⼀般分为两个阶段:
1.  以⼀种领域专家、设计⼈员、都能理解的“通⽤语⾔”作为相互交流的⼯具,在不断交流的过程中发现和挖出⼀些主要的领域概念,然后将这些概念设计成⼀个领域模型;
2.  由领域模型驱动,⽤代码来表现该领域模型。领域需求的最初细节,在功能层⾯通过领域专家的讨论得出。
领域驱动设计告诉我们,在通过软件实现⼀个业务系统时,建⽴⼀个领域模型是⾮常重要和必要的,因为领域模型具有以下特点:
1.  领域模型是对具有某个边界的领域的⼀个抽象,反映了领域内⽤户的本质;领域模型是有边界的,只反应了我们在领域内所关注的部分;
2.  领域模型只反映业务,和任何技术实现⽆关;领域模型不仅能反映领域中的⼀些实体概念,如货物,书本,应聘记录,地址,等;还能反映领域中的⼀些过程概念,如资⾦转账,等;
3.  领域模型确保了我们的软件的业务逻辑都在⼀个模型中,都在⼀个地⽅;这样对提⾼软件的,业务以及⽅⾯都有很好的帮助;
4.  领域模型能够帮助相对平滑地将领域知识转化为软件构造;
5.  领域模型贯穿软件分析、设计,以及开发的整个过程;领域专家、设计⼈员、通过领域模型进⾏交流,彼此共享知识与信息;因为⼤家⾯向的都是同⼀个模型,所以可以防⽌需求⾛样,可以让做出来的软件真正满⾜需求;
6.  要建⽴正确的领域模型并不简单,需要领域专家、设计、积极沟通共同努⼒,然后才能使⼤家对领域的认识不断深⼊,从⽽不断细化和完善领域模型;
7.  为了让领域模型看的见,我们需要⽤⼀些⽅法来表⽰它;图是表达领域模型最常⽤的⽅式,但不是唯⼀的表达⽅式,代码或⽂字描述也能表达领域模型;
8.  领域模型是整个软件的核⼼,是软件中最有价值和最具竞争⼒的部分;设计⾜够精良且符合的领域模型能够更快速的响应需求变化;
领域驱动设计中的⼀些基本概念:
1.实体(entity):
根据eric evans的定义,”⼀个由它的标识定义的对象叫做实体”。通常实体具备唯⼀id,能够被持久化,具有业务逻辑,对应现实世界业务对象。
实体⼀般和主要的业务/领域对象有⼀个直接的关系。⼀个实体的基本概念是⼀个持续抽象的⽣命,可以变化不同的状态和情形,但总是有相同的标识。
需要注意的是:
⼀些将实体当成了orm意义上的实体,⽽不是业务所有和业务定义的领域对象。在⼀些实现中采⽤了transaction script风格的架构,使⽤贫⾎的领域模型。这种认识上的混乱,在领域驱动架构中,不愿意在领域对象中加⼊业务逻辑⽽导致贫⾎的领域模型,同时还可能使混乱的服务对象激增。
2.值对象(value object)
值对象的定义是:描述事物的对象;更准确的说,⼀个没有概念上标识符描述⼀个领域⽅⾯的对象。这些对象是⽤来表⽰临时的事物,或者可以认为值对象是实体的属性,这些属性没有特性标识但同时表达了领域中某类含义的概念。
通常值对象不具有唯⼀id,由对象的属性描述,可以⽤来传递参数或对实体进⾏补充描述。
作为实体属性的描述时,值对象也会被存储。在uml的类图上显现为⼀对多或⼀对⼀的关系。在orm映射关系上需要采⽤较复杂的⼀对多或⼀对⼀关系映射。
关于实体与值对象的⼀个例⼦:⽐如员⼯信息的属性,如住址,电话号码都可以改变;然⽽,同⼀个员⼯的实体的标识将保持不变。因此,⼀个实体的基本概念是⼀个持续抽象的⽣命,可以变化不同的状态和情形,但总是有相同的标识。
实体与值对象的区别
实体具有唯⼀标识,⽽值对象没有唯⼀标识,这是实体和值对象间的最⼤不同。
实体就是领域中需要唯⼀标识的领域概念。有两个实体,如果唯⼀标识不⼀样,那么即便实体的其他所有属性都⼀样,也认为是两个不同的实体;⼀个实体的基本概念是⼀个持续抽象的⽣命,可以变化不同的状态和情形,但总是有相同的标识。
不应该给实体定义太多的属性或⾏为,⽽应该寻关联,发现其他⼀些实体或值对象,将属性或⾏为转移到其他关联的实体或值对象上。
如果两个对象的所有的属性的值都相同,我们会认为它们是同⼀个对象的话,那么我们就可以把这种对象设计为值对象。值对象在判断是否是同⼀个对象时是通过它们的所有属性是否相同,如果相同则认为是同⼀个值对象;⽽实体是否为同⼀个实体的区分,只是看实体的唯⼀标识是否相同,⽽不管实体的属性是否相同。
值对象另外⼀个明显的特征是不可变,即所有属性都是只读的。因为属性是只读的,所以可以被安全的共享;当共享值对象时,⼀般有复制和共享两种做法,具体采⽤哪种做法还要根据实际情况⽽定。
箴⾔:如果值对象时可共享的,它们应该是不可变的。(值对象应该保持尽量的简单)
值对象的设计应尽量简单,不要让它引⽤很多其他的对象,因为本质上讲值对象只是代表⼀个值。
3.聚合及聚合根(aggregate、aggregate root):
聚合是⽤来定义领域对象所有权和边界的领域模式。聚合的作⽤是帮助简化模型对象间的关系。聚合,它通过定义对象之间清晰的所属关系和边界来实现领域模型的内聚,并避免了错综复杂的难以维护的对象关系⽹的形成。聚合定义了⼀组具有内聚关系的相关对象的集合,我们把聚合看作是⼀个修改数据的单元。
划分aggregation是对领域模型的进⼀步深化,aggregation能阐释领域模型内部对象之间的深层关联.
对aggregation的划分会直接映射到程序结构上.⽐如:ddd推荐按aggregation设计model的⼦包.每个aggregation配备⼀个repository.aggregation内部的⾮root对象是通过导航获得的.                ⼀个聚合是⼀组相关的被视为整体的对象。每个聚合都有⼀个根对象(聚合根实体),从外部访问只能通过这个对象。根实体对象有组成聚合所有对象的引⽤,但是外部对象只能引⽤根对象实体。
只有聚合根才能使⽤仓储库直接查询,其它的只能通过相关的聚合访问。如果根实体被删除,聚合内部的其它对象也将被删除。
通常,我们把聚合组织到⼀个⽂件夹或⼀个包中。每⼀个聚集对应⼀个包,并且每个聚集成员包括实体、值对象,domain事件,仓储接⼝和其它⼯⼚对象。
repository文件夹可以删除吗聚合有以下⼀些特点:
  1. 每个聚合有⼀个根和⼀个边界,边界定义了⼀个聚合内部有哪些实体或值对象,根是聚合内的某个实体;
  2. 聚合内部的对象之间可以相互引⽤,但是聚合外部如果要访问聚合内部的对象时,必须通过聚合根开始导航,绝对不能绕过聚合根直接访问聚合内的对象,也就是说聚合根是外部可以保持对它的引⽤的唯⼀元素;
  3. 聚合内除根以外的其他实体的唯⼀标识都是本地标识,也就是只要在聚合内部保持唯⼀即可,因为它们总是从属于这个聚合的;
  4. 聚合根负责与外部其他对象打交道并维护⾃⼰内部的;
  5. 基于聚合的以上概念,我们可以推论出从查询时的单元也是以聚合为⼀个单元,也就是说我们不能直接查询聚合内部的某个⾮根的对象;
  6. 聚合内部的对象可以保持对其他聚合根的引⽤;
  7. 删除⼀个聚合根时必须同时删除该聚合内的所有相关对象,因为他们都同属于⼀个聚合,是⼀个完整的概念。
如何识别聚合?
聚合中的对象关系是内聚的,即这些对象之间必须保持⼀个固定规则,固定规则是指在数据变化时必须保持不变的规则。
当我们在修改⼀个聚合时,我们必须在事务级别确保整个聚合内的所有对象满⾜这个固定规则。
作为⼀条建议,聚合尽量不要太⼤,否则即便能够做到在事务级别保持聚合的完整性,也可能会带来⼀定的性能问题。
有分析报告显⽰,通常在⼤部分领域模型中,有70%的聚合通常只有⼀个实体,即聚合根,该实体内部没有包含其他实体,只包含⼀些值对象;另外30%的聚合中,基本上也只包含两到三个实体。这意味着⼤部分的聚合都只是⼀个实体,该实体同时也是聚合根。
如何识别聚合根?
  如果⼀个聚合只有⼀个实体,那么这个实体就是聚合根;如果有多个实体,可以思考聚合内哪个对象有独⽴存在的意义并且可以和外部直接进⾏交互。
并不是所有的实体都是聚集根,但只有实体才能成为聚集根。
4.⼯⼚(factories):
⼯⼚⽤来封装创建⼀个复杂对象尤其是聚合时所需的知识,作⽤是将创建对象的细节隐藏起来。客户传递给⼯⼚⼀些简单的参数,然后⼯⼚可以在内部创建出⼀个复杂的领域对象然后返回给客户。当创建 实体和值对象复杂时建议使⽤⼯⼚模式。
不意味着我们⼀定要使⽤⼯⼚模式。如果创建对象很简单,使⽤构造器或者控制反转/依赖注⼊容器⾜够创建对象的依赖。此时,我们就不需要通⽤⼯⼚模式来创建实体或值对象。
良好⼯⼚的要求:
每个创建⽅法都是原⼦的。⼀个⼯⼚应该只能⽣产透明状态的对象。对于实体,意味着创建整个聚合时满⾜所有的不变量。
⼀个单独的⼯⼚通常⽣产整个聚合,传出⼀个根实体的引⽤,确保聚合的不变量都有。如果对象的内部聚合需要⼯⼚,通常⼯⼚⽅法的逻辑放在在聚合根上。这样对外部隐藏了聚合内聚的实现,同时赋予了根确保聚合完整的职责。如果聚合根不是⼦实体⼯⼚的合适的家,那么继续创建⼀个单独的⼯⼚。
5.仓储(repositories):
仓储是⽤来管理实体的集合。
仓储⾥⾯存放的对象⼀定是聚合,原因是domain是以聚合的概念来划分边界的;聚合作为⼀个整体概念,要么⼀起被取出来,要么⼀起被删除。外部访问不会单独对某个聚合内的⼦对象进⾏单独操作。因此,我们只对聚合设计仓储。
仓储还有⼀个重要的特征就是分为仓储定义部分和仓储实现部分,我们在领域模型中定义仓储的接⼝,⽽在基础设施层实现具体的仓储。也符合按照接⼝分离模式在领域层定义仓储库接⼝的原则。
注意:repositories本⾝是⼀种领域组件,但repositories的实现却不是领域层中的。
respositories和dao:
dao和repository在领域驱动设计中都很重要。dao是⾯向数据访问的,是关系型和应⽤之间的契约。
repository:位于领域层,⾯向aggregation root。repository是⼀个独⽴的抽象,使⽤领域的通⽤语⾔,它与dao进⾏交互,并使⽤领域理解的语⾔提供对领域模型的数据访问服务的“业务接⼝”。
  dao⽅法是细粒度的,更接近,⽽repository⽅法的粒度粗⼀些,⽽且更接近领域。领域对象应该只依赖于repository接⼝。客户端应该始终调⽤领域对象,领域对象再调⽤dao将数据持久化到数据 存储中。
  处理领域对象之间的依赖关系(⽐如实体及其repository之间的依赖关系)是经常遇到的典型问题。解决这个问题通 常的设计⽅案是让服务类或外观类直
接调⽤repository,在调⽤repository的时候返回实体对象给客户端。
6.服务(services):
服务这个词在服务模式中是这么定义的:服务提供的操作是它提供给使⽤它的客户端,并突出领域对象的关系。
所有的service只负责协调并委派业务逻辑给领域对象进⾏处理,其本⾝并真正实现业务逻辑,绝⼤部分的业务逻辑都由领域对象承载和实现了。
service可与多种组件进⾏交互,这些组件包括:其他的service、领域对象和repository 或 dao。
通常,应⽤中⼀般包括:domain模型服务和应⽤层服务:
*  domain services encapsulate domain concepts that just are not naturally modeled as things.
*  application services constitute the application, or service, layer.
当⼀个领域操作被视为⼀个重要的领域概念,⼀般就应该作为领域服务。 服务应该是⽆状态的。
设计实现领域服务来协调业务逻辑,只在领域服务中实现领域逻辑的调⽤。
领域服务逻辑须以⾮常⼲净简洁的代码实现。因此,我们必须实现对领域低层组件的调⽤。通常应⽤的调⽤,例如仓储库的调⽤,创建事务等,不应该在这⾥实现。这些操作应该在应⽤层实现。
通常服务对象名称中都应包含⼀个动词。 service接⼝的传⼊传出参数也都应该是dto,可能包含的⼯作有领域对象和dto的互转换以及事务。
服务的3个特征:
  a. 服务执⾏的操作涉及⼀个领域概念,这个领域概念通常不属于⼀个实体或者值对象
  b. 被执⾏的操作涉及到领域中其它的对象
  c. 操作时⽆状态的
推荐:最好显式声明服务,因为它创建了领域中⼀个清晰的特性,封装了⼀个概念领域层服务和基础设施层服务:均建⽴在领域实体和值对象的上层,以便直接为这些相关的对象提供所需的服务;
领域服务与domain对象的区别
⼀般的领域对象都是有状态和⾏为的,⽽领域服务没有状态只有⾏为。需要强调的是领域服务是⽆状态的,它存在的意义就是协调领域对象共同完成某个操作,所有的状态还是都保存在相应的领域对象中。
通常,对来说创建不应该存在的服务相当容易;要么在服务中包含了本应存在于领域对象中的领域逻辑,要么扮演了缺失的领域对象⾓⾊,⽽这些领域对象并没有作为模型的⼀部分去创建。
7.domain事件
domain event模式最初由udi dahan提出,发表在⾃⼰的博客上:www.udidahan/2009/06/14/domain-events-salvation/
企业级事件⼤致可以分为三类:系统事件、应⽤事件和领域事件。领域事件的触发点在领域模型(domain model)中。它的作⽤是将领域对象从对repository或service的依赖中解脱出来,避免让领域对象对这些设施产⽣直接依赖。它的做法就是当领域对象的业务⽅法需要依赖到这些对象时就发出⼀个事件,这个事件会被相应的对象监听到并做出处理。
通过使⽤领域事件,我们可以实现领域模型对象状态的异步更新、外部系统接⼝的委托调⽤,以及通过事件派发机制实现系统集成。另外,领域事件本⾝具有⾃描述性。它不仅能够表述系统发⽣了什么事情,⽽且还能够描述发⽣事件的动机。
domain事件也⽤表进⾏存储。
8.DTO
dto- datatransfer object(数据传输对象):dto在设计之初的主要考量是以粗粒度的减少并简化调⽤接⼝。
领域驱动架构与n层
领域驱动架构
eric  evans的“领域驱动设计- 应对软件的复杂性“⼀书中描述和解释了建议的n层架构⾼层次的图:
user interface:
该层包含与其他系统/客户进⾏交互的接⼝与通信设施,在多数应⽤⾥,该层可能提供包括web services、rmi或rest等在内的⼀种或多种通信接⼝。该层主要由facade、dto和assembler三类组件构成,三类组件均是典型的j2ee模式。
dto的作⽤最初主要是以粗粒度的减少并简化调⽤接⼝。在领域驱动设计中,采⽤dto模型,可以起到隐藏领域细节,帮助实现独⽴封闭的领域模型的作⽤。
dto与领域对象之间的相互转换⼯作多由assembler承担,也有⼀些系统使⽤反射机制⾃动实现dto与领域对象之间的相互转换,如apache common beanutils。
facade的⽤意在于为远程客户端提供粗粒度的调⽤接⼝。facade本⾝不处理任何的业务逻辑,它的主要⼯作就是将⼀个⽤户请求委派给⼀个或多个service进⾏处理,同时借助assembler将service传⼊或传出的领域对象转化为dto进⾏传输。
application:
application层中主要组件就是service。这⾥需要注意的是,service的组织粒度和接⼝设计依据与传统transaction script风格的service是⼀致的,但是两者的实现却有质的区别。
  transaction script(事务脚本)的核⼼是过程,通过过程的调⽤来组织业务逻辑,业务逻辑在服务(service)层进⾏处理。⼤部分业务应⽤都可以被看成
⼀系列事务。
transaction script的特点是简单容易理解,⾯向过程设计。  如果应⽤相对简单,在应⽤的⾥不会有基础设施技术的改变,尤其是业务逻辑很少会变动,采⽤transaction script风格简单⾃然,性能良好,容易理解。
transaction script的缺点在于,对于复杂的业务逻辑难以保持良好的设计,事务之间的冗余代码不断增多。应⽤架构容易出现“胖服务层”和“贫⾎的领域模型”。同时,service层积聚越来越多的业务逻辑,导致和扩展性变差
  领域模型属于设计,领域模型具备⾃⼰的属性⾏为和状态,领域对象元素之间通过聚合配合解决实际业务应⽤。可复⽤,可维护,易扩展,可以采⽤合适的设计模型进⾏详细设计。缺点是相对复杂,要求
设计⼈员有良好的抽象能⼒。
transactionscript风格业务逻辑主要在service中实现,⽽在领域驱动设计的架构⾥,service只负责协调并委派业务逻辑给领域对象进⾏处理。因此,我们可以考察这⼀点来识别系统是transaction script架构还是domain model架构。在实践中,设计良好的领域设计架构在中也容易向transaction script架构演变。
domain:
domain层是整个系统的核⼼层,该层维护⼀个使⽤技术实现的领域模型,⼏乎全部的业务逻辑会在该层实现。domain层包含entity(实体)、valueobject(值对象)、domain event(领域事件)和repository(仓储)等多种重要的领域组件。
infrastructure:
infrastructure(基础设施层)为interfaces、application和domain三层提供⽀撑。所有与具体平台、框架相关的实现会在infrastructure中提供,避免三层特别是domain层掺杂进这些实现,从⽽“污染”领域模型。infrastructure中最常见的⼀类设施是对象持久化的具体实现。
n层
层(layers)被视为构成应⽤或服务的⽔平堆叠的⼀组逻辑上的组件。它们帮助区分完成不同任务的组件,提供⼀个最⼤化复⽤和的设计。简⾔之,是关于在架构⽅⾯应⽤关注点分离的原则。        在传统的多层架构中,每个解决⽅案的组件必须分隔到不同的层。每层的组件必须内聚⽽且有⼤约相同的抽象级别。每个⼀级层应该和其他的⼀级层松耦合。
从最底层的抽象级别看,例如第1层。这是系统的基础层。这些抽象的步骤是⼀步⼀步的最后到最顶层。
多层应⽤的关键在于对依赖的管理。传统的多层架构,层内的组件只能和同级或者低级层的组件交互。这有利于减少不同层内组件的依赖。通常有两种多层架构的设计⽅法:严格和灵活的。          *  “严格的层设计”限定层内的组件只能和同⼀层、或者下⼀层的组件通信。即第n层只能和第n-1层交互,n-1层只能和n-2层交互,等等。
*  “灵活的层设计”允许层内的组件和任何低级别层交互。这种设计中,第n层可以和n-1,n-2层交互。
这种设计由于不需要对其他层进⾏重复的调⽤,从⽽可以提⾼性能。然⽽,这种设计不提供层之间的同层隔离级别,使得它难以在不影响多个⾼级层的时候替换⼀个低级的层。
由于层之间是通过定义明确的接⼝进⾏交互这⼀事实,很容易为各层添加替代的实现(例如 mock  and stubs)。
因为⾼层的组件只能和底层的交互,在单独的组件上进⾏测试是很容易的。
使⽤层的好处  -  功能容易确定位置,解决⽅案也就容易维护。层内⾼内聚,层间松耦合使得维护/组合层更容易。 -  其他的解决⽅案可以重⽤由不同层暴露的功能。 -  当项⽬按逻辑分层时,分布式的部署更容易实现。 -  把层分布到不同的物理层可以提⾼可伸缩性;然后这⼀步应该进⾏仔细的评估,因为可能对性能带来负⾯影响。
⾯向领域架构的分层:
在⾯向领域架构中,关键是要清楚界定和分离领域模型层和其余的层。
领域驱动与项⽬开发
⼀般适合结合使⽤scrum(适⽤于)和xp(适⽤于⽬标)⽅法对处理ddd实施项⽬。敏捷⽅法注重于交付商业价值,⽽ddd侧重于结合和业务模型。此外,就ddd迭代的特性来说,scrum或dsdm这样的敏捷⽅法对来说也是更好的框架。
ddd迭代周期的模型如图所⽰。
本图根据《domain driven design and development in practice》⼀⽂中插图进⾏了部分修改。
  领域建模结束时可以开始领域驱动设计。关于如何开始实现领域对象模型,ramnivas laddad推荐如下的步骤。他强调要更侧重于领域模型中的领域对象,⽽不是服务。
*  从领域实体和领域逻辑开始。
*  不要⼀开始就从服务层开始,只添加那些逻辑不属于任何领域实体或值对象的服务。
*  利⽤通⽤语⾔、契约式设计(dbc)、测试、  ci和重构,使实现尽可能地与领域模型紧密结合。
设计领域模型的⼀般步骤:
1.  根据需求建⽴⼀个初步的领域模型,识别出⼀些明显的领域概念以及它们的关联,关联可以暂时没有⽅向但需要有(1:1,1:n,m:n)这些关系;可以⽤⽂字精确的没有歧义的描述出每个领域概念的涵义以及包含的主要信息;
2.  分析主要的软件功能,识别出主要的应⽤层的类;这样有助于及早发现哪些是应⽤层的职责,哪些是领域层的职责;
3.  进⼀步分析领域模型,识别出哪些是实体,哪些是值对象,哪些是领域服务;
4.  分析关联,通过对业务的更深⼊分析以及各种原则及性能⽅⾯的权衡,明确关联的⽅向或者去掉⼀些不需要的关联;
5.  出聚合边界及聚合根,这是⼀件很有难度的事情;因为你在分析的过程中往往会碰到很多模棱两可的难以清晰判断的选择问题,所以,需要我们平时⼀些分析经验的积累才能出正确的聚合根;
6.  为聚合根配备仓储,⼀般情况下是为⼀个聚合分配⼀个仓储,此时只要设计好仓储的接⼝即可;
7.  ⾛查场景,确定我们设计的领域模型能够有效地解决;
8.  考虑如何创建领域实体或值对象,是通过⼯⼚还是直接通过构造函数;
9.  停下来重构模型。寻模型中觉得有些疑问或者是蹩脚的地⽅,⽐如思考⼀些对象应该通过关联导航得到还是应该从仓储获取?聚合设计的是否正确?考虑模型的性能怎样,等等;
领域建模是⼀个不断重构,持续完善模型的过程,⼤家会在讨论中将变化的部分反映到模型中,从⽽是模型不断细化并朝正确的⽅向⾛。
  从设计和实现的⾓度来看,典型的ddd框架应该⽀持以下特征。
*  应该是⼀个以pojo为基础的架构。
*  应该⽀持使⽤ddd概念的业务领域模型的设计和实现。
*  应该⽀持像依赖注⼊(di)和⾯向⽅向编程(aop)这些概念的开箱即⽤。
*  与框架整合。
*  与其它java/java ee框架进⾏良好的集成,⽐如jpa、hibernate、toplink等。
⼀些反模式:
*  贫⾎的领域对象
*  重复的dao
*  肥服务层:服务类在这⾥最终会包含所有的业务逻辑。
*  依恋情结(feature envy):函数对某个类的兴趣⾼过对⾃⼰所处类的兴趣。
⼀些思考
1.  建⽴完整⾃封闭的领域模型。
领域驱动架构相对⽐较容易理解,但建⽴⼀个完整⾃封闭的领域模型却很困难。“领域模型”是⼀个针对业务逻辑抽象的分析模型,它反映出对领域问题的整体描述。领域模型不是编程的实现模型,⽽是⼀组抽象概念的集合。⼀个领域概念不⼀定映射成⼀个类,也有可能会映射很多的类(包括多个实体或值对象)。领域需求的最初细节,在功能层⾯通过领域专家的讨论得出。领域专家并不⼀定需要熟知领域的知识,相反强调的是具有领域中的相关知识。领域需求在相互讨论中不断得到细化,还有可能在出现需求的反复或变更,这都要求领域模型的建⽴完善是⼀个反复重构的过程。是⼀种应对快速变化的需求的⼀种能⼒。强调团队与业务专家之间的紧密协作、⾯对⾯的沟通(认为⽐书⾯的⽂档更有效)、频繁交付新的软件版本、紧凑⽽⾃我组织型的团队、能够很好地适应需求变化的代码编写和团队组织⽅法。故采⽤有利于领域模型的建⽴完善,以更能符合⽤户的实际需求。
关于领域模型分析存在有多种分析⽅法。也许并不是能经常能有机会去实践这些分析⽅法或分析领域模型。但关于领域驱动架构的理解,有助于帮助我们去理解领域驱动的设计,实现⼀些⾼内聚、低耦合的代码实现。
2.  领域服务建模
建⽴和识别领域服务也⽐较容易出错。通常的ssh分层架构与领域驱动架构相近,⽽ssh架构开发更容易导致transaction script架构⽽⾮领域驱动架构。在ssh分层架构上,更容易建⽴”贫⾎”模型,⽽在service
⾥实现业务逻辑。⽽ddd强调“充⾎模型”,“薄”service层。建⽴领域服务需要识别出领域业务逻辑,并将业务实现到领域模型中。⼀⽅⾯,充满着变化,在中难以把握。当业务不明需求不清时,“贫⾎模型”就更容易被⼈接受。另⼀⽅⾯,在构建领域模型时,orm映射显⽰⼗分重要并且也⾮常复杂,包括类继承体系与的映射,抓取策略和缓存管理在内的⼀系列问题等.“贫⾎模型”有时会简化这种映射关系,同时,在处理对象依赖关系上显得更加灵活性。⽽领域模型强调了领域边界,对领域对象的访问总是通过聚合根开始,在有时候,模型的某些遍历会带来更⼤的性能和稳定性上的问题。⽽解决这些问题时,⼜常常会从实效性上出发⽽牺牲模型个别的清晰性和纯洁性。
3.领域对象、领域服务以及repository之间的互相依赖
在实际开发中,会经常需要处理领域对象之间的依赖关系,以及领域对象与repository间的依赖。通常可能的⽅案是让service或façade直接调⽤repository,从⽽获得返回的领域对象。这种⽅式导致各层间的依赖,通常我们应该考虑解耦这种依赖。当前实现组件解耦常⽤的技术⽆⾮是:控制反转(ioc)、依赖注⼊(di)、⾯向⽅⾯编程(aop)以及分布式服务接⼝。因此,解决依赖的⼀种思路利⽤di或aop将repository和服务注⼊到领域对象中。spring框架提供了相关的机制。在spring环境中,利⽤spring实例化对象,注⼊依赖,将服务、repository等领域对象联系起来。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。