写点什么

这些 DDD"术语"你知道吗?

作者:idonkeyliu
  • 2022 年 3 月 16 日
  • 本文字数:7663 字

    阅读完需:约 25 分钟

这些DDD"术语"你知道吗?

DDD

Domain Driven Design

中文名是领域驱动设计,领域驱动设计是一种方法论,是针对某个领域需要解决的特定问题,从纷繁复杂的现实世界中抽象提炼出业务模型的思维方式与实践技巧,强调聚焦于“业务知识”。既然有领域驱动设计,那肯定还有 XX 驱动设计。设计四大范式,有数据表驱动、事物脚本驱动、活动记录模式和领域驱动设计,其中前两个是面向过程的,后两个是面向对象的,各有侧重。具体见软件架构设计模式简述

领域

Domain

学术思想或社会活动的范围。具体指一种特定的范围或区域。                            -百度百科
复制代码

领域指的是一个特定的业务范围,可以理解为 领域 = 业务问题

子域

Subdomain

子域与领域不是父子关系,而是包含关系。当多个业务域(领域)的组合形成了一个更大的业务域(领域)时,其中每一个领域(业务域)是这个更大的业务域的一部分,每一个业务域相对于这个更大地业务域称之为这个更大领域的子业务域,简称子域。可以认为 子域 = 部分业务问题。子领域是领域专家根据领域经验选择合适的功能分类策略进行划分,这个过程不会牵扯对业务服务的分析,体现的是领域专家对行业的洞见和深刻认识,获取子领域是一个自顶向下的过程。既然领域可以划分为多个子域,那么多个子域并不是同等重要的,我们可以对子域的重要性进行归类。



核心域

Core (Sub)Domain

核心域是整个业务系统的核心,所有的业务都要围绕着核心业务域展开。核心域是通过分析业务愿景得出的,而不是程序员决定的。核心域通常需要花大的资源来投入,核心域的建模应该是组织中的核心/资深人员来承担。

支撑域

Supporting (Sub)Domain

在业务域中,会有一些比较重要的业务,但却不是核心,那么它便是一个支撑子域。创建支撑子域的原因在于它们专注于业务的某个方面。它不像核心域在整个系统中那么重要。该子域一般建议由组织中的中级开发来承担该类领域的建模与实现,当然也可以考虑外包出去。

通用域

Generic (Sub)Domain

通用域类似于工具,是横向的概念,如果一个业务问题大家都会遇到,那么它就属于通用域。比如认证、权限等等,这类应用很容易买到,没有企业特点限制,不需要做太多的定制化。如果可以的话可以外包或者直接购买 SAAS。即使需要自己开发的话,也可以让组织中的初级开发者来承担该类领域的建模与实现。


统一语言

Ubiquitous Language

统一语言是提炼领域知识的产出物,获得统一语言就是需求分析的过程,也是团队中各个角色就系统目标、范围与具体功能达成一致的过程。统一语言的范围是所有角色而非仅仅局限于工程师。 开发人员与领域专家之间,他们掌握的知识存在巨大的差异。善于技术的开发人员关注于数据库、通信机制、集成方式与架构体系,而精通业务的领域专家对这些不甚了解,但他们在讲解业务知识时,使用各种概念如呼吸一般自然,这些对于开发人员来说,却成了天书,这种交流就好似使用两种不同语言的外国人在交谈。在没有达成一致的情况下,那就是鸡同鸭讲,毫无沟通效率,相反还可能造成误解。同时开发人员与开发人员之间也会有对概念命名发生冲突的时候,比如弹幕,有的叫 danmu,有的却叫 toast,这样对开发内部的交流也是一个阻碍。

在领域建模过程中,我们往往需要在文档中建立一个大家一致认可的术语表。语表中需要包括整个团队精炼出来的术语概念,以及对该术语的清晰明白的解释。为难以理解的术语提供具体的案例。一旦发生变更,也需要及时地对其进行更新。在维护领域术语表时,一定需要给出对应的英文术语,否则可能直接影响到代码实现
复制代码

事件风暴

Event Storming

