1. 背景

参考《DDD领域驱动设计-案例需求文档》,本文将构建实体,聚合根详述领域驱动中的建模设计。构建实体,聚合根的一些原则或方法,将在后续文章中说明。

2. 建模设计

2.1. 实体建模

参考售后补偿需求文档,对售后补偿业务做领域建模。现规划如下:

2.1.1. 补偿单聚合跟

补偿单聚合根主要是针对业务中,用户通过不同的场景创建补偿单的过程。如售后管理人员,客服人员通过后端管理系统发起补偿申请,电商用户通过app发起售后补偿申请。补偿单聚合根具有申请,修改状态,修改补偿信息等行为,整个过程在领域层做业务实现,最终通过仓库层落地到数据库。补偿聚合根的唯一标识为补偿单号,该补偿单号最终会落地到数据表中。补偿单聚合根具体组成属性如下图(右键图片,在新标签页中查看,可查看完整大图):
2.1.1.1. 补偿单实体
本例中的补偿单实体就是补偿单聚合根。他包括补偿单基础信息和补偿单方案信息。这里的补偿单基础信息和补偿单方案类似于订单信息和订单明细的关系,即一个为整体一个为部分。整体与部分具有共同的生命周期。部分依赖于整体,所以补偿方案与补偿单是一种组合的关系。
2.1.1.2. 补偿策略实体
补偿策略依赖于补偿单,抛开具体的补偿单,单独的补偿策略在系统中是不能独立存在的。补偿策略设置为一个实体,它是补偿单聚合根的一部分。补偿策略实体不能与补偿单实体分开,这两个实体中,补偿策略的信息,又会影响到补偿单实体的信息,如补偿策略为商品退款模式,补偿商品的总金额也要记录到补偿单实体的信息中。
一个策略实体由具体的补偿方案组成,分别为商品补偿策略实体,补发补偿策略实体,非商品补偿策略值对象三个方案中的一个方案组成(有且仅有一个)。因此策略实体于具体的子策略方案是一种聚合的关系。
2.1.1.3. 商品退款子实体
售后原因为商品原因导致的,针对这样的情况,为客服做补偿处理(不补发商品,可通过其他补偿如红包,代金卷),一个补偿单是基于订单的,一个订单存在多个商品的情况,因此一个补偿单存在多个商品需要补偿的情况。
2.1.1.4. 补发补偿子实体
最终对用户的补偿方案为补发商品。补发商品存在补发多个商品的情况。(补发与非补发,业务场景不同,需要的参数不同,是两个独立的实体)。
2.1.1.5. 非商品特殊补偿值对象
补偿的原因不是商品导致的,其他原因直接做的商品补偿。不补发商品,可通过其他补偿如红包,代金卷等放松补偿。

2.1.2. 售后履约单聚合根

售后履约单是售后补偿单的下游单据,当补偿单审批通过后,就开始真正执行补偿履约的功能了。补偿处理过程较长,有自己的生命周期,与补偿单的生命周期不一样,设置售后履约单聚合根,便于维护补偿过程中的信息。一个履约单在处理过程中,可能异步接收下级系统反馈的信息,将下级系统反馈的信息设置为履约单的值对象信息。履约单聚合根具体组成属性如下图(右键图片,在新标签页中查看,可查看完整大图):
2.1.2.1. 创建订单反馈值对象
补偿处理模式为补发时,会调用订单系统创建订单,订单系统又会基于消息通知售后补偿系统订单出库了。建立订单反馈值对象,用于接收订单创建成功时的消息。
2.1.2.2. 创建退款反馈值对象
补偿处理模式为商品补偿时,会调用支付系统发起退款,支付系统又会基于消息通知售后补偿系统退款的结果。建立退款反馈值对象,用于接收退款处理的消息。

2.2. 领域服务

实体行为的具体逻辑实现,单独编写一个实现类,这种类在DDD里被叫做领域服务(Domain Service)。

2.2.1. 补偿单聚合根领域服务设计

售后补偿单聚合根中,对于行为中简单的逻辑,如修改责任方信息,设置单据终止等直接在领域服务中编写实现逻辑代码。对于存在多个分支,复杂的情况如审批,保存,应基于面向对象编程思维,采用接口的模式完成功能。
例如:补偿聚合根保存时,补偿方案不同,保存的数据不同,判断的逻辑不同,应该将补偿策略信息单独抽象出来,在领域服务中,完善补偿策略信息。可根据策略设计模式,把可能导致分支变化的模块独立出来,基于接口编程,便于后续功能扩展和维护。
补偿单聚合根领域服务实现类图参考如下(右键图片,在新标签页中查看,可查看完整大图):

2.2.2. 履约单聚合根领域服务设计

补偿履约单是补偿单的下级单据,当补偿单据审批通过后,就创建补偿履约单。补偿履约单有自己的行为和生命周期。
补偿履约单处理过程中,失败时,仅修改履约单的单据状态,补偿完成时,也只修改履约单的单据状态。(不可在补偿履约聚合根中,直接操作补偿单聚合根修改补偿单的状态)。
售后补偿处理:调用售后履约单聚合根的处理行为,处理结果分为三种:
  1. 处理通过:这种是履约单调用下级系统,可以同步得到处理结果(成功或失败)。
  2. 处理中:履约单调用下级系统,是一个异步的回复过程。只有等下级单反馈后,才可以做补偿完成。处理中状态,设置履约单状态为处理中。
  3. 处理异常:履约单处理过程发生异常,记录补偿履约待沟通记录。
补偿处理场景举例如:
  • 履约单处理为补发订单时,发起处理并不能马上得到处理的结果。针对这样的情况,为履约单设计一个接受下级反馈的行为。
  • 履约单处理为其他模式补偿处理时,发起处理可以同步得到处理结果。基于接口编程,顶级接口设置了接受下级反馈函数(设置一个默认的default方法),实现类可不实现下级反馈函数。
