DDD领域设计

1.领域设计概念为什么能火起来

近些年随着计算机技术的普及以及高性能服务器的出现,软件应用发生了翻天覆地的变化,从最开始的单体(BS/CS),到集中式架构,到微服务架构以及分布式多基座微服务,系统变得越发复杂。

第一阶段单体应用:主要是围绕着数据库进行的设计开发;

第二阶段集中式架构:采用面向对象的设计方法,业务逻辑分为业务层、逻辑层、数据访问层。这种架构可能会导致某一层或者几层变得臃肿;

第三阶段微服务架构:主要解决第二阶段业务层臃肿问题以及服务间的解耦。

对于微服务,人们通常的认知是:将原来的单体应用拆分为多个服务应用,或者将原来的单体技术架构换成一套支持微服务的技术架构,这就是微服务了。但真正拆解微服务后,我们遇到了什么问题?

  • 微服务应该如何拆分出应用?拆分的粒度多大算合适?

  • 微服务拆分的边界是什么?为什么要将某个功能迁移到别的团队?

  • 为什么技术语义和业务语义不尽相同?简单的业务功能需要关联上下游多系统?

  • 业务新增功能,应该由外部团队提供还是本团队提供?依据是什么?

针对上述问题,最根本的原因是没有理清楚业务边界和应用边界,一旦边界清楚后,这些问题就迎刃而解了。DDD领域设计就是专门来解决业务边界问题的。那么什么是DDD呢?

2. DDD概述

DDD的全称为Domain-driven Design,即领域驱动设计,是Eric Evans在2004提出的一种从系统分析到软件建模的一套方法论,以领域为核心驱动力的设计体系。

2.1 什么是领域

广义上来讲,领域是一个组织所做的事情以及其中包含的一切。实际情况下,可以简单理解为:领域对应为一个问题域,在同一个领域内,面临问题域相同。只要我们确定了系统所属的领域,那这个系统的核心业务,即要解决的关键问题、问题的范围边界就基本确定了。

2.2 什么是设计

DDD中的设计主要指领域模型的设计。DDD是一种基于模型驱动开发的软件开发思想,强调领域模型是整个系统的核心,领域模型也是整个系统的核心价值所在。领域模型绑定了领域和技术实现,确保了最终技术实现就一定解决了领域中的核心问题。主要有以下观点支持:

  • 领域驱动领域模型设计

  • 领域模型驱动技术实现。

2.3 什么是驱动

在我看来,驱动是一种问题思考方式。DDD中,我们总是以领域为边界,分析领域中的核心问题(核心关注点),然后设计对应的领域模型,再通过领域模型驱动代码实现。

2.4 DDD的概念

DDD的整体概念图如下

3.如何DDD

3.1 DDD能做什么

DDD主要分为两个部分,战略设计与战术设计。战略设计主要从高层"俯视"系统,帮助我们精准地划分领域以及处理各个领域之间的关系;而战术设计则从技术实现的层面教会我们如何具体地实施DDD。

3.2 战略设计

主要从业务视角出发,建立业务领域模型,划分领域边界,建立通用语言的限界上下文,限界上下文可以作为微服务设计的参考边界。战略设计主要包括领域/子域、通用语言、限界上下文和架构风格等概念。

3.2.1 领域和子域

领域如上所说,是软件系统要解决的实际问题相关的所有东西的集合。根据重要性和功能,对领域进一步细分为三类子域:核心子域、支撑子域和通用子域。核心域是业务成功的主要促成因素,主要竞争力,支撑子域是支撑核心域的,而通用子域是业务系统的公用部分。

3.2.2 限界上下文

限界上下文(Bounded context)是一个显式边界(边界:通常是一个子系统或者一个特定团队的工作),领域模型存在于边界之内。

3.2.3 上下文映射

限界上下文之间会互相集成,这种集成关系称为上下文映射。在DDD中存在多种组织模式和集成模式,如合作关系、共享内核、客户方和供应方开发、遵奉者、防腐层(简称ACL)、开放主机服务(简称OHS)、大泥球等。

3.2.4 通用语言

在限界上下文中,相同的语言描述具有相同的业务语义。比如在支付系统订单的限界上下文中,支付订单就是一种通用语言描述。

3.3 如何进行战略设计