事件风暴是由 Alberto Brandolini 发明的一种轻量级,基于 DDD 概念的系统分析方法。事件风暴最大的作用是帮助开发人员,业务人员,UX,测试等项目参与者对于业务流程有一个统一的认识,这包括关键的流程,核心的业务规则,系统不同模块的使用者。其次是帮助开发人员梳理核心的业务对象,从某种程度上来说就是就是领域对象中的聚合。事件风暴都是以 Workshop 的形式展开,实施成本比较高

问题空间

Question Space

哲学家常常会围绕真实世界和理念世界的映射关系探索人类生存的意义,即所谓“两个世界”的哲学思考。软件世界也可一分为二,分为构成描述需求问题的真实世界与获取解决方案的理念世界。整个软件构建的过程,就是从真实世界映射到理念世界的过程。 定义:真实世界 利用核心子领域、通用子领域、支撑子领域来分解问题空间 问题空间:价值需求+业务需求(业务功能、业务实现) 其中属于问题空间的概念有领域、子域等

解空间

Solution Space

定义:使用软件世界构建的解空间 围绕着领域这一核心开展业务系统的战略设计与战术设计 其中属于解空间的有统一语言、限界上下文、上下文映射等



系统上下文

System Context 定义:定义目标系统解空间的边界,边界范围外的系统是伴生系统。系统上下文就是解空间的边界,该系统是为了解决该解空间的问题,而非解决另外的解空间的问题。

限界上下文【DDD 中核心概念】

Bounded Context

定义: 通过领域知识(业务规则)更清晰的划分边界,并且通过领域知识(业务规则)统一语言特征:它是领域模型的知识语境,是业务能力的纵向划分,是自治的单元- 自我履行:限界上下文能够根据自己拥有的知识来完成业务能力。自我履行体现了限界上下文纵向切分业务能力的特征。- 最小完备:限界上下文在履行属于自己的业务能力时,拥有的领域知识是完整的,无须针对自己的信息去求助别的限界上下文。- 稳定空间:限界上下文必须防止和减少外部变化带来的影响。- 独立进化:指减少限界上下文内部变化对外界产生的影响。
复制代码

限界上下文是语义和语境的边界。限界上下文拆开来看就是限界和上下文。限界就是领域的边界,而上下文则是语义环境。通过领域的限界上下文,我们就可以在统一的领域边界内用统一的语言进行交流。

限界上下文是 DDD 中比较核心且抽象难懂的概念。

上下文映射

Context Map

限界上下文封装了分离的业务能力,上下文映射则建立了限界上下文之间的关系。上下文映射提供了各种模式(防腐层、开放主机服务、发布语言、共享内核、合作者、客户方/供应方、分离方式、遵奉者、大泥球),本质是在控制变化在限界上下文之间传递所产生的影响



 具体见DDD领域驱动战略篇(3) 上下文映射与协作

战略设计

Strategic Design

“战略设计(strategic design)”一词最早来自于 2004 年 Eric Evans 最初的《领域驱动设计:软件核心复杂性应对之道》一书的最后一个部分(第四部分:战略设计)。


战略设计:一种针对系统整体的建模和设计决策。这样的决策影响整个项目,而且必须由团队来制定。---Eric Evans 
复制代码

战略设计包括统一语言、子域、领域、限界上下文、上下文映射等。具体见领域驱动实战思考(二):用分段思想改进那些混乱的战略设计和战术设计

战术设计

战略设计为我们提供一种高层视野来审视我们的软件系统,而战术设计则将战略设计进行具体化和细节化,它主要关注的是技术层面的实施,也是对程序员来得最实在的地方。很多团队会优先进行战术设计,其实这有点本末倒置了,容易变成自嗨型的 DDD。

分层架构

分层的本质是关注点分离,隔离对下层的变化。由于层间松散的耦合关系,使得我们可以专注于本层的设计,而不必关心其他层的设计,也不必担心当前层的设计会影响其它层。分层架构中最难的问题是决定建立那些层次和每一个层次的职责,不同于传统的三层架构,Eric Evans 在领域驱动设计的第四章分离领域中提出来 DDD 的四层架构(Layered Architecture)



