DDD实战之八:冲刺 1 战术之聚合设计(下)

简介: DDD实战之八:冲刺 1 战术之聚合设计(下)

其次,我们识别这个模型的值对象、实体对象,并适当地再次提炼归纳。分析如下:

“购物车商品总数”其实就是个普通整数,并没有特定的业务逻辑需求,故将其从对象模型删除。

“购物车待结算总价”、“订单支付金额”其实就是 Money 值对象类,没必要专门为其设定特定的值对象类,故统一为“金额 Money”值对象。

“购物车商品小计”、“购物车商品分类计数”,这两个是有结构性的、多字段属性的值对象,作为“购物车”和“购物车商品行”的属性存在,而无需作为带标识 ID 的实体对象存在。

“订单状态”、“订单可见状态”、“订单支付状态”这 3 个对象明显也是作为值对象而存在。因为其分别作为“订单”和“订单支付记录”的属性而存在,并且需要定义特定的业务取值,并不能无限任意取值,所以需要有自身的业务逻辑知识。

“订单提货方式”、“订单联系信息”、“订单送货地址”这 3 个对象也适合作为值对象而存在。其中,“订单提货方式”具备取值范围的业务知识;而“订单联系信息”、“订单送货地址”是多属性字段组合的整体概念,因为“群买菜”不是物流类软件,没必要将它们识别为具备 ID 标识的实体对象,所以它们也都作为值对象存在。

“订单备注”目前只是一段普通文本,其实是可以作为基本数据类型的,但考虑到将来的业务扩展性,可能会出现格式化要求。况且,即使现在我们也是需要限制其字符串长度的,这也算是一种“业务知识”。为此我也将其作为值对象考虑。

“手机号”是一种特定格式和取值范围要求的数字字符串,故也作为值对象。

“订单”、“订单行”、“订单商品快照”、“订单支付记录”这 4 个对象,是需要有 ID 标识存在的实体对象。很显然,一方面这些对象是具备多个字段属性的结构体,另一方面它们的“唯一性判别”不是基于其属性取值而是基于 ID 标识的。所以它们都是实体对象。

“品牌商品”可以理解它是品牌商店铺的商品库中某个商品、被加盟商销售后,形成在订单下的一种特殊的“订单商品快照”。“品牌商品”这个说法,只有在特定“订单”记录里面才生效。所以说,其实“品牌商品”是“订单商品快照”的一个子类,所以它也是一种实体对象。

“品牌商子订单”是在客户确认订单收货后,系统为品牌商品关联的品牌店铺自动生成的子订单,所以也是一种实体对象。不过,它属于“订单”的子类。但同时,“品牌子订单”又需要关联到“订单”作为其父订单,故“品牌子订单”和“订单”实体之间就有两重关系:泛化关系、关联关系。

需要说明的是:“品牌商品”、“品牌商子订单”不属于本次冲刺的工作范围。

“订单支付完成时间”可以使用 java 语言的基本数据类型 TDateTime 或 TLocalDateTime 即可,因为其取值并没有限制,故从对象模型中删除。

“微信预支付订单”其实是微信支付平台返回的、一系列用来给微信小程序前端调起微信支付的参数组合。它是依附在订单支付记录上的,随着微信支付的成功与否而更新内容,因此它也可以作为值对象存在。

“微信支付结果”类似“微信预支付订单”,它是微信支付平台返回的支付结果信息,也可以作为依附于订单支付记录而存在的值对象。

“订单”是记录订单操作痕迹的,是用来记录订单包含客户创建、客户支付、商家备货发货、客户确认、订单取消等一系列的信息,跟订单对象之间是合成关系,需要作为实体对象考虑。

