领域驱动设计(DDD)
2021-12-05 09:44:27 102 举报
AI智能生成
领域驱动设计(DDD)是一种软件开发方法,它关注于核心领域和领域逻辑的理解与实现。这种方法强调将复杂的业务问题分解为可管理的小领域,并为每个领域创建专门的模型和代码库。通过这种方式,DDD能够提高软件的可维护性、可扩展性和可测试性。在DDD中,领域专家与开发人员紧密合作,以确保模型准确地反映了业务需求。此外,DDD还鼓励使用通用语言来描述领域概念,以便在不同的团队成员之间进行有效沟通。总之,领域驱动设计是一种以领域为中心的软件开发方法,旨在提高软件质量和开发效率。
作者其他创作
大纲/内容
第6章模型
6.1什么是模型
6.1.1 物理模型
物理模型是拥有体积及重量的物理形态概念实体物件,是根据相似性理论制造的按原系统比例缩小(也可以是放大或与原系统尺寸一样)的实物。例如,风洞实验中的飞机模型、水力系统实验模型、建筑模型和船舶模型和汽车模型(如图6-1所示)等。
6.1.2 数学模型
数学模型是用数学语言描述的一类模型,可以是一个或一组代数方程、微分方程、差分方程、积分方程或统计学方程,也可以是某种适当的组合数学模型
6.1.3 概念模型
概念模型是对真实世界中问题域内的事物的描述,是领域实体,而不是对软件设计的描述,它和技术无关
6.1.4 思维模型
我们把用简单易懂的图形、符号或者结构化语言等表达人们思考和解决问题的形式,统称为思维模型
6.1.5 模型不能代替现实
6.2 UML
UML分为结构型和行为型建模图形
6.3类图
6.3.1 类的UML表示法
1. 类的组成部分
(1)类名(Name):每个类都必须有一个名字,类名是一个字符串。
(2)类的属性(Attributes):属性指类的性质,即类的成员变量。一个类可以有任意多个属性,也可以没有属性。
(3)类的操作(Operations):操作是类的任意一个实例对象都可以使用的行为,是类的成员方法。
2. 可见性、名称和类型的定义
1. 可见性:表示该属性对于类外的元素而言是否可见,包括公有(public)、私有(private)和受保护(protected),在类图中分别用符号+、-和#表示。
2. 名称:按照惯例,类的名称以大写字母开头,单词之间使用驼峰隔开。属性和操作的名称以小写字母开头,后续单词使用驼峰。·
3. 类型:表示属性的数据类型,可以是基本数据类型,也可以是用户自定义类型。
6.3.2 类的关联关系
1.双向关联
在默认情况下,关联是双向的。例如,一位教师(Teacher)可以教授一或多门课程(Course),一门课程只能被一位教师教授,因此Teacher类和Course类之间具有双向关联关系
2.限定关联
限定关联(Qualified association)具有限定符(Qualifier),限定符的作用类似HashMap中的键(key),用于从一个集合中选择一个或多个对象。
3.单向关联
4.自关联
5.聚合关系
1. 聚合(Aggregation)关系表示整体与部分的关联关系
2. 在UML中,聚合关系用带空心菱形的直线表示
3. 例如,汽车发动机(Engine)是汽车(Car)的组成部分,但是汽车发动机可以独立存在
4. 在用代码实现聚合关系时,成员对象通常作为构造方法、Setter方法或业务方法的参数注入整体对象中
6.组合关系
1. 组合(Composition)关系也表示类之间整体和部分的关联关系,但是在组合关系中,整体对象可以控制成员对象的生命周期,一旦整体对象不存在,成员对象也将不存在,成员对象与整体对象之间具有“同生共死”的关系。
2. 在UML中,组合关系用带实心菱形的直线表示
3. 人的头部(Head)与嘴(Mouth),嘴是头部的组成部分,如果头部不存在,那么嘴也就不存在了,因此头部和嘴是组合关系
6.3.3 类的依赖关系
1. 依赖(Dependency)关系是一种使用关系,特定事物的改变可能会影响到使用该事物的其他事物,在需要表示一个事物使用另一个事物时,使用依赖关系。
2. 在UML中,依赖关系用带箭头的虚线表示,由依赖的一方指向被依赖的一方
3. 依赖关系通常通过3种方式来实现
(1)第一种方式(也是常用的一种方式)是将一个类的对象作为另一个类中方法的参数,如图6-12所示。[插图]图6-12 依赖关系实例
(2)第二种方式是在一个类的方法中将另一个类的对象作为其局部变量。
(3)第三种方式是在一个类的方法中调用另一个类的静态方法。
6.3.4 类的泛化关系
1. 泛化(Generalization)关系也称为继承关系,用于描述父类与子类之间的关系。父类称为基类或超类,子类称为派生类
2. 在UML中,泛化关系用带空心三角形的直线来表示
6.3.5 接口与实现关系
1. 面向对象语言中会引入接口的概念。在接口中,通常没有属性,其操作通常都是抽象的,只有操作的声明,没有操作的实现
2. 在UML中,类与接口之间的实现关系通常是用带空心三角形的虚线来表示。
3. 在UML 2中新定义的插座表示法(Socket notation)
6.4领域模型
1. 从本质上来说,软件开发过程就是问题空间到解决方案空间的一个映射转化
2. 我们主要是找出某个业务面临的挑战及其相关需求场景用例分析;而在解决方案空间中,则通过具体的技术工具手段来进行设计实现。
3. “领域模型”就是“解决方案空间”,是针对特定领域里的关键事物及其关系的可视化表现,是为了准确定义需要解决问题而构造的抽象模型,是业务功能场景在软件系统里的映射转化,其目标是为软件系统的构建统一的认知。
4. 例如,请假系统解决的是人力工时的问题,属于人力资源领域,对口的是HR部门;费用报销系统解决的是员工和公司之间的财务问题,属于财务领域,对口的是财务部门;电商平台解决的是网上购物问题,属于电商领域。可以看出,每个软件系统本质上都解决了特定的问题,属于某一个特定领域,实现了同样的核心业务功能来解决该领域中核心的业务需求。
5. 领域模型在软件开发中的主要起到如下作用
1. 帮助分析理解复杂业务领域问题,描述业务中涉及的实体及其相互之间的关系,是需求分析的产物,与问题域相关。
2. 是需求分析人员与用户交流的有力工具,是彼此交流的语言。
3. 分析如何满足系统功能性需求,指导项目后续的系统设计。
6.5敏捷建模
1. 和开发模式一样,建模也可以用一套价值观、原则和实践来进行指导,以求尽可能地敏捷
2. 敏捷建模方法的重点如下。
·模型能用来沟通和理解。·力争用简单的工具创建简单的模型。
·我们知道需求是变化的,因此创建模型时要拥抱变化。
·重点是交付软件,而不是交付模型。
模型能带来价值时,我们就使用;
如果模型没有价值,不能加速软件的交付,就不创建它们。
6.6广义模型
6.6.1 C4模型
C4模型由Simon Brown提出。C4模型提出使用上下文(Context)、容器(Container)、组件(Component)和代码(Code)等一系列分层的图表,来描述不同缩放级别的软件架构
6.6.2 UI流程图
6.6.3 业务模型
6.7本章小结
第7章 DDD的精髓
7.1什么是DDD
7.2初步体验DDD
我们要实现一个银行转账的功能,如果用传统的事务脚本方式实现,业务逻辑通常会被写在MoneyTransferService中,而Account仅仅是getters和setters的数据结构,也就是所谓的“贫血模式”
7.3数据驱动和领域驱动
7.3.1 数据驱动
数据驱动以数据库为中心,其中最重要的设计是数据模型,但随着业务的增长和项目的推进,软件开发和维护的难度会急剧增加。
7.3.2 领域驱动
领域驱动设计关心的是业务中的领域划分(战略设计)和领域建模(战术设计)
领域模型对应的是业务实体,在程序中主要表现为类、聚合根和值对象,它更加关注业务语义的显性化表达,而不是数据的存储和数据之间的关系。这是“领域驱动设计”和“数据驱动设计”之间显著的区别。
通过DDD的战略设计和战术设计,我们可以为问题域划分出合适的子域,并对域中的业务进行建模。
7.3.3 ORM
ORM的问题在于它太理想化,期望通过工具把数据建模和领域建模合一,这样的尝试注定是很难成功的
衰落是可以预见的。现在使用最多的是MyBatis,它很简单,完全不理
7.4 DDD的优势
7.4.1 统一语言
统一语言(Ubiquitous Language)的主要思想是让应用能和业务相匹配,这是通过在业务与代码中的技术之间采用共同的语言达成的。
7.4.2 面向对象
领域模型的设计精髓在于面向对象分析、对事物的抽象能力,一个领域驱动架构师必然是一个面向对象分析的大师。
7.4.3 业务语义显性化
统一语言使得我们的核心领域概念可以无损地在代码中呈现,从而提升代码的可理解性。
7.4.4 分离业务逻辑和技术细节
代码复杂度是由业务复杂度和技术复杂度共同组成的
为什么说数据库、框架和UI都是技术细节呢?
1. 数据库:业务逻辑不应该受限于存储方式,也就是不论你是使用关系型数据库还是NoSQL,都不应该影响业务逻辑的实现。数据本身很重要,但数据库技术仅仅是一个实现细节。
2. UI:UI只是一种I/O设备的呈现,Web、WAP和Wireless都是不同的I/O,我们的核心业务逻辑应该与如何呈现解耦,以及针对不同的端可以使用不同的适配器(Adaptor)去做适配。
3. 框架:不要让框架侵入我们的核心业务代码,以Spring为例,最好不要在业务对象中到处写@autowired注解。业务对象不应该依赖框架。
7.5 DDD的核心概念
7.5.1 领域实体
现实世界中的事物在软件世界中可以被模拟成一个对象:该事物在现实世界中被赋予什么职责,在软件世界中就被赋予什么职责;在现实世界中拥有什么特性,在软件世界中就拥有什么属性;在现实世界中拥有什么行为,在软件世界中就拥有什么函数;在现实世界中与哪些事物存在怎样的关系,在软件世界中就应当与它们发生怎样的关联
用例分析法
一个简单的方式就是“找名词”
7.5.2 聚合根
聚合根(Aggregate Root)是DDD中的一个概念,是一种更大范围的封装,会把一组有相同生命周期、在业务上不可分割的实体和值对象放在一起,只有根实体可以对外暴露引用,这也是一种内聚性的表现。
确定聚合边界要满足固定规则(Invariant),是指在数据变化时必须保持的一致性规则
1. 根实体具有全局标识,最终负责检查规定规则。
2. 聚合内的实体具有本地标识,这些标识在Aggregate内部才是唯一的。
3. 外部对象不能引用除根Entity之外的任何内部对象。
4. 只有Aggregate的根Entity才能直接通过数据库查询获取,其他对象必须通过遍历关联来发现。·
5. Aggegate内部的对象可以保持对其他Aggregate根的引用。
6. Aggregate边界内的任何对象在修改时,整个Aggregate的所有固定规则都必须满足。
7.5.3 领域服务
当这样的行为从领域中被识别出来时,推荐的实践方式是将它声明成一个服务。
(1)服务执行的操作代表了一个领域概念,这个领域概念无法自然地隶属于一个实体或者值对象。
(2)被执行的操作涉及领域中的其他对象。
(3)操作是无状态的。
7.5.4 领域事件
1.事件命名
事件是表示发生在过去的事情,所以在命名上推荐使用Domain Name +动词的过去式+ Event,这样可以更准确地表达业务语义。
2.事件内容
(1)自恰(Enrichment):就是在事件的payload中尽量多放数据,这样consumer不需要回查就能处理消息,也就是自恰地处理消息。
(2)回查(Query-Back):这种方式是只在payload放置id属性,然后consumer通过回调的形式获取更多数据。这种形式会加重系统的负载,可能会引起性能问题。
7.5.5 边界上下文
边界上下文(Bounded Context)的作用是限定模型的应用范围,在同一个上下文中,要保证模型在逻辑上统一,而不用考虑它是不是适用于边界之外的情况。
在DDD中,这种机制叫作上下文映射(Context Mapping),我们可以使用防腐层(Anti-Corruption)来完成映射的工作。
7.6领域建模方法
7.6.1 用例分析法
1.方法介绍
(1)获取用例描述
(2)寻找概念类
寻找概念类就是对获取的用例描述进行语言分析,识别名词和名词短语,将其作为候选的概念类
(3)添加关联
关联意味着两个模型之间存在语义联系,在用例中的表现通常为两个名词被动词连接起来
·并非所有动词关联的概念类都需要作为关联存在,更重要的是我们需要判断两个概念类的关系是否需要被记住。
·应该尽量避免加入大量关联。
·关联不代表数据流,也不代表系统调用关系。
(4)添加属性
如何判断一个名词是否是属性?
·能完全通过基本数据类型(数字、文本、日期)表达的大多是属性。
·如果一个名词只关联一个概念类,并且其自身没有属性,那么它就是另一个概念类的属性。
(5)模型精化
模型精化是可选的步骤,有时我们希望在领域模型中表达更多的信息,这时会利用一些新的手段来表达领域模型,包括泛化、组合和子域划分等
子领域划分是常见的拆解领域的方式
2.案例介绍
(1)寻找概念类
(2)添加关联
(3)添加属性
7.6.2 四色建模法
1.方法介绍
(1)业务关键时刻(Moment-Interval)
这种对象表示那些在某个时间点存在或者会存在一段时间
(2)角色(Role)
这种对象表示一种角色,往往由人或者物来承担,会有相应的责任和权利。一般,一个Moment-interval对象会关联多个Role
2.案例介绍
(1)首先以满足管理和运营的需要为前提,寻找需要追溯的事件,或者称为关键业务时刻。
(2)根据这些需要追溯,寻找足迹以及相应的关键业务时刻对象。
(3)寻找“关键业务时刻”对象周围的“人-事-物”对象。
(4)从“人-事-物”中抽象出角色。
(5)把一些描述信息用对象补足。
7.7模型演化
7.8为什么DDD饱受争议
7.8.1 照搬概念
7.8.2 抽象的灵活性
7.8.3 领域层的边界
7.9本章小结
0 条评论
下一页