ddd实战课笔记

DDD定义

  • DDD核心思想是通过领域驱动设计方法定义领域模型,从而确定业务和应用边界,保证业务模型与代码模型的一致性。

DDD组成

  • DDD是一种处理高度复杂领域的设计思想,它试图分离技术实现的复杂性,并围绕业务概念构建领域模型来控制业务的复杂性,以解决软件难以理解,难以演进的问题。DDD不是架构,而是一种架构设计方法论,它通过边界划分将复杂业务领域简单化,帮我们设计出清晰的领域和应用边界,可以很容易地实现架构演进。

DDD包括战略设计和战术设计两部分。

战略设计主要从业务视角出发,建立业务领域模型,划分领域边界,建立通用语言的限界上下文,限界上下文可以作为微服务设计的参考边界。

战术设计则从技术视角出发,侧重于领域模型的技术实现,完成软件开发和落地,包括:聚合根、实体、值对象、领域服务、应用服务和资源库等代码逻辑的设计和实现。

战略设计

  • DDD战略设计会建立领域模型,领域模型可以用于指导微服务的设计和拆分。事件风暴是建立领域模型的主要方法,它是一个从发散到收敛的过程。它通常采用用例分析、场景分析和用户旅程分析,尽可能全面不遗漏地分解业务领域,并梳理领域对象之间的关系,这是一个发散的过程。事件风暴过程会产生很多的实体、命令、事件等领域对象,我们将这些领域对象从不同的维度进行聚类,形成如聚合、限界上下文等边界,建立领域模型,这就是一个收敛的过程。

我们可以用三步来划定领域模型和微服务的边界。

第一步:在事件风暴中梳理业务过程中的用户操作、事件以及外部依赖关系等,根据这些要素梳理出领域实体等领域对象。

第二步:根据领域实体之间的业务关联性,将业务紧密相关的实体进行组合形成聚合,同时确定聚合中的聚合根、值对象和实体。聚合之间的边界是逻辑边界,它们在同一个微服务实例中运行。

第三步:根据业务及语义边界等因素,将一个或者多个聚合划定在一个限界上下文内,形成领域模型。不同限界上下文内的领域逻辑被隔离在不同的微服务实例中运行,物理上相互隔离,所以是物理边界。

领域划分

  • 既然领域是用来限定业务边界和范围的,那么就会有大小之分,领域越大,业务范围就越大,反之则相反。领域可以进一步划分为子领域。我们把划分出来的多个子领域称为子域,每个子域对应一个更小的问题域或更小的业务范围。
  • 在领域不断划分的过程中,领域会细分为不同的子域,子域可以根据自身重要性和功能属性划分为三类子域,它们分别是:核心域、通用域和支撑域。

决定产品和公司核心竞争力的子域是核心域,它是业务成功的主要因素和公司的核心竞争力。没有太多个性化的诉求,同时被多个子域使用的通用功能子域是通用域。还有一种功能子域是必需的,但既不包含决定产品和公司核心竞争力的功能,也不包含通用功能的子域,它就是支撑域。

这三类子域相较之下,核心域是最重要的。通用域和支撑域如果对应到企业系统,举例来说的话,通用域则是你需要用到的通用系统,比如认证、权限等等,这类应用很容易买到,没有企业特点限制,不需要做太多的定制化。而支撑域则具有企业特性,但不具有通用性,例如数据代码类的数据字典等系统。

通用语言和限界上下文

  • 通用语言定义上下文含义,限界上下文则定义领域边界,以确保每个上下文含义在它特定的边界内都具有唯一的含义,领域模型则存在于这个边界之内。

怎么理解通用语言这个概念呢?在事件风暴过程中,通过团队交流达成共识的,能够简单、清晰、准确描述业务涵义和规则的语言就是通用语言。

限界上下文的定义就是:用来封装通用语言和领域对象,提供上下文环境,保证在领域之内的一些术语、业务相关对象等(通用语言)有一个确切的含义,没有二义性。这个边界定义了模型的适用范围,使团队所有成员能够明确地知道什么应该在模型中实现,什么不应该在模型中实现。

限界上下文是微服务设计和拆分的主要依据。在领域模型中,如果不考虑技术异构、团队沟通等其它外部因素,一个限界上下文理论上就可以设计为一个微服务。

  • 设计过程中我们可以用一些表格,来记录事件风暴和微服务设计过程中产生的领域对象及其属。比如,领域对象在DDD分层架构中的位置、属性、依赖关系以及与代码模型对象的映射关系等。