“订单支付时限”、“订单确认时限”这两个看起来是某个对象。但实际上,它们是某种业务规则,是用来在订单创建是创建该订单的支付截止时间、确认截止时间。所以说,这两个名词更像是某种业务规则的配置参数。对于这种情况,有两种处理方式:一种是设立“规则上下文”并引入规则引擎,将它们全部纳入规则引擎的设计框架下,不再遵循 DDD 思想对其进行设计;另一种是将其转化为某种 DDD 对象模型。考虑到“群买菜”前面的战略设计中,已经舍弃规则引擎的引入,所以我们采用第二种处理方式。鉴于“订单支付时限”、“订单确认时限”实际上是某种业务参数配置,为了通用性,我们在对象模型中引入“业务参数”实体对象,该实体对象的 ID 即为“参数编码”,用于区分获取不同的业务配置参数。这样,就将“订单支付时限”、“订单确认时限”作为某种“参数编码”的“业务参数”来看待,而计算订单支付截止时间、确认截止时间的业务逻辑则由“订单上下文”的相关领域服务来实现。

“订单剩余支付时限”是用来给客户提示支付剩余时间的。显然,它是一种“计算结果”,是根据订单支付截止时间(见上条)结合系统时钟自动读秒倒计时的。事实上,这是一个“瞬间”取值,仅用于前端界面提示客户支付,并没有其它的业务价值,而且技术上并不适合要求后端服务给出准确的计算结果(会导致大量的前后端交互)。更为可取的方法,还是由前端界面根据“订单创建时间”进行计算。当然,这样做的坏处是:前端界面具备的一定的业务知识。但考虑到在“设计价值”和“实现简便性”上的权衡,我们还是建议这部分计算在前端界面实现。为此,从对象模型删除该对象。

经过上面的提炼归纳,我们调整订单上下文的对象模型如下图:

image.png

需要说明的是:“订单行”与“订单商品快照”之间其实是合成关系。因为订单商品快照依赖于订单行管理其生命周期,也就是因为订单行的存在而存在。但这里的“合成”关系有点特殊:一个订单行只会有一个商品快照。

为了方便代码实现,我们将对象模型的中文名改为英文名,如下图:

image.png


4划分聚合


事实上,上面的对象模型已经基本将聚合划分清晰了。唯一需要考虑的,就是“品牌子订单”需要和“订单”这两个实体对象分开在不同的聚合中,因为“品牌子订单”对于品牌商来说,是需要有独立的访问入口的(如:查询某品牌商收到的子订单),故在聚合上必须区分开来。划分聚合后的对象模型如下图:

image.png

需要说明的是:Money、Visible、MobileNumber 放到“共享内核上下文”,不作为任何聚合的内容。


04商品上下文



1名词建模


根据各业务用例规约查找名词如下表:

image.png


需要说明的是:“店铺”属于店铺上下文、“购物车”、“购物车状态标记”属于订单上下文,这里不作为考虑范围。商品上下文根据名词初步建模的对象模型如下图:

image.png


2动词建模(时标对象)


由于冲刺 1 只涉及到查询类用例,故没必要分析时标对象。


3归纳抽象


我们对上面的商品上下文对象模型做归纳抽象,并去掉一些没必要的对象。分析如下:

“售罄商品”其实是商品的一种,在我们已经有“商品有货状态”对象后,这个对象就显得多余,故去掉。

“关键词列表”其实就是“关键词”的 List,没必要单独出来一个对象,去掉。

甚至“关键词”都可以直接使用 java 语言的基本类型 String,暂时还没有诸如关键词热度、关联属性等特定业务规则,也可以去掉。

“商品最小下单量”就是普通的浮点数,不作为对象模型。

“商品显示顺序”、“商品类别显示顺序”都可以视作普通的整数,不作为对象模型。

我们再来对该对象模型识别识别值对象、实体对象,并给对象加上英文名称。分析如下:

“商品名称”、“商品描述”其实是受限制的字符串类型,显然作为值对象;

“商品图片”并不会独立存在于“群买菜”系统中,而总是依附于商品存在,故也作为值对象,并改名为“图片”;

“商品定价”其实就是一个金额,取值没有限制、也没有特别的业务逻辑,直接改为使用 Money 值对象。

