DDD学习笔记 - 基础篇(Ⅰ)
领域驱动设计DDD
开篇词
作为中台,需要将通用的可复用的业务能力沉淀到中台业务模型,实现企业级能力复用。
早在2003年就诞生的DDD,怎么来指导迟到近20年才大热的微服务设计呢?
1.搞清楚DDD的核心设计思想,DDD、微服务和中台的关系:
中台本质是业务模型,微服务是业务模型的系统落地方案,DDD是一种设计思想,它可以同时指导中台业务建模和微服务设计,他们之间是铁三角关系。
DDD强调领域模型和微服务设计的一体性,先有领域模型然后才有微服务。
2.战略设计。建立领域模型、划分为服务边界。关键
3.战术设计。从领域模型转向微服务设计和落地。
术语多且陌生。
DDD必知必会十大概念、微服务架构分层、代码模型设计、DDD战略设计、事件风暴、领域建模、企业级中台业务建模。走一遍DDD战略设计和战术设计的全流程,DDD在领域模型和微服务设计过程中的技术要点。深化微服务设计原则、建立演进式微服务架构。
基础篇:领域、子域、核心域、通用域、支撑域、限界上下文、实体、值对象、聚合、聚合根。
进阶篇:领域事件、DDD分层架构、常见的微服务架构模型、中台设计思想
- 如何通过领域事件实现微服务解耦
- 怎样进行微服务分层设计
- 如何实现层与层之间的服务协作
- 领域模型和微服务分层的作用和价值
- 中台设计的核心思想,如何实现前中后台的协同和融合,如何利用DDD进行中台设计
实战篇:中台和领域建模的实战、微服务设计实战
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
01 | 领域驱动设计:微服务设计为什么要选择DDD?
课程链接:https://time.geekbang.org/column/article/149943
微服务设计过程中往往会面临边界如何划定的问题,微服务到底应该拆多小?
面对复杂问题,解决办法通常是拆分,模块化,化整为零。领域驱动建模DDD是面向业务,对业务领域的划分和整合,是逻辑层面。微服务是面向物理落地,是对应用的物理形态进行拆分和整合。从软件工程过程角度看,DDD的战略设计输出物,领域模型及划分的区域,是微服务的输入,一个区域对应一个微服务,微服务运行框架、平台可以承载所有的微服务,提供微服务统一的运行框架,也就是承载所有的业务领域。可见领域驱动与微服务是在软件不同阶段使用的工具,技术或方法论,围绕一个共同的目标,搭建企业业务中台,企业级业务复用,快速的需求响应能力。DDD战略设计得输出,是微服务的输入。
软件架构模式的演进:
大体来说经历了单机、集中式、到分布式微服务架构三个阶段。
单机架构:采用面向过程的设计方法。系统包括UI层和数据库两层,采用C/S架构模式,整个系统围绕数据库驱动设计和开发,总是从设计数据库和字段开始。
集中式架构:采用面向对象的设计方法,包括业务接入层、逻辑层和数据库层,是经典的三层架构,也有的采用传统的SOA架构。这种架构容易是系统变得臃肿,可扩展性和弹性伸缩性差。
分布式微服务架构:随着微服务架构理念的提出,集中式架构正式向微服务架构演进。可以很好的实现应用之间的解耦,解决单体应用扩展性和弹性伸缩能力不足的问题。
在单机和集中式架构时代,系统分析、设计和开发往往是独立、分阶段割裂进行的。提出需求、需求分析、系统设计、代码实现的流程很长,经手的人很多,容易导致信息丢失,容易导致需求、设计和代码实现的不一致。
在单机和集中式架构这两种模式下,软件无法快速响应需求和业务的迅速变化。
微服务设计和拆分的困境:
微服务的粒度应该多大?应该如何拆分和设计?边界应该在哪里?
很久以来都没有一套系统的理论和方法可以指导微服务的拆分,甚至包括微服务架构模式的提出者Martin Fowler也没有告诉我们应该如何拆分微服务。有很多项目因为前期微服务拆分过度,导致项目复杂度过高,无法上线和维护。
微服务拆分困境产生的根本原因就是不知道业务或者微服务的边界到底在哪里。确定了业务边界和应用边界,这个困境也就迎刃而解了。
领域驱动设计和微服务的前世今生。
2004年Eric Evans发表《领域驱动设计》一书,从此DDD诞生。
DDD核心思想是通过领域驱动设计方法定义领域模型,从而确定业务和应用边界,保证业务模型与代码模型的一致性。
直到Martin Fowler提出微服务架构,DDD才真正迎来了自己的时代。
可以利用DDD设计方法来建立领域模型,划分领域边界,再根据这些领域边界从业务视角划分微服务边界。按照DDD方法设计出的微服务的业务和应用边界都非常合理,可以很好的实现微服务内部和外部的“高内聚、低耦合”。
DDD是一种处理高度复杂领域的设计思想,它试图分离技术实现的复杂性,并围绕业务概念构建领域模型来控制业务的复杂性,以解决软件难以理解,难以演进的问题。DDD不是架构,而是一种架构设计方法论,它通过划分边界将复杂业务领域简单化,帮我们设计出清晰的领域和应用边界,可以很容易实现架构演进。
DDD包括战略设计和战术设计两部分:
战略设计主要从业务视角出发,建立业务领域模型,划分领域边界,建立通用语言的限界上下文,限界上下文可以作为微服务设计的参考边界。
战术设计主要从技术视角出发,侧重于领域模型的技术实现,完成软件开发和落地。包括聚合根、实体、值对象、领域服务、应用服务和资源库等代码逻辑的设计和实现。
DDD如何进行战略设计:
DDD战略设计(事件风暴) == 》建立领域模型 == 》指导微服务的设计和拆分。
事件风暴是建立领域模型的主要方法,是一个从发散到收敛的过程。通常采用用例分析、场景分析、用户旅程分析,尽可能全面不遗漏地分解业务领域,并梳理领域对象之间的关系,这是一个发散的过程。事件风暴过程会产生很多的实体、命令、事件等领域对象,将这些领域对象从不同维度进行聚类,形成聚合、限界上下文等边界,建立领域模型,这是一个收敛的过程。
划定领域模型和微服务的边界,分为三个步骤:
第一步:在事件风暴中梳理业务过程中的用户操作、时间以及外部依赖关系等,根据这些要素梳理出领域实体等领域对象。
第二步:根据领域实体之间的业务关联性,将业务紧密相关的实体进行组合形成聚合,同时确定聚合中的聚合根、值对象和实体。在这个图里,聚合之间的边界是第一层边界,他们在同一个微服务实例中运行,这个边界是逻辑边界,所以用虚线表示。
第三步:根据业务及语义边界等因素,将一个或者多个聚合划定在一个限界上下文内,形成领域模型。在这个图里,限界上下文之间的边界是第二层边界,这一层边界可能就是未来微服务的边界,不同限界上下文内的领域逻辑被隔离在不同的微服务实例中运行,物理上相互隔离,所以是物理边界,用实线表示。
有了这两层边界,微服务的设计就不难了。
至此,建立了领域模型,划定了业务领域的边界,建立了通用语言和限界上下文,确定了领域模型中各个领域对象的关系。业务端领域模型和设计基本就完成了,同时也确定了应用端的微服务边界。
在从业务模型向服务落地的过程中,也就是战略设计向战术设计的实施过程中,会将领域模型中的领域对象与代码模型中的代码对象建立映射关系,将业务架构和系统架构进行绑定。当为了响应业务变化调整业务架构和领域模型时,系统架构也会同时发生调整,同步建立新的映射关系。
DDD与微服务的关系:
DDD是一种架构设计方法,微服务是一种架构风格,从本质上讲都是为了追求高响应力,都强调从业务出发。
DDD主要关注:从业务领域视角划分领域边界,构建通用语言进行高效沟通,通过业务抽象,建立领域模型,维持业务和代码的逻辑一致性。
微服务主要关注:运行时的进程间通信、容错和故障隔离,实现去中心化数据管理和去中心化服务治理,关注微服务的独立开发、测试、构建和部署。
总结:
DDD战略设计 => 建立领域模型 => 划定领域边界 => 划定微服务拆分边界
DDD不仅可以用于微服务设计,还可以很好的应用于企业中台的设计。
DDD战术设计对设计和开发人员的要求相对较高,实现起来相对复杂。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
02 | 领域、子域、核心域、通用域和支撑域:傻傻分不清?
课程链接:https://time.geekbang.org/column/article/149945
DDD的知识体系提出了很多的名词:领域、子域、核心域、通用域、支撑域、限界上下文、聚合、聚合根、实体、对象值等等,都是关键概念。虽然在微服务设计和开发过程中不一定都用得上,但可以帮助理解DDD的核心设计思想和理念。在IT战略设计、业务建模和微服务设计中都是可以借鉴的。
如何理解领域和子域:
词典中的解释:“领域是从事一种专门活动或事业的范围、部类和部门。”、“领域具体指一种的特定的范围或区域”。
领域就是用来确定范围的,范围即边界。
DDD会按照一定的规则将业务领域进行细分,当细分到一定的成都后,会将问题范围限定在特定的边界内,在这个边界内建立领域模型,进而用代码实现该领域模型,解决响应的业务问题。DDD的领域就是这个边界内要解决的业务问题域。
领域越大,业务范围就越大。
领域可以进一步划分为子领域,叫做子域,每个子域对应一个更小的问题域或更小的业务范围。
DDD的研究方法和自然科学的研究方法类似。将问题一步一步细分,逐个深入研究,当所有子域完成研究时,就建立了全部领域的完整知识体系。
第一步:确定研究对象,即研究领域,一棵桃树。
第二步:对研究对象细分为器官,器官又分为营养器官和生殖器官。不同的器官再分。桃树的知识体系是要研究的问题域,对应DDD的领域。根、茎、叶、花、果实、种子等器官则是细分后的子域。这就是DDD将领域细分为多个子域的过程。
第三步:对器官进行细分为组织。叶子恶意细分为保护组织、营养组织和输导组织。这是DDD将子域进一步细分为多个子域。
第四步:对组织细分为细胞。细胞是最小的研究单元。细胞壁确定了单元的边界,即研究的最小边界。
在这里可以把细胞理解为DDD的聚合,细胞内的细胞核、小立体、细胞膜等可以理解为聚合里面的聚合根、实体以及值对象等,在聚合内这些实体一起写作完成特定的业务功能。这个功能类似DDD设计时,确定微服务内功能要素和边界的过程。
每一个细分的领域都会有一个知识体系,也就是DDD的领域模型。在所有子域的研究完成后,就建立了全域的知识体系,也就建立了全域的领域模型。
领域建模和微服务建设的过程和方法基本类似,其核心思想就是将问题域逐步分解,降低业务理解和系统实现的复杂度。
如何理解核心域、通用域、支撑域:
子域可以根据自身重要性和功能属性划分为三类:核心域、通用域、支撑域。
决定产品和公司核心竞争力的子域是核心域。没有太多个性化诉求,同时被多个子域使用的通用功能子域是通用域。还有一种功能子域是必需的,但既不是公司核心,也不含通用功能的子域,就是支撑域。
核心域是最重要的。通用域例如认证、权限等,支撑域具有企业特性,但不具有通用性。
为什么要划分核心域、通用域、支撑域?
什么是桃树的核心域?长在公园里,花是核心域;长在果园里,果实是核心域。不同场景下核心域不同。有时为了保证果实的营养,还会裁剪掉茎和叶(通用域和支撑域)。对不同的子域应有不同的关注度和资源投入策略,好钢用在刀刃上。
淘宝是C2C,个人卖家对个人买家,天猫、京东和苏宁是B2C,企业卖家对个人买家。苏宁是典型的传统线下转电商、京东则是直营加部分平台。商业模式不同会导致核心域划分结果不同。
总结:
领域的核心思想是将问题逐级细化,降低理解和实现的复杂度。逐步缩小微服务需要解决的问题域,构建合适的领域模型,领域模型映射成系统就是微服务。
----------------------------------------------------------------------------------------------------------------------------------
03 | 限界上下文:定义领域边界的利器
课程链接:https://time.geekbang.org/column/article/149950
DDD领域建模和系统建设过程中,领域专家、产品经理、项目经理、架构师、开发经理和测试经理都会参与。为了理解和交流的障碍,DDD中出现了“通用语言”和“限界上下文”两个重要概念。
通用语言定义上下文含义,限界上下文定义领域边界。
两个问题:
1为什么要提出限界上下文的概念?(除了解决交流障碍)
2限界上下文在微服务设计中的作用和意义
什么是通用语言:
先说通用语言:在事件风暴过程中,通过团队交流达成共识的,能够简单、清晰、准确描述业务含义和规则的语言就是通用语言
。通用语言是团队统一的语言,可以解决交流障碍的问题。
通用语言包含属于和用例场景,且能够直接反映在代码中。其中的名词可以给领域对象命名,如商品、订单等,对应实体对象;动词则表示一个动作或事件,如下单、付款等,对应领域事件。其贯穿DDD的整个设计过程。
上图描述了从事件风暴建立通用语言到领域对象设计和代码落地的完成过程:
1.事件风暴的过程中,领域专家、设计、开发一起建立领域模型,建模过程中会形成通用的业务属于和用户故事。这是团队同意语言的过程。
2.通过用户故事分析会形成一个个领域对象,并对应领域模型的业务对象,每一个业务对象和领域对象是一一映射的
3.微服务代码模型来源于领域模型,每个代码模型的代码和领域对象一一对应。
设计过程中可以用一些表格,来记录事件风暴和微服务设计过程中产生的领域对象及其属性。比如领域对象在DDD分层架构中的位置、属性、依赖关系以及与代码模型对象的映射关系等。
DDD分析和设计的每一个环节都保证限界上下文内术语的统一,在代码模型设计的时候就要建立领域对象和代码对象的一一映射,保证业务模型和代码模型的一致,实现业务语言与代码语言的统一。
什么是限界上下文:
通用语言有它的上下文环境。“限界上下文”用来确定通用语言所在的领域边界。
限界、和上下文。限界就是领域的边界,上下文则是语义环境。
限界上下文的定义:用来封装通用语言和领域对象,提供上下文环境,保证在领域内的一些术语、业务相关对象等(通用语言)有一个确切的含义,没有二义性。这个边界定义了模型的适用范围。
进一步理解限界上下文:
举个例子:
孩子问妈妈:今天应该穿几件衣服?妈妈说:能穿几件穿几件。
到底穿多还是穿少。
如果可以知道这句话的语义环境,炎炎夏日或寒冬腊月,那就能理解了。所以语言离不开它的语义环境。
而业务的通用语言就有它的业务边界,不大可能用一个简单的术语没有歧义的描述一个复杂的业务领域。限界上下文就是用来细分领域,定义通用语言所在的边界。
用一个保险领域的例子来说明:保险业务又投保单、保单、批单、赔案等专业术语,分别对应保险的不同业务流程:
- 客户投保时,记录投保信息,系统对应有投保单bean;
- 缴费完成后,投保单转为保单,系统对应有保单bean,保单bean与投保单bean关联;
- 如需要修改保单信息,保单变为批单,系统对应有批单bean,批单bean与保单bean关联;
- 如果发生理赔,生成赔案,系统对应有赔案bean,赔案bean与保单bean或批单备案关联。
投保单、保单、批单、赔案等,虽然都有保单有关,但不能将保单这个术语作用在保险全业务领域,因为术语有它的边界。
正如电商领域的商品,在销售阶段是商品,在运输阶段则变成了货物。同样的东西,因为业务领域不同,赋予了这些术语不同的涵义和指责边界,这个边界可能就会成为未来微服务设计的边界。领域边界就是通过限界上下文来定义的。
限界上下文和微服务的关系:
车险承保的流程包含了投保、缴费、出单等流程,如果出险还会有报案、查勘、定损、理算等。
用一个简单的保险模型来说明下届上下文和微服务的关系:
首先,保险领域被拆分为投保、支付、保单管理、理赔四个子域。
子域还可以根据需要进一步拆分为子子域,到一定程度后,有些子子域的领域边界可能就变成限界上下文的边界了。子域可能包含多个限界上下文,如理赔包含报案、查看、定损等(限界上下文和理赔的子子域领域边界重合),也有可能子域本身的边界就是限界上下文边界,如投保子域。
理论上限界上下文就是微服务的边界。将限界上下文内的领域模型映射到微服务,就完成了从问题域到软件的解决方案。
限界上下文是微服务拆分的主要依据,如果不考虑其他外部因素,领域模型中一个限界上下文就是可以设计为一个微服务。
当然,这只是理论,微服务的拆分有很多限制因素,设计中不宜过度拆分。