**建模的重要性:**世界很大,想去旅行需要一张地图。世界地图就是地球的一个“简化”模型,反映了真实世界在地理位置这个维度上呈现出来的面貌。面对复杂的事物,建立一个简化的模型是关键,能够帮助我们解决现实生活中的问题。

建模对软件开发的重要性被低估了……

  • 传统的软件开发模式,是以“数据”为中心,构建于特定软件平台和技术框架之上的,关注点一是数据、二是功能、三是技术

  • 整个系统开发过程中,并没有对“业务领域模型”加以特殊的关注……

**大泥球BBM(Big Ball of Mud)困境:**大泥球指一个系统几乎没有组织,到处都有隐藏的依赖关系以及大量重复数据和代码,各层和关注点也没有清晰标识,代码就像“意大利面条”,所有东西都混在一起。

BBM这个词由Brian Foote和Joseph Yoder“发明”:http://www.laputan.org/pub/foote/mud.pdf

数据/技术驱动还是业务驱动?DDD领域驱动设计强调业务领域是最重要的,数据不应该成为主导,而技术必须服从于业务需求。

DDD是什么?DDD主要用于解决业务逻辑复杂的软件系统的设计问题,是对大量复杂软件系统开发实践经验的一种总结。主要难度在于业务复杂,而非技术难度大。

  • 捕获在设计过程中的关键要素
  • 将它们组织为一系列的设计原则
  • 把领域模型的构建当作开发的中心
  • 提供了一组典型方法构建业务逻辑

2、DDD如何设计与开发软件

DDD领域驱动设计的奠基之作:为了应对越来越复杂的软件系统复杂性,Eric Evans总结人们开发经验,提出了颇具创行性的软件开发指南和设计方法 Domain-Driven Design领域驱动开发——软件核心复杂性应对之道

DDD与传统软件开发方式有何不同?——DDD如何解决实际问题?

DDD是怎样开发软件的?

DDD的两个主要阶段

DDD的基本流程

DDD眼中的软件系统

DDD最适合的领域:业务逻辑复杂的软件系统(如某大型电子商务集团),需要多个上下流软件系统“对接”……

  • Q:我编写的软件系统业务很简单,主要就是一些CRUD操作,有必要应用DDD吗?
  • A:对于这种业务逻辑很简单的系统,可以不用DDD,仍然采用以数据为中心的开发方式,但开发者如果能理解DDD中的许多原则和设计理念,则有助于得到更好的系统设计方案

为什么我们要关注DDD?

  • DDD总结了人们解决困难的问题、开发复杂的软件系统的经验和教训,给出了一些颇具价值的指导性原则和具体实施建议
  • DDD的目的是创建一个健壮的软件领域模型,使整个软件系统能支持长期的演化和升级
  • 应用DDD的指导原则,我们能开发出易于维护持续演化的生命周期比较长的软件系统

应用DDD来开发软件系统的具体好处

  • 可以得到一个灵活的、能很好地“拥抱变化的软件系统
  • 因为DDD强调从用户角度来分析和解决问题,因此能让我们开发出“正确”的软件,解决那些用户真正要解决的问题
  • DDD提供了具体的建议,并有业界大量的实例做参考,能帮助我们达到这样一个目的——做正确的事

用DDD设计的“伟大的”软件系统的特点

  • 业务逻辑集中于一个地方,而不会分布到N个软件模块中,不会显得支离破碎
  • 各子系统间的边界和关系明确,整个系统划分得井井有条,易于测试,也易于维护、升级甚至替换
  • 在领域事件的驱动下,我们实际上构建了一个完全可以自行运转而无需人工时时参与控制的软件系统,并且可以进一步地利用人工智能理论于技术成果,让其更具有智能性。

在复杂软件系统中使用DDD的益处:软件系统开发成本随着其复杂性提升线性增加,而非指数增加