履约单聚合根领域服务实现类图参考如下(右键图片,在新标签页中查看,可查看完整大图):

2.3. 事件通知

当履约单结束时,仅仅表示补偿单的履约环节结束了,并不表示整个补偿单完结了。补偿单与履约单是两个实体,不能跨越实体,直接在履约单中调用补偿单的完成行为。同时,履约单完成了,也不需要马上要求补偿单就完成,因此履约单完成后,可发送一个完成事件通知。
补偿完成存在两种情况:
  1. 补偿履约单在发起处理时,同步就完成了。
  2. 补偿履约单发起处理后,由下级系统反馈,异步告知补偿完成。
履约处理后,只更新履约单的状态。发布一个履约单事件,通知其他实体做对应的业务处理。参考事件通知的模式,基于Spring Event事件通知机制,做履约单后续的副作用业务处理(未采用领域事件模式,该模式还存在不完善的地方,采用Spring Event稳定可靠)。
履约单完成后,发送事件,补偿单监听事件,做后续业务处理:
  1. 当通知信息为履约单处理异常,补偿单变更为待沟通状态;
  2. 当通知信息为履约单处理成功,补偿单变更为结束状态;
  3. 当通知信息为履约单处理中时,补偿单变更为补偿中状态;

DDD领域驱动设计-案例建模设计-Ⅲ的更多相关文章

  1. DDD 领域驱动设计-商品建模之路

    最近在做电商业务中,有关商品业务改版的一些东西,后端的架构设计采用现在很流行的微服务,有关微服务的简单概念: 微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成.系统中的各个微服务可被独 ...

  2. C#进阶系列——DDD领域驱动设计初探(一):聚合

    前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的 ...

  3. DDD领域驱动设计和实践(转载)

    -->目录导航 一. DDD领域驱动设计介绍 1. 什么是领域驱动设计(DDD) 2. 领域驱动设计的特点 3. 如果不使用DDD? 4. 领域驱动设计的分层架构和构成要素 5. 事务脚本和领域 ...

  4. DDD领域驱动设计初探

    DDD领域驱动设计初探1 前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下D ...

  5. DDD领域驱动设计初探(一):聚合

    前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的 ...

  6. DDD领域驱动设计-概述-Ⅰ

     如果我看得更远,那是因为我站在巨人的肩膀上.(If I have seen further it is by standing on ye shoulder of Giants.)         ...

  7. DDD领域驱动设计-设计规范-Ⅵ

    不以规矩,不能成方圆.                                                                     -战国·邹·孟轲<孟子·离娄章句上 ...

  8. 浅谈我对DDD领域驱动设计的理解

    从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...

  9. DDD领域驱动设计之聚合、实体、值对象

    关于具体需求,请看前面的博文:DDD领域驱动设计实践篇之如何提取模型,下面是具体的实体.聚合.值对象的代码,不想多说什么是实体.聚合等概念,相信理论的东西大家已经知晓了.本人对DDD表示好奇,没有在真 ...

随机推荐

  1. python库--flashtext--大规模数据清洗利器

    flashtext.keyword (flashtext) 类/方法 返回值 参数 说明 .KeywordProcessor() 对象kp case_sensitive=False 是否区分大小写 添 ...

  2. [源码解析] 深度学习流水线并行 PipeDream(6)--- 1F1B策略

    [源码解析] 深度学习流水线并行 PipeDream(6)--- 1F1B策略 目录 [源码解析] 深度学习流水线并行 PipeDream(6)--- 1F1B策略 0x00 摘要 0x01 流水线比 ...

  3. Optional容器类

    一.Optional 容器类:用于尽量避免空指针异常 方法 /* * Optional.of(T t) : 创建一个 Optional 实例 * Optional.empty() : 创建一个空的 O ...

  4. 密码学系列之:Argon2加密算法详解

    目录 简介 密钥推导函数key derivation function Password Hashing Competition Argon2算法 Argon2的输入参数 处理流程 简介 Argon2 ...

  5. 集群环境下的Session管理

    1. 集群环境下的管理HTTPSSession所遇到的问题 一台服务器对应这个一个session对象,无法在另外一个服务器互通 解决方法: 1. Session 的 Replication(复制)将当 ...

  6. js原型和原型链理解 constructor 构造函数

    一.对象:普通对象   函数对象 二.构造函数特点:1.需要new实例化,内部使用this对象指向即将要生成的实例对象  2.首字母大写,用于区分普通函数 function Person(name){ ...

  7. Python3入门系列之-----字典

    字典 字典是一种可变容器模型,且存放任何类型对像(如:字符串,数字,或者列表甚至字典),每个字典有键名(key)和键值(value)且用冒号 :  隔开, 多个字典用逗号(,)隔开整个字典包括在花括号 ...

  8. Ubuntu开发相关环境搭建

    一.Ubuntu系统语言环境切换修改 安装时,选择的中文版,但实际使用起来,很不爽,果断切换为英文 1.1 打开终端: vim /etc/default/locale 1.2 修改配置 LANG=&q ...

  9. 题解 「2017 山东一轮集训 Day7」逆序对

    题目传送门 Description 给定 $ n, k $,请求出长度为 $ n $ 的逆序对数恰好为 $ k $ 的排列的个数.答案对 $ 10 ^ 9 + 7 $ 取模. 对于一个长度为 $ n ...

  10. 创建线程的4种方法 and 线程的生命周期

    线程的启动和运行 方法一:使用start()方法:用来启动一个线程,当调用start方法后,JVM会开启一个新线程执行用户定义的线程代码逻辑. 方法二:使用run()方法:作为线程代码逻辑的入口方法. ...