实体

  • 在DDD中有这样一类对象,它们拥有唯一标识符,且标识符在历经各种状态变更后仍能保持一致。对这些对象而言,重要的不是其属性,而是其延续性和标识,对象的延续性和标识会跨越甚至超出软件的生命周期。我们把这样的对象称为实体。
  • 与传统数据模型设计优先不同,DDD是先构建领域模型,针对实际业务场景构建实体对象和行为,再将实体对象映射到数据持久化对象。
  • 在领域模型映射到数据模型时,一个实体可能对应0个、1个或者多个数据库持久化对象。大多数情况下实体与持久化对象是一对一。在某些场景中,有些实体只是暂驻静态内存的一个运行态实体,它不需要持久化。比如,基于多个价格配置数据计算后生成的折扣实体。而在有些复杂场景下,实体与持久化对象则可能是一对多或者多对一的关系。

值对象

  • 值对象本质上就是一个集。那这个集合里面有什么呢?若干个用于描述目的、具有整体概念和不可修改的属性。那这个集合存在的意义又是什么?在领域建模的过程中,值对象可以保证属性归类的清晰和概念的完整性,避免属性零碎。

聚合和聚合根

  • 在事件风暴中,我们会根据一些业务操作和行为找出实体或值对象,进而将业务关联紧密的实体和值对象进行组合,构成聚合,再根据业务语义将多个聚合划定到同一个限界上下文中,并在限界上下文内完成领域建模。
  • 领域模型内的实体和值对象就好比个体,而能让实体和值对象协同工作的组织就是聚合,它用来确保这些领域对象在实现共同的业务逻辑时,能保证数据的一致性。聚合的特点:高内聚、低耦合,它是领域模型中最底层的边界,可以作为拆分微服务的最小单位,但我不建议你对微服务过度拆分。
  • 如果把聚合比作组织,那聚合根就是这个组织的负责人。聚合根也称为根实体,它不仅是实体,还是聚合的管理者。

聚合根是聚合对外的接口人,以聚合根ID关联的方式接受外部任务和请求,在上下文内实现聚合之间的业务协同。也就是说,聚合之间通过聚合根ID关联引用,如果需要访问其它聚合的实体,就要先访问聚合根,再导航到聚合内部实体,外部对象不能直接访问聚合内实体。

  • 聚合根的特点:聚合根是实体,有实体的特点,具有全局唯一标识,有独立的生命周期。一个聚合只有一个聚合根,聚合根在聚合内对实体和值对象采用直接对象引用的方式进行组织和协调,聚合根与聚合根之间通过ID关联的方式实现聚合之间的协同。

实体的特点:有ID标识,通过ID判断相等性,ID在聚合内唯一即可。状态可变,它依附于聚合根,其生命周期由聚合根管理。实体一般会持久化,但与数据库持久化对象不一定是一对一的关系。实体可以引用聚合内的聚合根、实体和值对象。

值对象的特点:无ID,不可变,无生命周期,用完即扔。值对象之间通过属性值判断相等性。它的核心本质是值,是一组概念完整的属性组成的集合,用于描述实体的状态和特征。值对象尽量只引用值对象。

领域事件

  • 领域事件驱动设计可以切断领域模型之间的强依赖关系,事件发布完成后,发布方不必关心后续订阅方事件处理是否成功,这样可以实现领域模型的解耦,维护领域模型的独立性和数据的一致性。在领域模型映射到微服务系统架构时,领域事件可以解耦微服务,微服务之间的数据不必要求强一致性,而是基于事件的最终一致性。
  • 领域事件处理包括:事件构建和发布、事件数据持久化、消息中间件、事件接收和处理等。
  • 事件基本属性至少包括:事件唯一标识、发生时间、事件类型和事件源,其中事件唯一标识应该是全局唯一的,以便事件能够无歧义地在多个限界上下文中传递。事件基本属性主要记录事件自身以及事件发生背景的数据。

另外事件中还有一项更重要,那就是业务属性,用于记录事件发生那一刻的业务数据,这些数据会随事件传输到订阅方,以开展下一步的业务操作。