“商品计量单位”、“商品优惠”、“商品限购”、“商品有货状态”显然也不会独立存在,而总是依附于商品存在,故也作为值对象。

“商品月销量”对每个商品来说,需要每月统计,而且它是依附在“商品”上存在的(“商品”控制了其生命周期),所以它是一种和“商品”之间有“合成”关系的实体对象。

最终修改后的对象模型如下图:

image.png


4划分聚合


该对象模型中,需要区别直接访问入口的实体对象有“商品类别”、“商品”。“商品类别”需要单独访问是需要在前端界面支持列出所有的商品类别,“商品”需要单独访问入口显而易见。而其它实体对象“商品图片”、“商品月销量”是不需要单独访问入口的,故最终聚合划分如下图:

image.png

需要说明的是:Money 仍然使用“共享内核”上下文的对象。Image 因为与业务完全无关,而且大概率会被其它上下文用到,也放到共享内核上下文。


05平台集成上下文



平台集成上下文,仅仅是微信开放接口的一些简单封装,包含“获取微信绑定手机号”、“保存订单状态通知订阅”、“向店铺发送订单状态通知”等简单功能。这些业务逻辑,基本上没有太多“领域”知识,正如我们在战略技术决策中考虑的,不考虑对其进行 DDD 战术设计。

到此,我就完成了“群买菜”冲刺 1 战术设计的聚合设计部分,剩下我还会用 1~2 篇完成冲刺 1 的服务设计和战术层面技术决策,然后就开始实际的 TDD 编码实现了。

相关文章
|
设计模式 算法 Java
设计模式第十五讲:重构 - 改善既有代码的设计(下)
设计模式第十五讲:重构 - 改善既有代码的设计
293 0
|
4月前
|
架构师 开发者
【悬念揭秘】DDD:那片隐藏在软件深处的业务乐土——.NET项目如何借力领域驱动设计,让复杂业务逻辑迎刃而解?
【8月更文挑战第28天】领域驱动设计(DDD)在.NET项目中的应用聚焦于将业务领域知识与软件开发紧密结合,通过构建清晰的领域模型管理复杂业务逻辑。DDD的核心概念包括限界上下文、聚合、实体等,确保模型与实现的统一。在.NET中,通过CQRS和事件源等模式提高系统响应性和可扩展性,实现业务事件驱动的解耦与协作。DDD不仅是一种设计方法,更是要求开发者深入理解业务的文化,助力.NET项目应对复杂挑战,实现业务与技术的融合。
68 6
|
7月前
|
敏捷开发 监控 架构师
【领域驱动设计专题】一文带领你透视DDD领域驱动模型的本质和设计原理分析指南(构建领域知识)
【领域驱动设计专题】一文带领你透视DDD领域驱动模型的本质和设计原理分析指南(构建领域知识)
195 0
|
7月前
|
运维 前端开发 JavaScript
平台设计-概念澄清说明
平台所说模块一般指一个独立部署的前端项目
|
前端开发 测试技术 定位技术
DDD实战之八:冲刺 1 战术之聚合设计(上)
DDD实战之八:冲刺 1 战术之聚合设计(上)
DDD实战之八:冲刺 1 战术之聚合设计(上)
|
敏捷开发 消息中间件 前端开发
DDD实战之七: 战术设计、整体流程与首次冲刺
DDD实战之七: 战术设计、整体流程与首次冲刺
DDD实战之七: 战术设计、整体流程与首次冲刺
|
消息中间件 运维 前端开发
DDD实战之六:战略设计之技术决策
DDD实战之六:战略设计之技术决策
DDD实战之六:战略设计之技术决策
|
Devops 持续交付
《精益产品开发》读书笔记之六--结
何老师的这本书是一本非常“好”读的书,深涩的概念也是讲得深入浅出,触类旁通,而且故事感十足。
210 0
《精益产品开发》读书笔记之六--结