1.概要
DDD(Domain-driven design,模型驱动设计)是一种软件设计的指导思想,而非固定的一套公式化开发模板(这样就会导致网络上出现各种基于自己或业务上的理解而产出的DDD落地的实现,会让很想学习的开发者迷茫)。在项目的全生命周期内,所有岗位的人员都基于对业务的相同的理解来展开工作。所有人员站在用户的角度、业务的角度区思考问题,而不是从一开始就站在技术的角度去思考。
在项目初期,需要将领域专家引入到团队中。那什么是领域专家呢?他应该是谁?领域专家是指对业务领域非常熟悉(或在该业务领域耕耘了很多年)。他可以是团队中的任何人,项目经理、产品经理、架构师等等都可以是领域专家,在需求分析阶段大家都需要对需求整体进行评估。团队需要思考这几个问题,是否需要使用DDD?现有的团队人员是否能支撑起DDD?任何技术或者设计都需要整体的评估,而不是一味地去迎合。
接下来让我们看看什么是领域驱动设计。
1.1需求分析
需求分析的方式有很多,例如:用例法,四色建模法。其目的就是为了去建立领域模型的认知。以上两种方法可能会有些抽象,在项目前期与客户沟通过程属于需求分析层面该如何去做。就需要一种统一的一种规则,需要统一语言。接下来就是介绍Domain Story Telling这个概念(这个概念是由domainstorytelling.org网站提出)。包含下图的四个部分。
由项目经理或产品经理通过这四种概念对需求进行梳理,通过这种方式可以很清楚直白的表达出业务场景。那么这四个部分各代表什么含义呢?
Pictograms and Vocabulary(象形图和词汇)
1.Actors(角色),(故事场景的参与者,故事指的是功能点)。
2.WorkObjects(参与者与参与者之间传递的内容,或需要呈现的内容称为工作对象)。
3.Acivities(代表的Actors和WorkObjects之间关系,用线的方式来表示)。
4.Annotations(所有流程的注解)。
基于以上概念画出来的图大概是这样,图中大致描述的是电商购物的一个流程分析。
以商城购物系统为例。
(1)用户查看商品,向购物网站发送商品列表请求信息。
(2)购物网站响应商品信息给客户。
(3)用户选择商品、购买,发送购买商品请求
(4)购物网站响应订单信息给客户。此时订单是未支付的。
(4.1)用户拿到订单信息之后,开始支付。发送支付请求。
(5)购物网站根据用户提供的支付信息,去(银行系统)对应的银行账户中进行扣款。
(5.1)扣款成功之后,再将扣款成功的订单信息返回给购物网站。
(6)购物网站拿到扣款成功的订单信息之后,开始通知仓库管理员准备发货。(6.1)并且通知用户,扣款成功购物网站准备发货了。
(7)仓库管理员拿到货物之后打包发送给快递员。
(8)快递员送货上门给客户。
基于以上的分析,大家可以清楚的了解到商城购物的整个环节。
1.2领域分析
领域(Domain ) :一个组织做的事情(举个比较狭隘的例子,阿里这个组织做的事情就是做电商)。
子域
核心域:解决项目核心问题,和组织业务紧密关联。
支撑域:解决项目的非核心问题,则具有组织特性,但不具有通用性。
通用域:通用特性,没有组织特性。
关心的业务重点不同,领域的划分也不同。
项目初期应考虑的是如何领域建模,用业务语言去描述和构建系统。而不是用技术语言,去思考代码怎么写。技术是服务于业务的脱离业务谈技术或者架构、设计都是空谈。
(1)商品浏览核心域
(2)选择购买商品、支付订单
(3)订单支付,银行交互
(4)发货、仓库管理
(5)配送
目前我们暂时先把购物到配送整个过程划分为五个子域,为什么不是四部分?为什么不是三部分?这个没有明确的标准取决于分析者的当前对业务的理解,站在现有的角度去分析领域边界。并不是说谁做就会更好,符合当前场景下的分析即可。那么划分领域的原则是什么呢?原则是业务并不是功能,围绕的是业务的走向划分而不是以业务的功能点划分。
有了领域边界之后,就开始绘制领域的边界图。那么又会引出新的问题,边界和边界之间存在什么样的关系?是如何交互的?
站在系统的角度从商品的查询到最终的发货是一个整体的流程。
(1)查询商品的边界和订单的数据做交互(2)订单需要跟支付、仓库做交互(3)仓库跟物流送货做交互那么为了让领域的内聚性更强,会需要去对领域做一个保护。保护手段有三种:防腐层(ACL)、开放主机服务(OHS)、发布语言(PL)。
防腐层(ACL)
通过适配器,桥接模式、外观模式对于访问操作的一种保护。查询商品和订购商品之间的交互是通过一个接口来实现的。定义这个接口是为了不影响别人是需要做一个处理,这个接口不会因为你的领域类的变化而影响接口的定义,我就不会去关心你的内部实现了只考虑在接口层面怎么去交互。接口的表现形式有很多种,在项目中可以是一个接口,在多个项目中可以是一个协议。例如reset api也是一种接口的实现,OSH/OL相当于就是做两个根本不在一起的服务进行交互的一种方式。ACL可以理解为它是一种接口(Interface)层面的定义,D和U就是Down和UP用于区分上下级的关系。以查询商品和订购商品(订单)举例,如果需要做防腐层应该在哪个边界中去做呢?答案是在查询商品,因为不管你商品是什么样的,对于订单来说查询商品属于低层。而对于PL(订购支付)来说就不是了,订购支付属于高层所以订购商品需要主动去做防腐处理。
1.3 Domain Design
(领域建模工具,推荐使用:StarUML,或微软的Viso)
BoundedContext(界限上下文)https://thedomaindrivendesign.io/bounded-context/
Aggregate(聚合根)
聚合根相当于领域中的一个大对象,它其实是由多个Entities和多个Value Obiect组成。它是操作具体业务流程中的关键点,它会由创建它的方法比如说Factories当然也可以去new。
Entities
Entities是有id的唯一标识的,有状态的对象。比如说商城中订单就是这样一个概念。
Value Obiect
是一个无状态的值对象,订单中包含的那些数据可以看作成值对象,例如收货信息address无论这些字段的数值如何改变都不会影响订单的状态而发生改变,只会影响数据不会影响状态。
Services
在Entities满足不了需要的情况下,它操作的都是无状态的数据对象和逻辑。需要先考虑这两条规则。例如转账功能,有AB两个账户,AB都有一个Entities对象,那么谁转账给谁呢?如果在Entities做一个转账好像又不是那么合理,所以会提出一个转账的Services,需要传递两个账户的Content来完成转账的服务。不能在Entities体现这个业务,这个业务是无状态的,这个业务不会去影响Entities的状态只是去做了一个业务逻辑的处理。这种情况下是可以用Services去做的。在DDD中需要弱化Services,大家不要把领域模型花了那么大心思去分析的业务,又大部分通过Services去实现成为了一个数据驱动的开发方式上。所以在做领域设计的时候大家一定要想清楚当时定义的意思和作用。
DomainEvents
因为Entities是有生命周期的且有状态改变的,它很多业务的一些触发条件都是因为这些状态改变而触发的,所以这个领域事件可以关联很多业务的执行逻辑。例如:订单支付完成付款之后,是需要根银行进行交互的,当银行将订单数据返回给商城之后。需要触发两个事件,第一个通知用户,第二个通知仓库管理员。这两件事情的完成都是需要基于支付完订单之后通知给下游的两个领域。所以我们就需要知道领域事件可以作为领域边界触发的一种行为,也可以立即为是解耦的一种方式。如果用领域事件的方式去实现业务那么可以拆分的更细。DomainEvents结合事务的一个分享。分布式事务里的事务消息问题,我们是把一个业务划分为主次的,这个业务关系是相对的。以银行扣款支付成功之后触发两条业务线为例子,什么是主呢?银行的消息提醒给你已经支付成功了这个业务,这是一个主业务,那么它的延申业务有两个(1)通知仓库管理员去发货(2)通知用户支付成功。在实现业务的时,接收银行返回支付状态改变的时候去触发两个事件给延申业务。站在事务的角度上来说,需要满足主线的业务事务提交两个延申的业务,提交之后就不做管理了成功就成功失败就失败,主线就是主线延申就是延申。本地事务提交了才会发送事务消息。事务消息发送完成之后,由两个订阅者去完成各自的业务逻辑,如果失败了也可以通过事务消息来做重复的事务补偿。
Factories
是为了创建Entities和Aggregate的对象,Aggregate实际上也是一个Entities对象,Aggregate只是一个概念实际对应的还是一个对象。具体的实现是Entities、Value Obiect、Services、DomainEvents、Factories、Repositories。而BoundedContext、Aggregate是抽象的概念。
Repositories
代表的是数据操作的资源和方法。比如说:订单信息最终还是要落库的,那么就会涉及到数据库操作那么就需要一个数据库操作的对象。让领域不关心数据的操作。
1.4UML(统一语言)
(看字面意思就能懂的直接略过...)
组合:两个类的对象生命周期是关联的。
聚合:其他业务也可能包含同样的对象。
1.5 实践
COLA(COLA是阿里巴巴基于领域模型实现的一套服务端的框架)
1.6 Specification introduction(规范介绍)
1.7GIT Manager(Git代码库版本管理)
1.8Test(测试环节)
单元测试:主要测试的是业务领域模型的逻辑。为什么要测试它呢?因为在做的时候我们的领域是不需要数据的,只要测试通过表示系统的关键业务没有出问题。
性能测试:根据在设计系统的时候,需要设置指标比如系统支撑的最大上限是多少,检测是否满足性能指标。性能测试的前提是在一定的资源下去做的,如果不断的增加服务器数量也是能达到性能指标的。
功能测试:大部分项目中的常见的测试,通过测试用例来验证功能是否可用。还有集成测试和回归测试。
2.Ref
https://domainstorytelling.org/#dst-ddd
https://www.wps.de/modeler/index.html