在开发中应用DDD的问题:

  • DDD的目标是应对软件开发的复杂性,提出了很好的建议于指南,在具体实施时:做对并不难,做错非常容易
  • 沟通通常会成为一个问题:开发者需要与领域专家进行大量的沟通。而如果沟通不畅,争吵、扯皮、踢皮球,足以让所有人筋疲力尽……
  • 开发者要学很多新东西,需要转换一些原有的开发思路,放弃一些已经很熟悉的开发“套路”

3、创建DDD中的“通用语言”

DDD涉及到的两大人群:领域专家和软件开发者

语言对协作的影响

通用语言的产生

什么是通用语言Ubiquitous Language?

  • 通用语言——特定人群使用的“行话”,开发团队使用它构建领域模型
  • 通用语言是针对特定领域的一组词汇表,由所有与开发相关联的人(包括领域专家和开发人员)使用,主要目的是为了避免歧义,用于日常交流与开发文档的撰写。

如何建立通用语言?

  • 从用户需求描述开始,仔细区分名词和动词,从中选择那些能正确反映出业务领域意义的词汇组成通用语言
  • 不同的概念使用不同的术语,相同的含义使用相同的术语

4、DDD领域模型的构成要素

  • DDD的着力点是“业务领域”而不是“技术”,其目的是创建一个可测试的、可伸缩的、组织良好的软件模型,这个模型称为“领域模型
  • 领域模型是关于某个特定业务领域的软件模型,通常领域模型通过对象模型来实现,这些对象模型包含了数据和行为,并且表达了准确的业务含义。

使用模型作为语言的支柱

什么叫“领域Domain”?

  • 领域是一个组织所做事情以及其中所包含的一切
  • 我们所开发的软件,就是为了解决领域中的特定问题
  • 领域中的事物,使用通用语言进行描述

领域建模的准备工作——领域建模的“原材料”

  • 发现名词,确定特定术语的含义
  • 判别出各类典型的业务活动
  • 明确各种业务规则

构成领域模型的三种主要元素:实体、值对象、服务

  • **实体Entry:**拥有唯一标识符,其状态会变化,并且需要持久化保存(到数据库或文件)
  • 值对象Value Object:没有标识,不需要保存到数据库中,通常作为实体的一部分而被“间接”保存。通常被设计为只读,例如String类
  • 服务Service:当领域中某个重要的过程(如某业务流程)或数据转换操作不属于实体或值对象的自然职责时,将其封装为Service

构成领域模型的核心是**聚合对象:**包容实体对象和值对象的聚合对象称为领域对象Domain Object

领域对象使用“模块Module”进行管理

  • 模块Module是一种分类机制,将逻辑上密切相关的“实体”、“值对象”和“聚合对象”归为一组,必要时也可以包容相关的领域服务和领域事件

  • 模块Module类似于编程语言中的“命名空间Name Space”

领域服务Domain Service:执行特定领域的操作,通常涉及到多个领域对象

**领域事件Domain Event:**由聚合对象和领域服务创建发布,外部响应事件

5、DDD中的限界上下文(上)

**限界上下文Bounded Context:**包含“实体”、“值对象”、“聚合对象”、“领域服务”、“领域事件”等内容。是对应特定业务领域和范围的具体模型

同一事物,可建立不同的模型

  • 限界上下文为所有功能、模型和业务流程设定了一个坚实的逻辑基础;将特定组件的作用限制于边界之内,不会影响到外部

  • 软件系统所涉及的业务领域——>多个限界上下文Bounded Context:一对多关系
  • 限界上下文Bounded Context——>唯一通用语言Ubiquitous Language:一对一关系

  • 相同的术语,在不同的限界上下文中可能会有不同的含义,比如“用户”在商品库存管理系统中,在线浏览产品目录时,所包含的数据项是不一样的。不同限界上下文进行数据交换时,通常需要翻译

都是User但有不同的实际指代

  • 建立上下文的目的是为了让术语在这一界限内有统一的含义