事件基本属性和业务属性一起构成事件实体,事件实体依赖聚合根。领域事件发生后,事件中的业务数据不再修改,因此业务数据可以以序列化值对象的形式保存,这种存储格式在消息中间件中也比较容易解析和获取。

事件发布之前需要先构建事件实体并持久化。

中台

  • 业务中台的建设可采用领域驱动设计方法,通过领域建模,将可复用的公共能力从各个单体剥离,沉淀并组合,采用微服务架构模式,建设成为可共享的通用能力中台。
  • 数据中台的主要目标是打通数据孤岛,实现业务融合和创新,包括三大主要职能:

一是完成企业全域数据的采集与存储,实现各不同业务类别中台数据的汇总和集中管理。

二是按照标准的数据规范或数据模型,将数据按照不同主题域或场景进行加工和处理,形成面向不同主题和场景的数据应用,比如客户视图、代理人视图、渠道视图、机构视图等不同数据体系。

三是建立业务需求驱动的数据体系,基于各个维度的数据,深度萃取数据价值,支持业务和商业模式的创新。

相应的,数据中台的建设就可分为三步走:

第一步实现各中台业务数据的汇集,解决数据孤岛和初级数据共享问题。

第二步实现企业级实时或非实时全维度数据的深度融合、加工和共享。

第三步萃取数据价值,支持业务创新,加速从数据转换为业务价值的过程。

数据中台不仅限于分析型场景,也适用于交易型场景。它可以建立在数据仓库或数据平台之上,将数据服务化之后提供给业务系统。基于数据库日志捕获的技术,使数据的时效性大大提升,这样就可以为交易型场景提供很好的支撑。

综上,数据中台主要完成数据的融合和加工,萃取数据业务价值,支持业务创新,对外提供数据共享服务。

DDD、中台和微服务的协作

  • 中台在企业架构上更多偏向业务模型,形成中台的过程实际上也是业务领域不断细分的过程。在这个过程中我们会将同类通用的业务能力进行聚合和业务重构,再根据限界上下文和业务内聚的原则建立领域模型。而DDD的战略设计最擅长的就是领域建模。

那在中台完成领域建模后,我们就需要通过微服务来完成系统建设。此时,DDD的战术设计又恰好可以与微服务的设计完美结合。可以说,中台和微服务正是DDD实战的最佳场景。

中台建模

  • 中台如何建模?

中台业务抽象的过程就是业务建模的过程,对应DDD的战略设计。系统抽象的过程就是微服务的建设过程,对应DDD的战术设计。下面我们就结合DDD领域建模的方法,讲一下中台业务建模的过程。

第一步:按照业务流程(通常适用于核心域)或者功能属性、集合(通常适用于通用域或支撑域),将业务域细分为多个中台,再根据功能属性或重要性归类到核心中台或通用中台。核心中台设计时要考虑核心竞争力,通用中台要站在企业高度考虑共享和复用能力。

第二步:选取中台,根据用例、业务场景或用户旅程完成事件风暴,找出实体、聚合和限界上下文。依次进行领域分解,建立领域模型。

由于不同中台独立建模,某些领域对象或功能可能会重复出现在其它领域模型中,也有可能本该是同一个聚合的领域对象或功能,却分散在其它的中台里,这样会导致领域模型不完整或者业务不内聚。这里先不要着急,这一步我们只需要初步确定主领域模型就可以了,在第三步中我们还会提炼并重组这些领域对象。

第三步:以主领域模型为基础,扫描其它中台领域模型,检查并确定是否存在重复或者需要重组的领域对象、功能,提炼并重构主领域模型,完成最终的领域模型设计。

第四步:选择其它主领域模型重复第三步,直到所有主领域模型完成比对和重构。

第五步:基于领域模型完成微服务设计,完成系统落地。

DDD战略设计包括上述的第一步到第四步,主要为:业务域分解为中台,对中台归类,完成领域建模,建立中台业务模型。DDD战术设计是第五步,领域模型映射为微服务,完成中台建设。

DDD的战略设计可用于中台业务建模,战术设计可指导中台微服务设计。

构建中台业务模型

  • 我们可以用DDD领域建模的方法来构建中台业务模型。你可以选择两种建模策略:自顶向下和自底向上的策略。具体采用哪种策略,你需要结合公司的具体情况来分析,下面我就来介绍一下这两种策略。
  • 自顶向下的策略

