领域驱动设计:软件核心复杂性应对之道

零、概述

很多应用程序最主要的复杂性并不在技术上,而是来自领域本身、用户的活动或业务。

领域驱动设计是一种设计方法,试图解决软件难以理解、难以演化的问题。领域驱动设计试图用围绕业务概念来构建领域模型的方式来控制业务的复杂性

一、领域模型

领域模型是经过严格组织并精心选择的抽象知识,对知识进行有选择的简化和有组织的结构化。适当的模型使人理解信息的意义,并专注于问题相关的信息。

1. 消化知识

开发人员与领域专家通过不断的沟通和协作,消化专业知识构建领域模型,并且在迭代过程中不断地完善模型

知识消化是一种探索,永无止境;在知识消化过程中领域模型不断精化,使得开发人员不断学习重要的业务原理,同时领域专家也通过不断地提炼专业知识完善自身的理解。

业务活动和规则的消化:业务活动和规则同实体一样,也是领域的中心。建模不应局限于寻找实体和值对象,同时还要消化业务活动和规则。通过将规则消化为概念(如抽象为策略对象),能够更好的保护和共享知识。

2. 模型语言

模型应该为开发人员和领域专家提供一种用于相互交流的通用语言,减少沟通障碍成本。讨论系统时要结合模型,使用模型中的元素来描述场景,并且使用尽可能简洁的语言,然后将新思想应用到图和代码中。

:应该使用简化的图,图中只包含对象模型的重要概念部分。(综合性的大图反而会失去沟通和解释能力) 在澄清一些要点时,需要添加一些文本和非标准的符号。模型不是图,图的目的是为了帮助表达和解释模型。

文档文档应作为代码和口头交流的补充。(文档不该再重复表示代码已明确表达出的内容)设计文档的最大价值是解释模型的概念,帮助在代码的细节中指引方向。如果文档在项目中的角色不太重要,可以将其淘汰,而对于与项目活动紧密交互的文档应当需要保持更新

解释性模型:解释模型时,不必拘泥于软件设计相关的技术模型。可以专门为某个特殊主题定制一些表达力更强的风格。

3. 绑定模型和实现

建立的模型需要在分析和程序设计阶段都能发挥良好的作用,既能反映深层次的领域概念,又能在软件中更加自然的实现模型;不能因为技术考虑而削弱分析的功能,也不能接收只反映领域概念却舍弃软件设计原则的拙劣设计。

在软件开发中设计是无处不在的,编写代码和建模工作不应该分离。任何参与建模的人员,无论在项目中主要职责是什么,都必须花时间了解代码;开发人员需要学会用代码来表达模型,并不用程度的参与模型讨论并与领域专家保持联系。

二、模型驱动设计的构造块

在这里插入图片描述

1. 分离领域

分层架构:
在这里插入图片描述

  • 用户界面层:负责向用户显示信息和解释用户指令
  • 应用层:定义软件要完成的任务,指挥表达领域概念的对象来解决问题。应用层要尽量简单,不包含业务规则和知识,只为下一层的领域对象协调任务,分配工作
  • 领域层:表达业务概念,业务状态信息和业务规则。领域层是业务软件的核心。(处理业务规则的是领域层,而不是应用层)
  • 基础设施层:为上面各层提供通用的技术能力。

每一层内具有内聚性并只依赖于它的下层,且只与上层进行松散连接。领域对象将重点放在如何表达领域模型上,而无需考虑自身的显示和存储问题。

2. 模型的表示
1)控制关联
  1. 规定一个遍历方向(理解领域中更关心哪个方向的关联,如更关心国家有哪些总统,还是总统是哪个国家的,从而确定关联的遍历方向)
  2. 添加一个限定符,以便有效减少多重关联(如添加时段的限制,某个国家在某个时段内只能有一个总统,从而将一对多的关联转换为一对一的关联)
  3. 消除不必要的关联
2)实体Entity

实体由标识定义,而不是由属性定义,实体是一种贯穿整个生命周期(甚至经历多种形式)的抽象的连续性。在定义标识时,要确保这种操作为每个对象生成唯一的结果。

并不是所有对象都需要这种标识,只有在需要时才将对象表示为实体。

在对Entitty进行建模时,应该只添加对概念至关重要的行为和这些行为所必须的属性;而将其他属性和行为转移到与核心实体关联的其他对象中。

3)值对象Value Object

用于描述领域的某个方面而本身没有概念标识的对象称为Value Object(值对象)。当只关心一个模型元素的属性时,应把它归类为值对象。

