DDD架构设计方法

DDD

2003年埃里克-埃文斯出版《领域驱动设计》,标志DDD诞生。直到Martin Fowler提出微服务架构后,DDD才真正落地。DDD是架构设计方法论,微服务是架构风格。
DDD的核心思想是先从业务领域入手,划分业务领域边界。接着采用事件风暴方法,分析并提取业务场景中的实体、值对象、聚合根、聚合、领域事件等领域对象。再根据限界上下文边界构建领域模型,将领域模型作为微服务设计的输入,进而完成微服务详细设计。在微服务落地时,建立业务模型与微服务代码模型的映射关系,从而保证业务架构与微服务系统架构的一致。

微服务与DDD

微服务与DDD的共生关系主要从两个方面体现。一方面是微服务提倡将应用进行服务化拆分,通过业务领域边界实现应用服务边界的划分。另一方面,DDD恰好提供基于业务限界上下文边界来实现微服务高内聚,低耦合的服务构建方法。
DDD是划分业务领域边界的方法,以帮助完成应用的拆分和微服务的设计。按照流程或功能边界分解业务领域,根据业务上下文边界,构建领域模型。
DDD通过业务边界划分将复杂业务领域简单化,从而划分出清晰的业务领域和应用边界,从而实现微服务的架构演进。DDD使得微服务的拆分和设计不再困难。

战略设计和战术设计

战略设计,是从业务视角出发,划分业务的领域边界,建立基于通用语言和业务上下文语意边界的限界上下文,构建领域模型。限界上下文可以作为微服务拆分和设计的边界。
利用DDD战略设计,分解业务领域。从事件风暴入手,根据限界上下文边界构建可复用的领域模型。通过战略设计完成业务边界划分和领域建模,然后将领域模型作为战术设计的输入,完成微服务设计。
战术设计,是从技术视角出发,侧重于领域模型的技术实现,按照领域模型完成微服务的开发和落地。在战术设计中会有聚合、聚合根、实体、值对象、领域服务、领域事件、应用服务和仓储等领域对象。这些领域对象会以代码的形式映射到微服务中,从而完成设计和系统落地。
战略设计是连接产品需求与开发代码的一道桥梁

事件风暴

是一个团队活动,领域专家与项目团队通过头脑风暴的形式,罗列出领域中所有的领域事件,整合后形成领域事件集合,然后对每一个事件标注出导致该事件的命令,再为每个事件标注出命令发起方的角色
image.png

构建领域模型和划分微服务边界

1,在事件风暴中根据场景分析,梳理业务过程中的用户操作、领域事件以及与外部的依赖关系等,找出业务对象的业务行为,并根据业务对象梳理出实体、值对象、领域事件等领域对象
2、根据领域实体之间的业务关联性,找出聚合根,将业务紧密相关的、相互依赖的实体组合形成聚合,确定聚合中的聚合根、值对象和实体。
3、根据业务语义环境和上下文边界等因素,将一个或多个聚合划定在一个限界上下文内,构建领域模型。
从而,我们有了聚合限界上下文两层边界。
image.png

DDD基本原理

【业务】领域

领域,DDD的领域就是边界内要解决的业务问题域。是一个特定范围
领域分为核心子域,通用子域和支撑子域。
核心子域,让企业业务和商业模式成功的关键核心能力。
通用子域,被多个子域重复使用的通用功能子域。
支撑子域,不被其他子域复用的通用功能。
DDD领域划分的核心思想就是将问题域逐级细分,采用分而治之的策略,将复杂问题简单化,从而降低业务理解和系统实现的复杂度。
业务领域的子域划分是比较粗的领域边界划分阶段,它不考虑子域内的领域对象以及对象之间的关系和层次结构。子域按照业务流程功能模块的边界进行粗分,目的是为了逐步减少业务边界,从而能够在一个相对较小的空间内,比较好的用事件风暴来梳理业务场景,构建领域模型。
在多数业务场景中,子域的边界天然就是流程节点的边界。
划分核心子域、支撑子域和通用子域的主要目的,是通过领域划分,区分不同子域在业务中的不同功能属性和重要性,对不同子域采取不同的资源投入和建设策略。

限界上下文:定义领域边界的利器

通用语言,定义对象在上下文的含义。在事件风暴过程中,团队内达成共识的,准确描述业务含义和规则的语言。
限界上下文,Bounded Context,定义领域边界,确保每个上下文对象在特定的边界内具有唯一的含义,在这个边界内,组合这些对象构建领域模型。在限定的上下文环境内,用来封装通用语言和领域对象,保证领域内的术语、领域对象等有确切的含义,没有语义二义性业务边界
限界上下文是定义通用语言的上下文边界。这个边界既是业务领域的边界,也是微服务拆分的边界。
限界上下文本质上就是子域,只不过它会更多的考虑领域对象的语义边界和技术实现细节。
DDD中包括问题域和解决方案域两个不同维度。问题域从业务视角,完成从领域到子域的分解。解决方案域从技术实现的角度,通过划分限界上下文和采用DDD战术设计完成微服务的拆分和落地。
小结,通用语言是项目团队内部交流的统一语言,而通用语言的语义上下文环境是由限界上下文限定的,这个边界确保通用语言无二义性。

实体和值对象:领域模型的基础单元

实体

实体,有唯一标识符的对象。
实体的业务形态
在战略设计时,实体是业务对象,集多个业务属性、业务操作或行为于一体。
实体的代码形态
在代码模型中,实体是实体类,这个类包括实体的属性和方法。
DDD强调面向对象的设计方法,这些实体通常采用充血模型。在充血模型中,业务逻辑都在领域实体对象中实现,实体本身包含属性和业务行为。贫血模型只有setter和getter方法。
实体的数据库形态
DDD是先构建领域模型,通过场景分析找出实体对象和行为,再将实体对象映射到数据持久化对象。在领域模型映射到数据模型时,一个实体可能对应0个、1个或多个持久化对象。