第一种策略是自顶向下。这种策略是先做顶层设计,从最高领域逐级分解为中台,分别建立领域模型,根据业务属性分为通用中台或核心中台。领域建模过程主要基于业务现状,暂时不考虑系统现状。自顶向下的策略适用于全新的应用系统建设,或旧系统推倒重建的情况。

由于这种策略不必受限于现有系统,你可以用DDD领域逐级分解的领域建模方法。它的主要步骤:第一步是将领域分解为子域,子域可以分为核心域、通用域和支撑域;第二步是对子域建模,划分领域边界,建立领域模型和限界上下文;第三步则是根据限界上下文进行微服务设计。

  • 自底向上的策略

第二种策略是自底向上。这种策略是基于业务和系统现状完成领域建模。首先分别完成系统所在业务域的领域建模;然后对齐业务域,找出具有同类或相似业务功能的领域模型,对比分析领域模型的差异,重组领域对象,重构领域模型。这个过程会沉淀公共和复用的业务能力,会将分散的业务模型整合。自底向上策略适用于遗留系统业务模型的演进式重构。

事件风暴

  • 事件风暴是一项团队活动,领域专家与项目团队通过头脑风暴的形式,罗列出领域中所有的领域事件,整合之后形成最终的领域事件集合,然后对每一个事件,标注出导致该事件的命令,再为每一个事件标注出命令发起方的角色。命令可以是用户发起,也可以是第三方系统调用或者定时器触发等,最后对事件进行分类,整理出实体、聚合、聚合根以及限界上下文。而事件风暴正是DDD战略设计中经常使用的一种方法,它可以快速分析和分解复杂的业务领域,完成领域建模。
  • 事件风暴参与者会将自己的想法和意见写在即时贴上
  • 在领域建模的过程中,我们需要重点关注这类业务的语言和行为。比如某些业务动作或行为(事件)是否会触发下一个业务动作,这个动作(事件)的输入和输出是什么?是谁(实体)发出的什么动作(命令),触发了这个动作(事件)…我们可以从这些暗藏的词汇中,分析出领域模型中的事件、命令和实体等领域对象。
  • 领域建模的过程主要包括产品愿景、业务场景分析、领域建模和微服务拆分与设计这几个重要阶段。

1. 产品愿景

产品愿景的主要目的是对产品顶层价值的设计,使产品目标用户、核心价值、差异化竞争点等信息达成一致,避免产品偏离方向。

2. 业务场景分析

场景分析是从用户视角出发的,根据业务流程或用户旅程,采用用例和场景分析,探索领域中的典型场景,找出领域事件、实体和命令等领域对象,支撑领域建模。事件风暴参与者要尽可能地遍历所有业务细节,充分发表意见,不要遗漏业务要点。

3. 领域建模

领域建模时,我们会根据场景分析过程中产生的领域对象,比如命令、事件等之间关系,找出产生命令的实体,分析实体之间的依赖关系组成聚合,为聚合划定限界上下文,建立领域模型以及模型之间的依赖。领域模型利用限界上下文向上可以指导微服务设计,通过聚合向下可以指导聚合根、实体和值对象的设计。

4. 微服务拆分与设计

原则上一个领域模型就可以设计为一个微服务,但由于领域建模时只考虑了业务因素,没有考虑微服务落地时的技术、团队以及运行环境等非业务因素,因此在微服务拆分与设计时,我们不能简单地将领域模型作为拆分微服务的唯一标准,它只能作为微服务拆分的一个重要依据。

BFF与企业级业务编排和协同

  • 企业级业务流程往往是多个微服务一起协作完成的,每个单一职责的微服务就像积木块,它们只完成自己特定的功能。那如何组织这些微服务,完成企业级业务编排和协同呢?

你可以在微服务和前端应用之间,增加一层BFF微服务(Backend for Frontends)。BFF主要职责是处理微服务之间的服务组合和编排。

总结

  • DDD战略设计从事件风暴开始,然后我们要找出实体等领域对象,找出聚合根构建聚合,划分限界上下文,建立领域模型。
  • 战术设计从事件风暴的命令开始,识别和设计服务,建立各层服务的依赖关系,设计微服务内的实体和值对象,找出微服务中所有的领域对象,并建立领域对象与代码对象的映射关系。
我来评几句
登录后评论

已发表评论数()

相关站点

热门文章