各层定位于职责:用户接口层:1. 协议转换,将外界的协议转换为DTO 2. 接口定义应用层:1. 用户用例的映射  2. 业务逻辑编排而非实现领域层:1. 业务逻辑的实现基础设施层:1. 领域对象与数据对象转换 2. 持久化 3. 工具&配置
复制代码

应用服务

Application Service

应用服务是用来表达用例和用户故事(User Story)的主要手段。

应用层通过应用服务接口来暴露系统的全部功能。在应用服务的实现中,它负责编排和转发,它将要实现的功能委托给一个或多个领域对象来实现,它本身只负责处理业务用例的执行顺序以及结果的拼装。通过这样一种方式,它隐藏了领域层的复杂性及其内部实现机制。

应用层相对来说是较“薄”的一层,除了定义应用服务之外,在该层我们可以进行安全认证,权限校验,持久化事务控制,或者向其他系统发生基于事件的消息通知,另外还可以用于创建邮件以发送给客户等。

应用层作为展现层与领域层的桥梁。展现层使用 VO(视图模型)进行界面展示,与应用层通过 DTO(数据传输对象)进行数据交互,从而达到展现层与 DO(领域对象)解耦的目的。 使用应用服务的时候尽量遵循以下几个原则

1. 应用层是用户用例的映射,包括所有的用户接口,是用户接口层定义的接口的具体实现2. 应用层不能包括业务逻辑,也就是不能包括if else等。注意包括多个if的异常判断是可以的3. 应用层调用多个领域服务或者领域模型来完成用户用例逻辑,只是逻辑的编排者而非实现者
复制代码

领域服务

Domain Service

领域层就是较“胖”的一层,因为它实现了全部业务逻辑并且通过各种校验手段保证业务正确性。而什么是业务逻辑呢?业务流程、业务策略、业务规则、完整性约束等。 当领域中的某个操作过程或转换过程不是实体或值对象的职责时,我们便应该将该操作放在一个单独的接口中,即领域服务。请确保该服务和通用语言时一致的;并且保证它是无状态的。 使用领域服务的时候尽量遵循以下几个原则

1. 尽量不要使用领域服务,因为一旦开口了,很容易将领域模型的逻辑转移到领域服务来实现,从而造成贫血模型2. 当某个业务逻辑不适合在领域模型中实现的时候可以考虑领域服务3. 领域服务是解决跨实体的业务逻辑,主要是跨实体而非跨聚合,聚合中可以包括多个实体4. 领域服务之间不允许互相调用
复制代码

领域消息/领域事件

Domain Event

A domain event is a full-fledged part of the domain model, a representation of something that happened in the domain. Ignore irrelevant domain activity while making explicit the events that the domain experts want to track or be notified of, or which are associated with state change in the other model objects.
复制代码

领域事件是一个领域模型中极其重要的部分,用来表示领域中发生的事件。忽略不相关的领域活动,同时明确领域专家要跟踪或希望被通知的事情,或与其他模型对象中的状态更改相关联。

  • 领域事件作为领域模型的重要部分,是领域建模的工具之一。

  • 用来捕获领域中已经发生的事情。领域事件的命名一般是XXX已XXX

  • 并不是领域中所有发生的事情都要建模为领域事件,要忽略无业务价值的事件。

  • 领域事件是领域专家所关心的(需要跟踪的、希望被通知的、会引起其他模型对象改变状态的)发生在领域中的一些事情。

  • 简而言之,领域事件是用来捕获领域中发生的具有业务价值的一些事情。它的本质就是事件,不要将其复杂化。在 DDD 中,领域事件作为通用语言的一种,是为了清晰表述领域中产生的事件概念,帮助我们深入理解领域模型。 具体见DDD理论学习系列(9)– 领域事件

应用消息/应用事件

Application Event

事件的发送,处理逻辑放在应用服务层,即 Application Service 中。 事件的使用建议

1. 事件的发送和订阅在应用层2. 事件的定义和处理可以在领域层
复制代码



聚合【DDD 中核心概念】

Aggregate