值对象

值对象,是通过对象属性值来识别的对象,它将多个相关属性组合成一个概念整体,用于描述领域的某个特定方面。值对象是一个没有标识符的对象,即描述的是不可变的对象。
值对象通过抽象或标准化设计,可以采用数据冗余的方式在不同的业务领域实现数据流转。
数据字典是值对象。
DDD引入值对象是希望实现从“数据建模”向“领域建模”的转变,减少数据库表的数量和表与表之间复杂的依赖关系,尽可能简化数据库设计,提升数据库性能。
值对象可以简化数据库设计,提升性能。但无法满足基于值对象的快速查询和统计分析。但有不少数据库已开始支持基于JSON串的查询方式。

值对象与实体

很多值对象的数据可能来源于其他聚合,它们以数据冗余的方式完成不同领域中数据的共享。在值对象的数据源头聚合中,以实体或聚合根的形式存在,完成实体和数据的集中维护和生命周期管理。在自己的聚合中它则以值对象的形式存在,被聚合内的实体引用。
实体一般对应业务对象,它具有相对丰富的业务属性和业务行为。值对象是属性集合,完成对实体的状态和特征描述,其核心本质是“值”。

聚合和聚合根

聚合

聚合,将紧密关联的实体和值对象聚集在一起,按照统一的业务规则共同完成特定的业务功能。
聚合在DDD分层架构里属于领域层,同一个微服务的领域层可以有多个聚合,每个聚合内有一个聚合根,多个实体、值对象和领域服务等领域对象。同一个限界上下文内的多个聚合,通过应用层组合在一起共同实现领域模型的核心领域逻辑。为每个聚合设计一个仓储完成聚合数据的持久化。
聚合在领域模型里是一个逻辑边界。它本身没有业务逻辑实现相关的代码。
聚合内的实体以充血模型实现业务逻辑。跨多个实体的领域逻辑通过领域服务来实现。跨多个聚合的业务逻辑的组合和编排,是通过应用服务来实现。
聚合内有业务规则确保聚合内数据的一致性。

聚合根

聚合根的主要目的是避免聚合内由于复杂数据模型缺少统一的业务规则控制,而导致聚合内实体和值对象等领域对象之间数据不一致的问题。
如果把聚合比作组织,那么聚合根就是这个组织的负责人。
聚合根也是实体。协调完成聚合共同的业务逻辑。聚合之间以聚合根ID关联方式接受聚合的外部任务和请求,在限界上下文内实现聚合之间的业务协同。聚合外部对象不能直接通过对象引用的方式访问聚合内的对象。
理论上,聚合根方法和领域服务都可以组合多个实体对象完成复杂的领域逻辑。但建议聚合根除了承担聚合管理外,只作为实体实现与聚合根自身行为相关的业务逻辑,而将跨多个实体的复杂领域逻辑放在领域服务中。

聚合的设计步骤

DDD领域建模时通常采用事件风暴方法,采用用户旅程分析和场景分析等需求分析方法,针对特定的业务场景梳理出所有业务行为和领域事件,然后找出所有产生这些业务行为的实体和值对象等领域对象,梳理这些领域对象之间的关系,找出聚合根,找出与聚合根业务紧密关联的实体和值对象,组合并构建聚合。

聚合的设计原则

1、聚合是封装业务不变性,而不是简单的将对象组合在一起。聚合内有一套不变的业务规则,各实体和值对象按照统一的业务规则运行,保证聚合内对象数据的一致性。
2、聚合之间通过引用聚合根ID的方式,而不是通过对象引用的方式进行交互
3、在聚合内采用数据强一致性,在聚合之间采用数据最终一致性。如果一次业务操作涉及多个聚合数据的修改,那么应该采用领域事件驱动机制。
4、通过应用层实现跨聚合的服务调用。

聚合的设计模式

在聚合设计时,如果聚合内领域对象较多,领域对象的初始化和持久化就会比较复杂。这时可以使用两种设计模式:仓储模式(Repository Mode)和工厂模式(Factory Mode)。仓储模式完成领域对象的持久化,工厂模式完成聚合领域对象的创建和数据初始化。
仓储模式既实现了领域逻辑和数据处理逻辑的解耦,也解决了领域层对基础层的依赖,实现依赖倒置。

小结

一个微服务可以有多个聚合,聚合之间的边界是微服务内的逻辑边界,它是可拆分的最小单元。有了这个逻辑边界,在微服务架构演进时,可以以聚合为单位进行拆分和组合。因此微服务的架构是可以演进的。

领域事件:解耦微服务的关键

领域事件,结合消息中间件和事件发布订阅的异步处理方式,实现数据的最终一致性。
在同一个微服务内,为什么一次事务更新多个聚合数据时,要使用事件总线或事务机制?
聚合是微服务内最小的业务功能单元。为保证聚合内数据更新时符合聚合内固定的业务规则,在一次事务提交时通常会将聚合内所有变更的数据作为整体,通过聚合领域服务或聚合根方法一次通过仓储完成数据持久化。
image.png

领域驱动战术设计落地

考虑两个大方向即可:

  • 模型设计:使用充血模型还是贫血模型
  • 架构设计:DDD/CQRS、整洁架构、六边形架构……

参考

《中台架构与实现–基于DDD和微服务》–欧创新
DDD领域建模实战——四色建模法 - 脉脉

  • 14
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值