领域专家通过业务经验、事物联系、领域风暴等方法,划分出限界上下文。在携程酒店的DDD落地中,有比较好的实现经验,概括为【两个前提】、【一个原则】【一个方法】,具体为:

  • 两个前提:

  • 对核心业务运营的逻辑有深刻的了解。能输出核心业务玩法和未来的规划,这样对业务玩法的讨论才会有明确的方向,最终制定的模型才能满足当前业务的诉求和未来调整的规划。有了这个前提,就不会讨论出一些大家谁都不知道会不会发生、也没人敢拍板的场景。

  • 对现有业务需求的来龙去脉有所了解。明确核心业务诉求及限于当前架构等各种因素采用的业务玩法,这样就可以有目的地进行改善。

  • 一个原则:

  • 业务原子原则。基于业务原子,容易划清业务边界避免业务职责重叠、过耦合。从根本上说,业务的划分应该到业务原子级别。做到了上面说到的两个前提,业务原子其实并不是难事。

  • 一个方法:通过业务本质保证业务原子。寻找业务本质的方法,可以基于业务开展的目的。任何明确的业务都有属于自己的玩法,开展这项业务也一定是有实际的目的,玩法根本上是要为目的服务的,目的是玩法最终希望表现的结果。只有抓住业务开展的目的,从根本上了解业务的本质和玩法,才能保证最终业务边界是基于业务原子划分的。

3.3.1 场景分析法
3.3.2 4色建模法

四色模型,顾名思义是通过四种不同颜色代表四种不同的原型。基本概念有:

  1. Moment-Interval Archetype 时标性原型:表示事物在某个时刻或某一段时间内发生的。使用红色表示,简写为 MI.

  1. Part-Place-Thing Archetype 参与方 - 地点 - 物品原型:表示参与扮演不同角色的人或事物。使用绿色表示。简写为 PPT。

  1. Role Archetype 角色原型:角色是一种参与方式,它由人或组织机构、地点或物品来承担。使用黄色表示。简写为 Role。

  1. Description Archetype 描述原型:表示资料类型的资源,它可以被其它原型反复使用,并为其它原型提供行为。使用蓝色表示。简写为 DESC。

用一句话来概括四色原型就是:一个什么样的人或组织或物品以某种角色在某个时刻或某段时间内参与某个活动。 其中“什么什么样的”就是DESC,“人或组织或物品”就是PPT,“角色”就是Role,而”某个时刻或某段时间内的某个活动"就是MI。 四色建模法主要的流程可以分为:

  • 首先满足业务需求,寻找需要追溯的事件;

  • 根据这些需要追溯,寻找足迹以及相应的时标性对象。

  • 寻找时标对象周围的人/事/物

  • 从中抽象角色

  • 把一些信息用描述对象补足。

3.3.3 事件风暴法

3.4 战术模式

战术模式是将战略设计进行具体化和细节化,它主要关注的是技术层面的实施。战术模式严重依赖于领域模型和通用语言,通过技术模式将领域模型和通用语言中的概念映射到代码实现中。战术模式和通用语言一样,都工作在特定限界上下文内,其应用边界受限界上下文的保护。

3.4.1 领域技术落地相关概念

各部分的协作关系

3.4.2 聚合的一致性边界

实体和值对象会相互协作,形成复杂的关联关系,在操作领域对象时,通过聚合模式来确保操作的一致性和事务的并发边界。

  • 聚合的不变形:聚合中对象的任何变更都需要通过聚合根来完成,聚合外部的对象只能引用另一个聚合的聚合根;

  • 生命周期的一致性:聚合对外的生命周期保持一致,聚合根生命周期结束,聚合的内部所有对象的生命周期也都应该结束。

  • 事务的一致性:聚合内的数据库操作再同一个事务内,不应该有跨聚合的事务。

4.如何构建DDD的微服务

4.1 领域驱动架构模型

  • 用户接口层:面向前端用户提供服务和数据适配。这一层聚集了接口和数据适配相关的功能。

  • 应用层:实现服务组合与编排,主要适应业务流程快速变化的需求。

  • 领域层:实现领域模型的核心业务逻辑。这一层聚集了领域模型的聚合、聚合根、实体、值对象、领域服务和领域事件,通过个领域对象的协同和组合形成领域模型的核心业务能力。

  • 基础设施层:聚集了各种底层资源相关的服务和能力,为各层提供基础资源服务。

4.2 六边形架构模型

核心思想:对于每种外界类型,都有一个适配器与之相对应。业务核心逻辑被包裹在内部,外界通过应用层API与内部进行交互,内部的实现无须关注外部的变化,更加聚焦。

结合分层架构的思想,外部的六边形可以理解为接口层与基础层,内部理解为应用层与领域层,内部通过DIP与外部解耦。

4.3 洋葱架构模型

