DDD领域驱动设计总结.docx
《DDD领域驱动设计总结.docx》由会员分享,可在线阅读,更多相关《DDD领域驱动设计总结.docx(14页珍藏版)》请在三一办公上搜索。
1、-领域驱动设计之领域模型-为什么建立一个领域模型是重要的. 领域通用语言(UBIQUITOUS LANGUAGE )-将领域模型转换为代码实现的最佳实践. 领域建模时思考问题的角度-领域驱动设计的经典分层架构- 用户界面/展现层. 应用层. 领域层- 基础设施层-领域驱动设计过程中使用的模式-所有模式的总揽图. 关联的设计 实体(Entity) 值对 象(Value Object) 领 域服务(Domain Service)应用层服务领域层服务基础层服务 聚 合及聚 合 根(Aggregate,Aggregate Root)聚合有以下一些特点:如何识别聚合?如何识别聚合根? 工厂(Factor
2、y) 仓储(Repository )设计领域模型的一般步骤 在分层架构中其他层如何与领域层交互对于会影响领域层中领域对象状态的应用层功能 关于Unit of Work(工作单元)的几种实现方法 对于不会影响领域层中领域对象状态的查询功能 为什么面向对象比面向过程更能适应业务变化 领域驱动设计的其他一些主题 一些相关的扩展阅读 CQRS架构 Event Sourcing (事件 溯源) DCI架构 四色原型分析模式 时亥U -时间段 原 型(Moment-Interval Archetype) 参与方-地点-物品原型(Part-Place-Thing Archetype ) 描 述 原 型(De
3、scription Archetype ) 角 色 原 型(Role Archetype )领域驱动设计之领域模型加一个导航,关于如何设计聚合的详细思考,见这篇文章。2004 年 Eric Evans 发表 Domain-Driven Design -Tackling Complexity in the Heart of Software (领域驱动设计),简称Evans DDD。领域驱动设计分为两个阶段:以一种领域专家、设计人员、开发人员都能理解的通用语言作为相互交流的工具,在交流的过程中发现领域概念, 然后将这些概念设计成一个领域模型;由领域模型驱动软件设计,用代码来实现该领域模型;由此可
4、见,领域驱动设计的核心是建立正确的领域模型。为什么建立一个领域模型是重要的领域驱动设计告诉我们,在通过软件实现一个业务系统时,建立一个领域模型是非常重要和必要的,因为领域模型 具有以下特点:1. 领域模型是对具有某个边界的领域的一个抽象,反映了领域内用户业务需求的本质;领域模型是有边界的,只反应 了我们在领域内所关注的部分;2. 领域模型只反映业务,和任何技术实现无关;领域模型不仅能反映领域中的一些实体概念,如货物,书本,应聘记 录,地址,等;还能反映领域中的一些过程概念,如资金转账,等;3. 领域模型确保了我们的软件的业务逻辑都在一个模型中,都在一个地方;这样对提高软件的可维护性,业务可理解
5、 性以及可重用性方面都有很好的帮助;4. 领域模型能够帮助开发人员相对平滑地将领域知识转化为软件构造;5. 领域模型贯穿软件分析、设计,以及开发的整个过程;领域专家、设计人员、开发人员通过领域模型进行交流,彼 此共享知识与信息;因为大家面向的都是同一个模型,所以可以防止需求走样,可以让软件设计开发人员做出来的 软件真正满足需求;6. 要建立正确的领域模型并不简单,需要领域专家、设计、开发人员积极沟通共同努力,然后才能使大家对领域的认 识不断深入,从而不断细化和完善领域模型;7. 为了让领域模型看的见,我们需要用一些方法来表示它;图是表达领域模型最常用的方式,但不是唯一的表达方式, 代码或文字描
6、述也能表达领域模型;8. 领域模型是整个软件的核心,是软件中最有价值和最具竞争力的部分;设计足够精良且符合业务需求的领域模型能 够更快速的响应需求变化;领域通用语言(UBIQUITOUS LANGUAGE )我们认识到由软件专家和领域专家通力合作开发出一个领域的模型是绝对需要的,但是,那种方法通常会由于一些 基础交流的障碍而存在难点。开发人员满脑子都是类、方法、算法、模式、架构,等等,总是想将实际生活中的概 念和程序工件进行对应。他们希望看到要建立哪些对象类,要如何对对象类之间的关系建模。他们会习惯按照封装、 继承、多态等面向对象编程中的概念去思考,会随时随地这样交谈,这对他们来说这太正常不过
7、了,开发人员就是 开发人员。但是领域专家通常对这一无所知,他们对软件类库、框架、持久化甚至数据库没有什么概念。他们只了 解他们特有的领域专业技能。比如,在空中交通监控样例中,领域专家知道飞机、路线、海拔、经度、纬度,知道 飞机偏离了正常路线,知道飞机的发射。他们用他们自己的术语讨论这些事情,有时这对于外行来说很难直接理解。 如果一个人说了什么事情,其他的人不能理解,或者更糟的是错误理解成其他事情,又有什么机会来保证项目成功 呢? 在交流的过程中,需要做翻译才能让其他的人理解这些概念。开发人员可能会努力使用外行人的语言来解析一些设 计模式,但这并一定都能成功奏效。领域专家也可能会创建一种新的行话
8、以努力表达他们的这些想法。在这个痛苦 的交流过程中,这种类型的翻译并不能对知识的构建过程产生帮助。领域驱动设计的一个核心的原则是使用一种基于模型的语言。因为模型是软件满足领域的共同点,它很适合作为这 种通用语言的构造基础。使用模型作为语言的核心骨架,要求团队在进行所有的交流是都使用一致的语言,在代码 中也是这样。在共享知识和推敲模型时,团队会使用演讲、文字和图形。这儿需要确保团队使用的语言在所有的交 流形式中看上去都是一致的,这种语言被称为通用语言(Ubiquitous Language)。通用语言应该在建模过程 中广泛尝试以推动软件专家和领域专家之间的沟通,从而发现要在模型中使用的主要的领域
9、概念。将领域模型转换为代码实现的最佳实践拥有一个看上去正确的模型不代表模型能被直接转换成代码,也或者它的实现可能会违背某些我们所不建议的软件 设计原则。我们该如何实现从模型到代码的转换,并让代码具有可扩展性、可维护性,高性能等指标呢?另外,如 实反映领域的模型可能会导致对象持久化的一系列问题,或者导致不可接受的性能问题。那么我们应该怎么做呢?我们应该紧密关联领域建模和设计,紧密将领域模型和软件编码实现捆绑在一起,模型在构建时就考虑到软件和设 计。开发人员会被加入到建模的过程中来。主要的想法是选择一个能够恰当在软件中表现的模型,这样设计过程会 很顺畅并且基于模型。代码和其下的模型紧密关联会让代码
10、更有意义并与模型更相关。有了开发人员的参与就会有 反馈。它能保证模型被实现成软件。如果其中某处有错误,会在早期就被标识出来,问题也会容易修正。写代码的 人会很好地了解模型,会感觉自己有责任保持它的完整性。他们会意识到对代码的一个变更其实就隐含着对模型的 变更,另外,如果哪里的代码不能表现原始模型的话,他们会重构代码。如果分析人员从实现过程中分离出去,他 会不再关心开发过程中引入的局限性。最终结果是模型不再实用。任何技术人员想对模型做出贡献必须花费一些时 间来接触代码,无论他在项目中担负的是什么主要角色。任何一个负责修改代码的人都必须学会用代码表现模型。 每位开发人员都必须参与到一定级别的领域讨
11、论中并和领域专家联络。领域建模时思考问题的角度用户需求不能等同于用户,捕捉用户心中的模型也不能等同于以用户为核心设计领域模型。老子书中 有个观点:有之以为利,无之以为用。在这里,有之利,即建立领域模型;无之用,即包容用户需求。举些例子, 一个杯子要装满一杯水,我们在制作杯子时,制作的是空杯子,即要把水倒出来,之后才能装下水;再比如,一座 房子要住人,我们在建造房子时,建造的房子是空的,唯有空的才能容纳人的居住。因此,建立领域模型时也要将 用户置于模型之外,这样才能包容用户的需求。所以,我的理解是:1. 我们设计领域模型时不能以用户为中心作为出发点去思考问题,不能老是想着用户会对系统做什么;而应
12、该从一个 客观的角度,根据用户需求挖掘出领域内的相关事物,思考这些事物的本质关联及其变化规律作为出发点去思考问 题。2, 领域模型是排除了人之外的客观世界模型,但是领域模型包含人所扮演的参与者角色,但是一般情况下不要让参与 者角色在领域模型中占据主要位置,如果以人所扮演的参与者角色在领域模型中占据主要位置,那么各个系统的领 域模型将变得没有差别,因为软件系统就是一个人机交互的系统,都是以人为主的活动记录或跟踪;比如:论坛中 如果以人为主导,那么领域模型就是:人发帖,人回帖,人结贴,等等;DDD的例子中,如果是以人为中心的话, 就变成了:托运人托运货物,收货人收货物,付款人付款,等等;因此,当我
13、们谈及领域模型时,已经默认把人的 因素排除开了,因为领域只有对人来说才有意义,人是在领域范围之外的,如果人也划入领域,领域模型将很难保 持客观性。领域模型是与谁用和怎样用是无关的客观模型。归纳起来说就是,领域建模是建立虚拟模型让我们现实 的人使用,而不是建立虚拟空间,去模仿现实。以Eric Evans(DDD之父)在他的书中的一个货物运输系统为例子简单说明一下。在经过一些用户需求讨论之后, 在用户需求相对明朗之后,Eric这样描述领域模型:1. 一个Cargo (货物)涉及多个Customer (客户,如托运人、收货人、付款人),每个Customer承担不同的角 色;2. Cargo的运送目标
14、已指定,即Cargo有一个运送目标;3. 由一系列满足Specification (规格)的Carrier Movement (运输动作)来完成运输目标;从上面的描述我们可以看出,他完全没有从用户的角度去描述领域模型,而是以领域内的相关事物为出发点,考虑 这些事物的本质关联及其变化规律的。上述这段描述完全以货物为中心,把客户看成是货物在某个场景中可能会涉 及到的关联角色,如货物会涉及到托运人、收货人、付款人;货物有一个确定的目标,货物会经过一系列列的运输 动作到达目的地;其实,我觉得以用户为中心来思考领域模型的思维只是停留在需求的表面,而没有挖掘出真正的 需求的本质;我们在做领域建模时需要努力
15、挖掘用户需求的本质,这样才能真正实现用户需求;关于用户、参与者这两个概念的区分,可以看一下下面的例子:试想两个人共同玩足球游戏,操作者(用户)是驱动者,它驱使足球比赛领域中,各个人(参与者)的活动。这 里立下一个假设,假设操作者A操作某一队员a,而队员a拥有着某人B的信息,那么有以下说法,a是B的镜 像,a是领域参与者,A是驱动者。领域驱动设计的经典分层架构负责向用户展现信息以及解释用户命令。更细的方面来讲就是:1. 请求应用层以获取用户所需要展现的数据;2. 发送命令给应用层要求其执行某个用户命令;应用层很薄的一层,定义软件要完成的所有任务。对外为展现层提供各种应用功能(包括查询或命令),对
16、内调用领域层 (领域对象或领域服务)完成各种业务逻辑,应用层不包含业务逻辑。领域层负责表达业务概念,业务状态信息以及业务规则,领域模型处于这一层,是业务软件的核心。基础设施层本层为其他层提供通用的技术能力;提供了层间的通信;为领域层实现持久化机制;总之,基础设施层可以通过架 构和框架来支持其他层的技术需求;领域驱动设计过程中使用的模式所有模式的总揽图push state关联的设计关联本身不是一个模式,但它在领域建模的过程中非常重要,所以需要在探讨各种模式之前,先讨论一下对象之间 的关联该如何设计。我觉得对象的关联的设计可以遵循如下的一些原则:1. 关联尽量少,对象之间的复杂的关联容易形成对象的
17、关系网,这样对于我们理解和维护单个对象很不利,同时也很 难划分对象与对象之间的边界;另外,同时减少关联有助于简化对象之间的遍历;2. 对多的关联也许在业务上是很自然的,通常我们会用一个集合来表示1对多的关系。但我们往往也需要考虑到性能 问题,尤其是当集合内元素非常多的时候,此时往往需要通过单独查询来获取关联的集合信息;3. 关联尽量保持单向的关联;4. 在建立关联时,我们需要深入去挖掘是否存在关联的限制条件,如果存在,那么最好把这个限制条件加到这个关联 上;往往这样的限制条件能将关联化繁为简,即可以将多对多简化为1对多,或将1对多简化为1对1;实体(Entity)实体就是领域中需要唯一标识的领
18、域概念。因为我们有时需要区分是哪个实体。有两个实体,如果唯一标识不一样, 那么即便实体的其他所有属性都一样,我们也认为他们两个不同的实体;因为实体有生命周期,实体从被创建后可 能会被持久化到数据库,然后某个时候又会被取出来。所以,如果我们不为实体定义一种可以唯一区分的标识,那 我们就无法区分到底是这个实体还是哪个实体。另外,不应该给实体定义太多的属性或行为,而应该寻找关联,发 现其他一些实体或值对象,将属性或行为转移到其他关联的实体或值对象上。比如Customer实体,他有一些地 址信息,由于地址信息是一个完整的有业务含义的概念,所以,我们可以定义一个Address对象,然后把Customer
19、 的地址相关的信息转移到Address对象上。如果没有Address对象,而把这些地址信息直接放在Customer对象 上,并且如果对于一些其他的类似Address的信息也都直接放在Customer上,会导致Customer对象很混乱, 结构不清晰,最终导致它难以维护和理解;值对象(Value Object )在领域中,并不是没一个事物都必须有一个唯一标识,也就是说我们不关心对象是哪个,而只关心对象是什么。就 以上面的地址对象Address为例,如果有两个Customer的地址信息是一样的,我们就会认为这两个Customer 的地址是同一个。也就是说只要地址信息一样,我们就认为是同一个地址。用
20、程序的方式来表达就是,如果两个对 象的所有的属性的值都相同我们会认为它们是同一个对象的话,那么我们就可以把这种对象设计为值对象。因此, 值对象没有唯一标识,这是它和实体的最大不同。另外值对象在判断是否是同一个对象时是通过它们的所有属性是 否相同,如果相同则认为是同一个值对象;而我们在区分是否是同一个实体时,只看实体的唯一标识是否相同,而 不管实体的属性是否相同;值对象另外一个明显的特征是不可变,即所有属性都是只读的。因为属性是只读的,所 以可以被安全的共享;当共享值对象时,一般有复制和共享两种做法,具体采用哪种做法还要根据实际情况而定; 另外,我们应该给值对象设计的尽量简单,不要让它引用很多其
21、他的对象,因为他只是一个值,就像int a = 3; 那么3就是一个我们传统意义上所说的值,而值对象其实也可以和这里的3 一样理解,也是一个值,只不过是用 对象来表示。所以,当我们在C#语言中比较两个值对象是否相等时,会重写GetHashCode和Equals这两个方 法,目的就是为了比较对象的值;值对象虽然是只读的,但是可以被整个替换掉。就像你把a的值修改为4(a = 4;)一样,直接把3这个值替换为4 了。值对象也是一样,当你要修改Customer的Address对象引用时,不 是通过Customer.Address.Street这样的方式来实现,因为值对象是只读的,它是一个完整的不可分割
22、的整体。我们可以这样做:Customer.Address = new Address,.);领域服务(Domain Service )领域中的一些概念不太适合建模为对象,即归类到实体对象或值对象,因为它们本质上就是一些操作,一些动作, 而不是事物。这些操作或动作往往会涉及到多个领域对象,并且需要协调这些领域对象共同完成这个操作或动作。 如果强行将这些操作职责分配给任何一个对象,则被分配的对象就是承担一些不该承担的职责,从而会导致对象的 职责不明确很混乱。但是基于类的面向对象语言规定任何属性或行为都必须放在对象里面。所以我们需要寻找一种 新的模式来表示这种跨多个对象的操作,DDD认为服务是一个很
23、自然的范式用来对应这种跨多个对象的操作,所 以就有了领域服务这个模式。和领域对象不同,领域服务是以动词开头来命名的,比如资金转帐服务可以命名为MoneyTransferService。当然,你也可以把服务理解为一个对象,但这和一般意义上的对象有些区别。因为一般 的领域对象都是有状态和行为的,而领域服务没有状态只有行为。需要强调的是领域服务是无状态的,它存在的意 义就是协调领域对象共完成某个操作,所有的状态还是都保存在相应的领域对象中。我觉得模型(实体)与服务(场 景)是对领域的一种划分,模型关注领域的个体行为,场景关注领域的群体行为,模型关注领域的静态结构,场景 关注领域的动态功能。这也符合了
24、现实中出现的各种现象,有动有静,有独立有协作。领域服务还有一个很重要的功能就是可以避免领域逻辑泄露到应用层。因为如果没有领域服务,那么应用层会直接 调用领域对象完成本该是属于领域服务该做的操作,这样一来,领域层可能会把一部分领域知识泄露到应用层。因 为应用层需要了解每个领域对象的业务功能,具有哪些信息,以及它可能会与哪些其他领域对象交互,怎么交互等 一系列领域知识。因此,引入领域服务可以有效的防治领域层的逻辑泄露到应用层。对于应用层来说,从可理解的 角度来讲,通过调用领域服务提供的简单易懂但意义明确的接口肯定也要比直接操纵领域对象容易的多。这里似乎 也看到了领域服务具有Facade的功能,呵呵
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- DDD 领域 驱动 设计 总结
链接地址:https://www.31ppt.com/p-4883919.html