DDD技术方案落地实践 | 京东云技术团队

本文作者分享了在领域驱动设计(DDD)落地实践中的经验。先简要回顾DDD理论,包括名词概念、建模方法和整洁架构;接着介绍工程示例,如内核与实现分离、CQRS读写分离;最后针对实践中遇到的问题,如领域资源注册、内核模块化等,给出解决方案,还探讨了SAGA事务。

1. 引言

从接触领域驱动设计的初学阶段,到实现一个旧系统改造到DDD模型,再到按DDD规范落地的3个的项目。对于领域驱动模型设计研发,从开始的各种疑惑到吸收各种先进的理念,目前在技术实施这一块已经基本比较成熟。在既往经验中总结了一些在开发中遇到的技术问题和解决方案进行分享。

因为DDD的建模理论及方法论有比较成熟的教程,如《领域驱动设计》,这里我对DDD的理论部分只做简要回顾,如果需要了解DDD建模和基础的理论知识,请移步相关书籍进行学习。本文主要针对我们团队在DDD落地实践中的一些技术点进行分享。

2. 理论回顾

理论部分只做部分提要,关于DDD建模及基础知识相关,可参考 Eric Evans 的《领域驱动设计》一书及其它理论书籍,这里只做部分内容摘抄。

2.1.1 名词

领域及划分:领域、子域、核心域、通用域、支撑域,限界上下文;

模型:聚合、聚合根、实体、值对象;

实体

是指描述了领域中唯一的且可持续变化的抽象模型,有ID标识,有生命周期,有状态(用值对象来描述状态),实体通过ID进行区分;

每个实体对象都有唯一的 ID。我们可以对一个实体对象进行多次修改,修改后的数据和原来的数据可能会大不相同。比如商品是商品上下文的一个实体,通过唯一的商品 ID 来标识,不管这个商品的数据如何变化,商品的 ID 一直保持不变,它始终是同一个商品。

在 DDD 里,这些实体类通常采用充血模型,与这个实体相关的所有业务逻辑都在实体类的方法中实现。

聚合根

聚合根是实体,是一个根实体,聚合根的ID全局唯一标识,聚合根下面的实体的ID在聚合根内唯一即可;

聚合根是聚合还原和保存的唯一入口,聚合的还原应该保证完整性即整存整取;

聚合设计的原则

  1. 聚合是用来封装真正的不变性,而不是简单的将对象组合在一起;

  2. 聚合应尽量设计的小,主要因为业务决定聚合,业务改变聚合,尽可能小的拆分,可以避免重构,重新拆分

  3. 聚合之间的关联通过ID,而不是对象引用;

  4. 聚合内强一致性,聚合之间最终一致性;

值对象

值对象的核心本质是值,与是否有复杂类型无关,值对象没有生命周期,通过两个值对象的值是否相同区分是否是同一个值对象;

值对象应该设计为只读模式, 如果任一属性发生变化,应该重新构建一个新的值对象而不是改变原来值对象的属性;

领域事件

在事件风暴过程中,会识别出命令、业务操作、实体等,此外还有事件。比如当业务人员的描述中出现类似“当完成…后,则…”,“当发生…时,则…”等模式时,往往可将其用领域事件来实现。领域事件表示在领域中发生的事件,它会导致进一步的业务操作。如电商中,支付完成后触发的事件,会导致生成订单、扣减库存等操作。

在一次事务中,最多只能更改一个聚合的状态。如何一个业务操作涉及多个聚合状态的更改,可以采用领域事件的方式,实现聚合之间的解耦;在聚合根和跨上下文之间实现最终一致性。聚合内数据强一致性,聚合之间数据最终一致性。

事件的生成和发布:构建的事件应包含事件ID、时间戳、事件类型、事件源等基本属性,以便事件可以无歧义地在不同上下文间传播;此外事件还应包含具体的业务数据。

领域事件为已发生的事务,具有只读,不可变更性。一般接收消息为异步监听,处理的后续处理需要考虑时序和重复发送的问题。

2.1.2 聚合根、实体、值对象的区别?

从标识的角度:

聚合根具有全局的唯一标识,而实体只有在聚合内部有唯一的本地标识,值对象没有唯一标识;

从是否只读的角度:

聚合根除了唯一标识外,其他所有状态信息都理论上可变;实体是可变的;值对象是只读的;

从生命周期的角度:

聚合根有独立的生命周期,实体的生命周期从属于其所属的聚合,实体完全由其所属的聚合根负责管理维护;值对象无生命周期可言,因为只是一个值;

2.2 建模方法

2.2.1 事件风暴

事件⻛暴法类似头脑⻛暴,简单来说就是谁在何时基于什么做了什么,产⽣了什么,影响了什么事情。

在事件风暴的过程中,领域专家会和设计、开发人员一起建立领域模型,在领域建模的过程中会形成通用的业务术语和用户故事。事件风暴也是一个项目团队统一语言的过程。

2.2.2 用户故事

用户故事在软件开发过程中被作为描述需求的一种表达形式,并着重描述角色(谁要用这个功能)、功能(需要完成什么样子的功能)和价值(为什么需要这个功能,这个功能带来什么样的价值)。

例:

作为一个“网站管理员”,我想要“统计每天有多少人访问了我的网站”,以便于“我的赞助商了解我的网站会给他们带来什么收益。

通过用户故事分析会形成一个个的领域对象,这些领域对象对应领域模型的业务对象,每一个业务对象和领域对象都有通用的名词术语,并且一一映射。

2.2.3 统一语言

在事件风暴和用户故事梳理过程及日常讨论中,会有越来越多的名词冒出来,这个时候,需要团队成员统一意见,形成名词字典。在后续的讨论和描述中,使用统一的名称名词来指代模型中的对象、属性、状态、事件、用例等信息。

可以用Excel或者在线文档等方式记录存储,标注名称,描述和提取时间和参与人等信息。

代码模型设计的时侯就要建立领域对象和代码对象的一一映射,从而保证业务模型和代码模型的一致,实现业务语言与代码语言的统一。

2.2.4 领域划分及建模

DDD 内核的代码模型来源于领域模型,每个代码模型的代码对象跟领域对象一一对应。

通过UML类图(通过颜色标注区分聚合根、实体、值对象等)、用例图、时序图完成软件模型设计。

2.3 整洁架构(洋葱架构)

整洁架构(Clean Architecture)是由Bob大叔在2012年提出的一个架构模型,顾名思义,是为了使架构更简洁。

整洁架构最主要原则是依赖原则,它定义了各层的依赖关系,越往里,依赖越低,代码级别越高。外圆代码依赖只能指向内圆,内圆不知道外圆的任何事情。一般来说,外圆的声明(包括方法、类、变量)不能被内圆引用。同样的,外圆使用的数据格式也不能被内圆使用。

整洁架构各层主要职能如下:

  • Entities:实现领域内核心业务逻辑,它封装了企业级的业务规则。一个 Entity 可以是一个带方法的对象,也可以是一个数据结构和方法集合。一般我们建议创建充血模型。

  • Use Cases:实现与用户操作相关的服务组合与编排,它包含了应用特有的业务规则,封装和实现了系统的所有用例。

  • Interface Adapters:它把适用于 Use Cases 和 entities 的数据转换为适用于外部服务的格式,或把外部的数据格式转换为适用于 Use Casess 和 entities 的格式。

  • Frameworks and Drivers

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值