聚合就是由业务和逻辑紧密关联的实体和值对象组合而成的,聚合是数据修改和持久化的基本单元,每一个聚合对应一个仓储,实现数据的持久化。 聚合有一个聚合根和上下文边界,这个边界根据业务单一职责和高内聚原则,定义了聚合内部应该包含哪些实体和值对象,而聚合之间的边界是松耦合的。按照这种方式设计出来的微服务很自然就是“高内聚、低耦合”的。

聚合设计的一些原则1. 在一致性边界内建模真正的不变条件。聚合用来封装真正的不变性,而不是简单地将对象组合在一起。聚合内有一套不变的业务规则,各实体和值对象按照统一的业务规则运行,实现对象数据的一致性,边界之外的任何东西都与该聚合无关,这就是聚合能实现业务高内聚的原因。
2. 设计小聚合。如果聚合设计得过大,聚合会因为包含过多的实体,导致实体之间的管理过于复杂,高频操作时会出现并发冲突或者数据库锁,最终导致系统可用性变差。而小聚合设计则可以降低由于业务过大导致聚合重构的可能性,让领域模型更能适应业务的变化。
3. 通过唯一标识引用其它聚合。聚合之间是通过关联外部聚合根 ID 的方式引用,而不是直接对象引用的方式。外部聚合的对象放在聚合边界内管理,容易导致聚合的边界不清晰,也会增加聚合之间的耦合度。
4. 在边界之外使用最终一致性。聚合内数据强一致性,而聚合之间数据最终一致性。在一次事务中,最多只能更改一个聚合的状态。如果一次业务操作涉及多个聚合状态的更改,应采用领域事件的方式异步修改相关的聚合,实现聚合之间的解耦。
5. 通过应用层实现跨聚合的服务调用。为实现微服务内聚合之间的解耦,以及未来以聚合为单位的微服务组合和拆分,应避免跨聚合的领域服务调用和跨聚合的数据库表关联。
复制代码

实体

Entity

实体(Entity,又称为 Reference Object)很多对象不是通过他们的属性定义的,而是通过一连串的连续事件和标识定义的。主要由标识定义的对象被称为 ENTITY。实体是与其他事物分开存在的事物,并且具有自己的明确标识。 为一个事物定义了明确的标识后,会使你有一种错觉。 你会感觉只要为事物定义明确标识后,事物就是实体或者实体就是事物了。

实体最主要有两点特征,一是唯一标识,二是连续性。
唯一标识:当一些对象不是由属性定义,而是由一个唯一标志定义的话,我们就可以认为它是一个实体。好比我们不能通过一个人的外在特征去唯一定位一个人,因为人从小到大,从年轻到衰老其外在特征都是在改变的。而身份证号码可以贯穿一个人的一生而不发生变化。而且唯一标识不一定仅有一个属性表示,有可能通过多个属性标识某一个唯一对象,就好比数据库中的联合外键(可能有根据电话以及姓名唯一确定一个实体的情况)。
连续性:对象的连续性体现在对象是有生命周期的。在这个生命周期内,对象内的属性可能是变化着的。好比银行账户表,它就属于一个实体,用户的银行卡号可以唯一的确定一个人的账户,而账户的内的余额随着时间变化(利息)或者随着交易变化。
复制代码

值对象

Value Object

很多对象没有概念上的表示,他们描述了一个事务的某种特征。用于描述领域的某个方面而本身没有概念表示的对象称为 Value Object(值对象)。上面这句话比较抽象难以理解,值对象是 DDD 领域模型中的一个基础对象,跟实体一样源于事件风暴所构建的领域模型,都包含若干属性,与实体一起构成聚合。 实体是看得到、摸得着的实实在在的业务对象,实体具有业务属性、业务行为和业务逻辑。 值对象只是若干个属性集合,只有数据初始化操作和有限的不涉及修改数据的行为,基本不包含业务逻辑。值对象与实体最大的区别是实体有唯一标识而值对象没有

领域原语

Domain Primitive