案例:图书出版流程中,图书Book这个实体在不同阶段拥有不同的属性

6、DDD中的限界上下文(下)

共享内核Shared Kernel:共享界限的上下文。抽取两个限界上下文中的共同部分,构建新的上下文实现统一术语和领域对象重用……

  • 通过创建一个新的上下文,原先的两个上下文现在使用统一的术语、共享相同的领域对象

Bounded Context之于软件,类似细胞之于生物……

一个复杂的软件系统通常包容多个Bounded Context。我们需要关注它们之间的信息交换与合作方式……

  • 使用上下文映射Context Map展示信息的对应:由于信息流动需要跨越界限上下文的边界,因此,不同上下文中的术语或概念存在着一种对应和关联,Context Map可以展示这种对应和关联。

  • 限界上下文之间的协作关系:Context Map使用“上游Upstream”和“下游Downstream”两个单词描述两个限界上下文Bounded Context间的关系——上游可以影响到下游

几种典型的限界上下文间的关系

数据在限界上下文间的状态同步:发布订阅、双向通讯

反腐层Anti-Corruption Layers:用于隔离多个限界上下文Bounded Context,或将外界不使用DDD开发的系统集成到本系统

限界上下文Bounded Context的技术现实

  • 每一个限界上下文都包含着一个完整的软件系统,可以采用多种架构实现

限界上下文和分层架构

  • 集成多个限界上下文的三种典型技术实现方式:远程方法调用、RESTful/GraphQL服务、消息队列

案例:Visual Studio项目中的限界上下文

  • 为每个限界上下文创建和一个解决方案文件夹
  • 每一个项目都包含着目的明确的代码和组件

**限界上下文与微服务:**限界上下文的特性和微服务的特性相当吻合,在实际开发中人们经常将微服务与限界上下文对应起来

  • 通常情况下,限界上下文可以实现为多个微服务。这些微服务有些是“对外的",允许外界访问;有些是“内部使用的”,限界上下文定义了它的可访问边界

7、模型重构的故事

软件开发中的重构

  • 代码重构——编程语言IDE支持
  • 模型重构——DDD要解决的问题

为什么要重构模型?

  • 人们对于世界的认识,并不是一次能完成的,很有可能整个方向都错了。当我们发现以前的认识是错误的时候,就应该重构领域模型以逼近事物的真相

通过切掉螃蟹的腿研究其听觉器官在腿上

案例:以人们对大象的认知建模为例

  • 初步:通过局部认知构建的模型

  • 第一次模型重构——组合

  • 第二次模型重构——抽象

必须看到领域模型重构的风险

  • 模型的重构可以比拟为数据结构发生了变化,相应的,算法通常也得改,看上去成本太高了

DDD领域模型重构的经验法则

  • 模型的重构意味着我们对事物的认识更为深刻,更接近事物的本质
  • 对于那些需要长期进行维护的代码,进行模型重构通常是值得的
  • 当前痛苦,日后轻松

在DDD的过程中,模型重构是必须的,只有这样我们才能真正地开发出真正能解决问题的、有长久生命力的软件系统

8、DDD实践之值对象设计

值对象的职责:主要负责承载特定信息,可以在限界上下文内部或多个限界上下文之间“传递”,类似一个“邮差”……

利用值对象传递信息

  • **“复制”:**在复制过程中可以修改值对象的属性,或者提取某个值对象的信息,再转化为另一个值对象
  • **“共享”:**要求值对象是只读的

值对象 VS 值类型

  • 值对象Value Object是DDD中的概念,用于系统建模
  • 值类型Value Type是.NET中的概念,用于编码
  • 如果使用C#来开发系统,可以选择用值类型Value Type(比如Struct)来实现DDD中的值对象。当然引用类型(class或record)实现也没问题

值对象的典型例子