洋葱架构模型是在六边形模型的基础上,结合分层架构,进一步把内部逻辑分为应用服务层,领域服务层和领域模型层。特点有:

  • 围绕独立的领域构建应用,内层定义接口,外层实现;

  • 依赖方向指向圆心,符合领域作为核心驱动的思想

  • 所有的应用代码可独立于基础设施编译和运行;

5.领域驱动实践

5.1 贫血模型和充血模型

贫血模型概念

所谓的贫血模型是在定义对象时,指定对象的属性信息,却没有对象的行为信息。贫血对象在设计之初就被定义为只能包含数据,不能加入领域逻辑;所有的业务逻辑是放在所谓的业务层(xxxService, xxxManager对象中),需要使用这些模型来传递数据。

比如,定义Employee对象会包含id,name,age,role,phone等信息,最后再通过添加一些对象属性的get/set方法来赋值取值操作,对象的逻辑方法抽象到EmployeeService中。

充血模型概念

充血模型在定义对象时不但包含对象的属性信息,还包括对象的行为信息,模型中状态的改变只能通过模型上的行为来触发,同时所有的约束及业务逻辑都收敛在模型上。

5.2 领域驱动设计的4个步骤

有时一个领域会很复杂,涉及到的领域概念、业务规则、交互流程很多,导致我们没办法直接针对这个大的领域进行领域建模。所以,我们需要将领域进行拆分,本质上就是把大问题拆分为小问题,然后各个击破的思路。

把一个大的领域划分为了多个小的领域(子域),最关键的就是要理清每个子域的边界;然后要搞清楚哪些子域是核心子域,哪些是非核心子域,哪些是公共支撑子域;然后,还要思考子域之间的联系是什么。总结抽象出以下4个步骤:

  • 第一个步骤:确定项目的愿景和目标,确定问题空间,找到项目核心业务流程,划分出核心子领域、通用子领域、支撑子领域;

  • 第二个步骤:针对每个子域,确定解决方案空间里的上下文限界。实际中可以简单理解为进程间隔离的物理边界;

  • 第三个步骤:在每个限界上下文内,采用分层架构思想进行分层设计;

  • 第四个步骤:领域内为了保障各个领域的完整性和一致性,引入聚合作为隔离领域的最小单元。

5.3 经典的电商领域设计

电商平台是一个具备非常复杂逻辑的综合体,我们该如何就问题划分子域呢?个人看法是从业务相关性的角度去思考,也就是平时说的按业务功能为出发点进行划分。

5.3.1 确定愿景和目标,确定问题空间

从业务和领域专家视角,电商平台提供的主要业务:

  • 用户登录平台;

  • 在平台上选择商品加入购物车,生成订单;

  • 优惠促销活动等,会在订单维度做相应的价格计算;

  • 订单支付;

  • 商品根据用户的地址信息快递到用户手中,完成商品选购;

所以根据业务逻辑,我们划分出相应的子域:

  • 会员中心:负责用户账户信息管理;

  • 商品中心:负责商品的展示、上下架、导航、管理等;

  • 订单中心:负责订单的生成和生命周期管理;

  • 营销中心:负责营销活动管理;

  • 交易中心:负责交易支付;

  • 库存中心:负责商品的库存信息;

  • 物流中心:负责商品的物流信息;

在这些子域内,可以根据业务需求确定哪些是核心子域。比如,核心子域包括商品、订单、交易、库存子域,会员中心可划分为通用子域,营销中心可划分为支撑子域(也可以划分为核心子域),物流中心可划分通用子域。

5.3.2 针对每个子域,确定上下文限界

以商品中心核心子域为例。商品中心子域的限界上下文圈定为:与商品相关的信息管理,包括:商品的名称、类目、属性、规格、价格、销售属性、图片信息;

5.3.3 在每个限界上下文内分层设计

仍以商品中心子域为例:采用洋葱模型,最核心的领域模型是商品,实体是商品信息,值对象有类目,属性、规格、销售属性等,这些聚合在一起形成商品聚合,商品聚合提供领域服务:商品基本信息更新、商品上下架、商品价格调整。

5.3.4 抽象出聚合,代码实现

参考文献:

https://zhuanlan.zhihu.com/p/436465508

https://www.cnblogs.com/youkiko/p/14684881.html

https://blog.csdn.net/m0_37583655/article/details/117565641

https://www.zhihu.com/topic/19826540/hot

https://blog.csdn.net/u011829645/article/details/118494114

https://www.infoq.cn/article/B738OdlRzubhZdFI54Gx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值