▍Domain Primitive 的定义
让我们重新来定义一下 Domain Primitive :Domain Primitive 是一个在特定领域里,拥有精准定义的、可自我验证的、拥有行为的 Value Object 。
DP是一个传统意义上的Value Object,拥有Immutable的特性DP是一个完整的概念整体,拥有精准定义DP使用业务域中的原生语言DP可以是业务域的最小组成部分、也可以构建复杂组合注:Domain Primitive的概念和命名来自于Dan Bergh Johnsson & Daniel Deogun的书 Secure by Design。
▍使用 Domain Primitive 的三原则让隐性的概念显性化让隐性的上下文显性化封装多对象行为▍Domain Primitive 和 DDD 里 Value Object 的区别在 DDD 中, Value Object 这个概念其实已经存在:
在 Evans 的 DDD 蓝皮书中,Value Object 更多的是一个非 Entity 的值对象在Vernon的IDDD红皮书中,作者更多的关注了Value Object的Immutability、Equals方法、Factory方法等Domain Primitive 是 Value Object 的进阶版,在原始 VO 的基础上要求每个 DP 拥有概念的整体,而不仅仅是值对象。在 VO 的 Immutable 基础上增加了 Validity 和行为。当然同样的要求无副作用(side-effect free)。
复制代码

具体见阿里技术专家详解 DDD 系列 第一讲- Domain Primitive

仓储

Repository

repository,又作仓库、仓储。在 DDD 中,是用来持久化领域对象(entity, value object)的 Repository 的职责仅是数据永久化,当然 repository 是不能够包含业务逻辑的。

数据传输对象

DTO

数据传输对象(Data Transferring Object,DTO)在层与层之间进行传递,无业务含义。qry cmd 和 event 都属于 DTO。

视图对象

View Object

视图对象,为 ui 提供数据,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来

业务对象

BO

Business Object,业务对象,在业务逻辑层封装业务逻辑,一般就是聚合或者 Entity

领域对象

DO

Domain Object, 领域对象,就是从现实世界中抽象出来的有形或无形的业务聚合或者实体。

持久化对象

Persistent Object

持久对象,这里用于描述数据库中特定 table。它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应 PO 的一个(或若干个)属性。

数据对象

Data Object

同 PO, 类似

数据访问对象

DAO

数据访问对象

防腐层

Anti-corruption layer / ACL

DDD(Eric Evans)中引入的模式, 用于隔离两个系统, 允许两个系统之间在不知道对方领域知识的情况下进行集成。主要进行的是两个系统之间的 model(模型)或者协议的转换, 并且最终目的是为了系统使用者的方便而不是系统提供者的方便, 进一步解释就是 ACL 尽量把系统提供者的模型转换为系统使用者的模型(而不引入中间第三者模型)

工厂

Factory

Entity 的创建组件

CQRS

Command-Query Responsibility Segregation

CQRS(Command-Query Responsibility Segregation) 是一种读写分离的模式,从字面意思上理解 Command 是命令的意思,其代表写入操作;Query 是查询的意思,代表的查询操作,这种模式的主要思想是将数据的写入操作和查询操作分开。

贫血模型

anaemic domain model

贫血模型中只包含了数据,而不包含任何的业务逻辑。 在一些系统中,数据库映射实体、RPC 请求和返回结果等被设计为贫血模型。这里的“血”指的是业务逻辑

充血模型

rich domain model

在贫血模型的基础上,充血模型的对象拥有了自身的业务方法,能够对外暴露一些“动作”。在贫血模型的基础上,充血模型的对象拥有了自身的业务方法,能够对外暴露一些“动作”。充血模型是 DDD 建议的模型。具体见领域模型、贫血模型、充血模型概念总结

其余相关概念

BDD

Behavior-driven development,即行为驱动开发。其目的是鼓励软件项目中的开发者、QA 和非技术人员或商业参与者之间的协作。是从用户的需求出发,强调系统行为。具体见混合开发:TDD、DDD和BDD交集的值

TDD

Test-driven development,即测试驱动开发。一种开发过程中应用方法。其思想为先根据需求抽象接口,先编写测试用例,然后在开始编写开发代码。TDD 的本意就是通过测试来推动整个开发的进行。具体见混合开发:TDD、DDD和BDD交集的值

术语表


用户头像

idonkeyliu

关注

还未添加个人签名 2018.04.16 加入

做个纯粹的人~

评论

发布
暂无评论
这些DDD"术语"你知道吗?_DDD_idonkeyliu_InfoQ写作平台