为什么要将数据封装为值类型?

  • 以简单的int类型为基础,构建出一个值类型为Temperature,其中可以封装多种有价值的信息和功能

  • 编程中实现值对象(C#实现)

  • 值对象的判等规则:内向等则对象等

开发建议:

  • 开发中,能建模为**值对象(优先)**就不要建模为实体,因为值对象更为轻量级,无需持久化,易处理
  • DDD推荐在值对象中封装相应的业务规则,比如将某实体ID构建的值对象中封装ID的检测规则(正则表达式)等
  • 同一事物,在一个系统中可能是实体,在另一个系统中可能就成为了值对象。需要按照具体情况分析

9、DDD实践之实体对象设计

DDD实体Entities对象的特点

  • 唯一的标识
  • 包容业务数据
  • 封装了业务规则
  • 不包容持久化代码

如何为实体建模?

  • 符合什么条件才算是相同的事物?——确定实体的标识
  • 对于此事物来说,什么东西是我们所关注的?——确定实体类的成员
  • 对于此事物来说,哪些数据有意义?——确定实体属性数据的有效规则
  • 此事物怎样创建、修改、保存和销毁?——实体状态跟踪机制、持久化机制

实体标识的确认方法

  • 实体标识应该保证在多个计算机系统中的唯一性
  • 实体标识应该由领域模型来决定。在具体的编程实现时,可依据底层技术框架的特点,附加一些额外的标识,比如数据库表自增字段的值或GUID,但要小心使用,不要带来混乱
  • 实体标识是只读的,一旦生成,就不可更改

实体标识的生成策略

  • 使用数据库自动生成的值,然后插入数据后取回此值作为实体ID。某些ORM(如EFCore)集成了该功能

  • 为实体标识设计一个专用的值对象

实体标识生成策略比对

  • GUID:过于通用,不方便
  • 使用业务领域中确定的标识(例如身份证号),DDD推荐但开发中使用不方便——往往与底层数据存取技术使用的标识不一致
  • 折中:DDD确定的实体标识(用于业务逻辑)+底层数据库自动生成的标识(方便编码实现)

实体判等原则

  • 引用等则对象等
  • 标识等则对象等

实体的状态:指特定时刻实体对象各属性值的集合。

  • 通常情况下,实体状态会保存到数据存储中,在需要时再从数据存储中读取值,重建实体。负责完成实体持久化工作的通常是一个Repository

实体 VS 值对象

  • 避免在一个实体中包含太多的业务逻辑
  • 避免实体中的代码访问外部资源(访问网络或数据库)
  • 避免实体依赖外部特定组件(比如基础设施层中的组件)

10、DDD实践之聚合对象基础

大规模软件系统通常导致复杂的数据模型,使用关系型数据库存储数据易形成成百上千张表……

为了方便软件系统的建模,我们可以对整体问题域进行切分“Aggregate聚合对象”是一种切分手段。类似中国有960万平方公里的国土,划分为“省”、“自治区/市/县”……几级进行管理

“Aggregate聚合对象”:可以看成一组相关对象(实体和值对象)的集合,作为一个整体对待

  • 每个“Aggregate聚合对象”都有一个“根Root”和“边界boundary”。边界定义了聚合对象内部有什么,根是外界访问的唯一入口

  • 聚合对象之间的相互访问:除了可以使用从“根”遍历来查找对象这种方法外,禁止用其它方法对聚合对象内部的任何对象进行访问

什么时候“聚合对象”?需要根据业务领域确定

  • 这几个对象经常一起出现
  • 从业务角度而言,这两个对象具备密切的联系

聚合对象的优点

  • 简化对象间的交互

  • 对多个对象进行统一管理:“聚合根Aggregate Root”的职责

  • 简化聚合对象内部对象间关系:聚合对象内部对象一般单向关联,不允许循环关联和双向关联

聚合对象的四个职责:组合对象、隐藏信息、封装业务规则、维护一致性边界

聚合对象的CRUD

注意保证聚合对象数据的有效性和完整性

11、DDD实践之聚合设计指南

聚合根标识接口:应该有一个比较简便的方法,将聚合根对象与普通的实体对象区分开来——设计一个专用的标记接口

**聚合追求简洁:**设计聚合对象时,应该尽量降低其复杂性

仔细考虑需要聚合什么?精心挑选被聚合的对象

合理的选择聚合的标识

合适的聚合对象创建方式

了解什么是Repository?

**EF与“贫血模型Anemic Models”:**Entry Framework生成的数据实体。指那些只包容属性(即get/set方法)的数据对象,不包容任何业务代码,仅作为“数据 容器”除了数据外,它什么也没有

验证聚合对象中属性数据:将数据访问规则写在对象对象内部,专供外部调用

继承runtimeexception编写业务异常类检测单个数据有效性

封装专门方法或类或注解检测多个属性有效性

12、DDD实践之领域服务和应用服务

服务的概述:将实体、值对象处理并产生结果的加工过程

领域服务与特定的业务逻辑相关

  • 在系统分析时,通常使用自然语言描述需要关注的业务流程或业务规则。这些业务流程或业务规则在软件系统中的具体实现就是领域逻辑
  • 业务逻辑的实现:仅涉及单个实体或聚合对象的领域逻辑,通常直接集成到实体或聚合对象中;对那些不适合放到单个聚合对象或实体,涉及多个聚合对象或实体的领域业务逻辑,应单独实现为领域服务domain service
  • 领域服务负责管理与协调Aggregate和Repository之间的关系,操控多个对象实现特定业务逻辑
  • 领域服务的出现源自真实的业务需要,通常经过领域专家的认可
  • 实现领域服务的类名是“通用语言ubiquitous language”的组成部分,是通用语言术语表中的一个

分层架构中的领域层

领域服务的特点

领域服务的实例1

领域服务的实例2

什么是应用服务?

应用服务的典型用法

13、DDD实践之领域事件

回顾一下事件的相关概念

典型开发场景

直观应对方法:编码链式调用逻辑

领域事件驱动的解决方案

领域事件的典型开发模式

区分用户界面事件与领域事件

对领域事件的描述

领域事件设计指南

处理重复事件的问题

  • 对事件的处理尽量是“幂等”的,以解决重复事件的问题

处理跨越限界上下文的事件

QA

14、领域驱动的分层架构设计

物理上的层和逻辑上的层

典型的三层架构

DDD分层架构设计要点

DDD对经典三层架构的调整和修改

DDD分层架构之间的调用关系

注意分层结构项目代码的两种组织方式:DDD中建议按照功能组织代码

DDD分层架构中各层的职责

15、DDD手机开发实践:Google标准App架构实战

在实际开发中如何应用Android App标准架构

……

16、从DDD到Clean Architecture

几种常见的软件系统架构

整洁架构的其它名称

整洁架构是DDD在特定领域的演化

分层视角下的Clean Architecture

与整洁架构相关的面向对象原则

整洁架构的实现

Google标准架构与整洁架构

《移动互联系统分析与设计》结课设计要求

一、基本要求

1、自由组队,完成一个功能完整的移动互联系统

2、系统至少包含两个组成部分

(1)Server端(后端应用):所使用的开发平台和技术不限,Java/.NET/Node.js……

(2)Client端(前端应用):与后端相配套的前端应用,以下任选一种

  • 手机app:NativeApp/HybridApp
  • Web前端应用:基于Vue、React、Angular等运行于浏览器端
  • 桌面客户端应用:开发技术不限,WPF、PWA、Electron、QT……

3、提交内容

  • 可正常编译的代码
  • 可直接运行的可执行程序
  • 项目电子版文档

二、文档要求

1、系统需求文档:原因→问题领域→具体需求和方法→同类产品对比

2、系统功能文档:

17、Clean Architecture的Android实现

……