设计值对象时,可以采用复制、共享、保持不变的方式。把值对象设计为不可变,可以极大的简化实现,确保共享和引用传递的安全性。

4)服务Service

当领域中的某个重要的过程或转换操作不属于实体或值对象的自然职责时,应该在模型中添加一个作为独立接口的操作,并将其声明为Service。

各个层都存在Service
在这里插入图片描述

5) Module

Module将紧密概念关系的模型元素集中到一起,将具有相关职责的对象元素聚合到一起,把建模和设计工作集中到单一Module中。这通常会实现Module之间的低耦合,如果效果不理想则应该尝试更改模型或找到另一个可作为Module基础的概念。

3. 领域对象的生命周期

在生命周期的开始阶段,使用 Factory(工厂)创建和重建复杂对象,并使用 Aggregate 封装他们的内部结构,在生命周期的中间和末尾使用 Repository(存储库)来提供查找和检索持久对象并封装庞大基础设施的手段。

1)聚合

聚合Aggregate:聚合是一组相关对象的集合,每个聚合都有一个边界和聚合根(root)。边界定义了聚合内部有什么,聚合根是聚合中所包含的一个特定的 Entity。边界内部的实体可以相互引用,而聚合根是唯一允许外部对象保持对它引用的元素外部对象除了根以外看不到其他对象(对内部成员的临时引用可以被传递出去,但只在一次操作中有效)。

根控制访问不能绕过聚合根来修改内部对象,这样有利于确保聚合内的所有对象满足所有固定规则,也可以保证在任何状态变化时聚合作为一个整体满足固定规则。

2)工厂

工厂 Factory:将创建复杂对象的实例和聚合的职责转移给一个单独的对象。

Factory的选择:

  • 向一个已存在的聚合添加元素,可以在聚合根上添加一个工厂method,从而对外隐藏实现细节并保证聚合的完整性。
  • 如果一个对象的创建主要使用另一个对象的数据,可以在后者对象上添加一个工厂method(即使被创建对象不在创建对象的聚合内)
  • 找不到合适的地方隐藏创建细节时,可以创建一个专有的Factory。聚合通常由一个专有的Factory来创建,且Factory负责把对根的引用传递出去。(聚合内部的工厂应该把访问限制在聚合内部)
2)Repository

在访问持久对象时,当不易通过遍历方式访问聚合根时,这一小部分的对象必须能通过基于对象属性的搜索来全局访问。
其他对象则不应该使用这种方式,毫无约束的数据库查询会破坏领域对象的封装和Aggregate。应该通过聚合根导航来获取这些数据。(不能随便就通过数据库查询获得聚合内部对象)

Repository将数据库访问技术和策略封装起来,让客户始终聚焦于模型,而将所有对象存储和访问操作交给Repository来完成。只为那些需要直接访问的聚合根提供Repository

【内容简介】 “每个有思想的软件开发者的书架上都应该有这样一本书”——Kent Beck “Eric设法收集了经验丰富的对象设计人员一直使用的一些设计过程,作为一个团队的人们在这些过程中却没能够成功地完成剩下的工作。人们将知识弄得支离破碎……却从来没有将建立领域逻辑的原则组织起来并使其系统化。这本书是非常重要的。”—— Kyle Brown,《Enterprise Java Programming with IBM WebSphere》的作者。 本书涉及的主题具体包括: ●隔离领域●实体、值对象、服务和模块●一个领域对象的生命周期●将过程表示为领域对象●创建没有副作用的函数●总体轮廓●独立的类●扩展说明●应用分析模式●将设计模式与模型相联系●维护模型的完整性●设计领域前景声明●选择重构目标●职责层次●创建可插入的组件框架●结合大比例结构与界限上下文 本书为读者系统地介绍了领域驱动的设计方法。书中介绍了大量优秀的设计示例、基于经验的技术以及促进处理复杂领域的软件开发的基本原则。本书将设计和开发实践相结合,在介绍领域驱动设计时,还提供了大量的Java示例,这些例子都是从实际中提取出来的,展示了领域驱动设计软件开发中的实际应用。 通过对本书的阅读,读者将获得对领域驱动设计的总体认识,了解领域驱动设计中涉及的关键原则、术语和推断。本书介绍的经验和标准模式将为开发团队提供一种通用语言。另外,书中还介绍了如何在领域模型中进行重构,如何与敏捷开发进行集成,如何获得对领域更深的认识并增进领域专家和程序员之间的交流等。并在此基础上,介绍了在复杂系统和较大组织中进行的领域驱动设计
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值