DDD的一些概念
什么是领域
一个领域本质上可以理解为一个问题域,只要确定了系统所属的领域,那么这个系统的核心业务,即要解决的关键问题就基本确定了。
例如:论坛是一个领域,电商系统是一个领域
界限上下文
通常来说,一个领域有且只有一个核心问题,我们称之为该领域的核心子域,界限上下文可以简单理解成一个子系统或组件模块(??)
领域模型
特点:
对具有某个边界的领域的一个抽象,反映了领域内用户业务需求的本质。它属于解决问题空间。领域模型是有边界的,只反应了我们在领域内所关注的部分,包括实体概念以及过程概念
提高软件的可维护性,业务可理解性以及可重用性。领域模型确保了我们的软件的业务逻辑都在一个模型中,帮助开发人员相对平滑地将领域知识转化为软件构造 。
贯穿软件分析,设计,开发的整个过程,领域专家,设计人员,开发人员面向同一个模型进行交流,彼此共享知识与信息,所以可以防止需求走样,让软件开发人员做出来的软件真正满足需求;要建立正确的领域模型并不简单,需要领域专家,设计,开发人员积极沟通共同努力,然后才能使大家对领域的认识不断深入,从而不断细化和完善领域模型
为了让领域模型看得见,使用的常用表达领域模型的方式:图,代码或文字
重要性:领域模型是整个软件的核心,是软件中最有价值和最具竞争力的部分;设计足够精良且符合业务需求的领域模型能够更快速地响应需求变化。
领域通用语言
开发过程中,开发人员以类,算法,设计模式,架构等进行思考与交流。但领域专家对此一无所知,他们对技术上的术语没有太多概念,只了解特有的领域专业技能。领域驱动设计的一个核心原则是使用一种基于模型的语言。使用模型作为语言的核心骨架,要求团队在进行所有的交流是都使用一致的语言,在代码中也是这样,这种语言被称为通用语言
建模思考的问题:用户需求
设计领域模型时不能以用户为出发点去思考问题,不能老想着用户会对系统做什么;而应该从一个客观的角度,根据用户需求挖掘出领域内的相关事物,思考这些事物的本质关联及其变化规律作为出发点去思考问题。
领域模型是 排除了人之外的客观世界模型 ,包含了人所扮演的参与者角色。但是一般情况下不要让参与者角色在领域模型中占据主要位置,否则各个系统的领域模型将变得没有差别。
经典分层架构:
用户界面/展示层:1)请求应用层获取用户所需的展示数据;2)发送命令给应用层执行用户的命令
应用层:薄薄的一层,定义软件要完成的任务。对外为展示层提供各种应用功能,对内调用领域层(领域对象或领域服务)完成各种业务逻辑。应用层不包含业务逻辑
领域层:表达业务概念、业务状态信息及业务规则,是业务软件的核心
基础设施层:为其他层提供通用的技术能力,提供了层间通信;为领域层提供持久化机制。
实体:
实体就是领域中需要 唯一标识 的领域概念。因为我们有时需要区分是哪个实体:有两个实体,如果唯一标识不一样,那么即便实体的其他所有属性都一样,也认为他们是两个不同的实体。
值对象:
如果两个对象所有属性的值都相同,我们会认为它们是同一个对象,那么就可以把这种对象设计为值对象。
值对象的特征:
值对象 没有唯一标识 ,这是它和实体的最大不同。值对象在判断是否是同一个对象时是通过它们的所有属性是否相同,如果相同则认为是同一个值对象。在区分是否是同一个实体时,只看实体的唯一标识是否相同,而不管实体的属性是否相同。
值对象是 不可变 的,即所有属性都是只读的,所以可以被安全的共享。
领域服务:
领域中的一些概念不太适合建模为对象(实体对象或值对象),因为它们本质上就是一些操作、动作,而不是事物。这些操作往往需要 协调多个领域对象。如果强行将这些操作职责分配给任何一个对象,则被分配的对象就是承担一些不该承担的职责,从而会导致对象的职责不明确很混乱。DDD认为领域服务模式是一个很自然的范式用来对应这种跨多个对象的操作。一般的领域对象都是有状态和行为的,而领域服务没有状态只有行为。
领域服务还有一个很重要的功能就是可以避免领域逻辑泄露到应用层。因为如果没有领域服务,那么应用层会直接调用领域对象完成本该是属于领域服务该做的操作,需要了解每个领域对象的业务功能,以及它可能会与哪些其他领域对象交互等一系列领域知识。这样一来,领域层可能会把一部分领域知识泄露到应用层。对于应用层来说,通过调用领域服务提供的简单易懂且意义明确的接口肯定也要比直接操纵领域对象容易的多。
三种服务
软件中一般有三种服务:应用层服务、领域服务、基础服务。从以下的例子中可以清晰的看出每种服务的职责:
应用层服务
获取输入(如一个XML请求)
发送消息给领域层服务,要求其实现转帐的业务逻辑
领域层服务处理成功,则调用基础层服务发送Email通知
领域层服务
获取源帐号和目标帐号,分别通知源帐号和目标帐号进行扣除金额和增加金额的操作
提供返回结果给应用层
基础层服务
按照应用层的请求,发送Email通知
聚合及聚合根
聚合定义了一组具有 内聚关系 的相关对象的集合,以及对象之间清晰的所属关系和边界,避免了错综复杂的难以维护的对象关系网的形成。我们把聚合看作是一个修改数据的单元。
聚合有以下特点:
每个聚合有一个根和一个边界:根是聚合内的某个实体;边界定义了一个聚合内部有哪些实体或值对象;
聚合根是外部可以保持对聚合引用的唯一元素,负责与外部其他对象打交道并维护自己内部的业务规则。聚合内部的对象之间可以相互引用,但是聚合外部如果要访问聚合内部的对象时,必须通过聚合根开始导航,绝对不能绕过聚合根直接访问聚合内的对象;
聚合内除根以外的其他实体的唯一标识都是本地标识,也就是只要在聚合内部保持唯一即可,因为它们总是从属于这个聚合的;
聚合内部的对象可以保持对其他聚合根的引用;
删除一个聚合根时必须同时删除该聚合内的所有相关对象,因为他们都同属于一个聚合,是一个完整的概念;
基于聚合的以上概念,我们可以推论出从数据库查询时的单元也是以聚合为一个单元,不能直接查询聚合内部的某个非根的对象;
如何识别聚合:
可以从业务的角度分析哪些对象它们的关系是内聚的,可看成一个整体来考虑的,然后这些对象可以放在一个聚合内。关系内聚是指这些对象之间必须保持一个固定规则,固定规则是指在数据变化时必须保持不变的一致性规则。当修改一个聚合时,必须在 事务级别 确保整个聚合内的所有对象满足这个固定规则。聚合尽量不要太大,否则可能带来一定的性能问题。通常在大部分领域模型中,有70%的聚合通常只有一个实体,即聚合根,该实体内部没有包含其他实体,只包含一些值对象;另外30%的聚合中,基本上也只包含两到三个实体。
如何识别聚合根:
如果一个聚合只有一个实体,那么这个实体就是聚合根;如果有多个实体,那么我们可以思考聚合内哪个对象有独立存在的意义并且可以和外部直接进行交互。
仓储
仓储被设计出来的原因:领域模型中的对象自从创建后不会一直留在内存活动,当它不活动时会被持久化到DB中,当需要的时候会重建该对象。所以,重建对象是一个和DB打交道的过程,需要提供一种机制,提供类似集合的接口来帮助我们 管理对象。
仓储里存放的对象一定是聚合,因为之前提到的领域模型是以聚合的概念来划分边界的。我们 只对聚合设计仓储 ,把整个聚合看成一个整体,要么一起取出来,要么一起被删除,不会单独对某个聚合内的子对象进行单独查询和更新。仓储还有一个重要的特征就是分为仓储定义部分和仓储实现部分,在领域模型中定义仓储的接口,而在基础设施层实现具体的仓储。
设计领域模型时一般步骤
根据需求建立初步的领域模型,识别明显的领域概念和之间的关联(1:1, 1:n的关系),用文字精确没有歧义的描述出每个领域概念的含义;
分析主要的软件功能,识别主要的应用层的类,这样有助于及早发现哪些是应用层的职责,哪些是领域层的职责;
进一步分析领域模型,识别出实体、值对象、领域服务;
分析关联,通过对业务的深入分析和软件设计原则及性能方面的权衡,明确关联的方向,去掉一些不需要的关联;
找出聚合边界及聚合根,在分析过程中会出现难以清洗判断的选择问题,这就依赖平时分析经验的积累了;
为聚合根配置仓储,一般情况下为一个聚合分配一个仓储,此时设计好仓储的接口即可;
遍历所有场景,确定设计的领域模型能有效解决业务需求;
考虑如何创建实体和值对象,是通过工厂还是构造函数;
重构模型,寻找模型中有疑问或蹩脚的地方,比如思考:聚合的设计是否正确,模型的性能等等;
领域建模是一个不断重构,持续完善的过程,大家会在讨论中将变化的部分反映到模型中,从而模型不断细化并朝正确的方向走。
现实世界中的领域
现实世界中的领域包括 问题空间(Problem Space)和 解决方案空间(Solution Space):
问题空间:是核心域和其他子域的组合,思考的是 业务面临的挑战
解决方案空间:一组特定的 软件模型,包括一个或多个限界上下文,思考的是如何实现软件(限界上下文 即是一个 特定的解决方案,通过软件的方式实现解决方案)以 解决这些业务挑战
限界上下文
一个 显式的边界(主要是一个语义上的边界),领域模型便存在于这个边界之内;每一个模型概念(包括它的属性和操作)在边界之内都具有特殊的含义;
一个 给定的业务领域 会包含多个限界上下文,想与一个限界上下文沟通,则需要通过显示边界进行通信;系统通过确定的限界上下文来进行解耦,而每一个上下文内部紧密组织,职责明确,具有较高的内聚性;
一个很形象的隐喻:细胞质所以能够存在,是因为细胞膜限定了什么在细胞内,什么在细胞外,并且确定了什么物质可以通过细胞膜(引用);
领域事件:
对 领域中 所发生的事件(领域专家所关心的发生在领域中的一些事件)进行建模,即 领域事件(领域模型 的组成部分)
领域事件的特点
领域事件 用来捕获领域中发生的一些事情,开始使用领域事件时,要 对不同的事件进行定义;
“当...时,请通知我” 等等场景
领域事件发布方法
限界上下文内,观察者模式 是一种简单高效的发布领域事件的方法;
限界上下文外,利用 消息机制 将本地限界上下文产生的事件发送到 远程限界上下文 中(我们要保证 所有限界上下文 的最终一致性);
关于领域(Domain)、领域模型(Domain Model)、边界上下文(Bounded Context)的关系
领域就是问题域,问题空间;
领域模型是一种模型,表达了领域中哪些业务需求以及业务规则必须被满足;
每一个领域中的问题,都会有一个对应的领域模型去解决;
Bounded Context的作用是用来对领域模型进行划分;
划分领域就是对问题空间的划分,通俗的理解,就是将大问题拆分为小问题;
划分Bounded Context就是将一个大的领域模型划分为多个小的领域模型;
可以把Bounded Context看成是一种解决方案空间,所以,Bounded Context也可以理解为是对解决方案空间的划分;
理论上,一个Domain可能会对应多个Bounded Context;同样,一个Bounded Context可能也会对应多个Domain;所以他们之间没有绝对的关系。主要是他们划分的依据不同,一个是针对领域(问题空间),一个是针对领域模型(解决方案空间);理想情况,一个Domain最好对应一个Bounded Context;

浙公网安备 33010602011771号