其他分享
首页 > 其他分享> > DDD如何设计落地?(库存,产品账示例)

DDD如何设计落地?(库存,产品账示例)

作者:互联网

一. 背景

     本文以预算管控服务建设作为一个DDD设计的例子介绍,目标是是呈现一次DDD设计的过程,为了减少绘图和描述的工作量,文中会对预算管控业务需求和功能做简化。请重点关注设计的流程,这是我们想传达的重点,忽略设计细节的合理性。

另外,对于预算管控服务来讲,不一定要用DDD来进行分析设计,基于传统的数据驱动就完全可以满足需求,但作为介绍DDD实施过程,预算管控是一个不错的例子(不需要画太多的图)。在这里我们不讨论什么类型项目合适DDD,可以参考:

大致的共识为复杂度高的业务适合DDD。而复杂度一般体现在:

业务流程长

业务场景多

业务概念多

业务系统干系人多

业务系统需要长期维护且持续有变更

 

业务背景

需要设计一个适用于本地生活场景的资源预算规划和管控服务,业务需求上主要包括两方面的用例:

  1. 品牌发放权益需要有一定的限额,不能无限制的发放。包括品牌、门店、活动、人群、权益等维度

  2. 个人消费者参与活动领取权益有次数的限制,不能无限额领取或使用。包括在活动、品牌、门店、商品等维度

目前各业务线针对以上需求,各自实现了部分能力,整体上看较零碎、不完善、不统一。本次目标是设计一个统一的平台为各业务方提供基础能力

二. 战略设计

2.1 业务梳理

2.1.1 业务定位&目标分析

协同分析阶段,需要各干系方共同参与,如,业务运营,业务产品,运营产品,平台架构,业务系统方的技术等。

目标:聚焦业务需求和平台定位,确定平台的能力范围和服务方式

输出需求文档:

  1. 提供一个统一的记账能力,以平台的方式为各个系统提供记账服务。

主要功能:

细化要求:

  1. 库存管理

主要功能:

细化要求:

2.1.2 业务抽象可视化

通过事件风暴或四色建模法来可视化。我们这里选择事件风暴法。过程主要涉及

 

 

 

这里列了主要的命令

 

主要是识别发出命令的主体是谁,如C端消费者,B端消费者还是某个系统。主要是通个主体在具体Usecase中去串联命令对于领域对象(对应领域名词)的影响。串联业务流程完成领域分析

在命令发出后对一个领域对象(聚合根)将产生影响,往往对内(聚合根)会生成数据或发生状态变更;对外(向其他聚合根)发送消息或触发事件。

这些事件是业务专家重点关心的结果

 

这里是先识别领域事件,还是先识别命令可以根据设计者的习惯和熟悉度,自行选择

 

最后,整合命令,领域对象和领域事件的关系,得到业务梳理的输出文档(实际命令可能比图中多,如库存冻结和预扣等):

 

2.2 统一领域语言(示意,不包括全部)

2.1章中几个阶段是一个来回讨论的阶段,通常需要经过很多轮的修改和妥协,以至于早期列出的领域名词、领域事件和命令远多于上面的图例,但最后大家需要统一确定其中关键的领域名词、领域事件,并统一领域语言,在后续的讨论和设计阶段均使用统一语言建模。这里我们用下面的统一语言仅示例产品账:

术语

描述

记账主体(principal)(mainPrincipal)(subPrincipal)

记账主体(id),如,抽奖活动中的消费者记账,则为cid

账单(accounting document)(accounting doc)

名词,一次记账请求提交的数据为一条记录。指产品方提交给记账平台的原始单据数据

记账(keep account)

动词,记录record的过程

销账(write off account)

动词组,记账的反向操作

金额(amount)

记账的数量

账(account)

按账期 统计的在该周期内的数额总和相关数据

账期(account cycle)

账期(会计周期)的类型,如,日账,月账,终身账等

账期值(account cycle value)

账期值。如对于自然日类型的账期,账期值可以是“20210415”代表4月15这天的账

记账类型(operate type)

操作类型指,记账或销账

 

2.3 限界上文识别

最后,当领域名词、领域事件和命令都统一并清理好之后,我们需要圈定合适领域出来,这里要注意,并没有统一的最佳答案,圈定原则只是遵循现实世界的松紧耦合关系,某些场景下可能有多种选择,本例较简单,示例如下

 

 

2.4 问题子域识别

在战略设计阶段的最后,按“一个子域负责解决一个独立业务价值的问题”的原则,将限界上下文划分到不同的问题子域(Subdomain)中,同时还需要从更大的域视角来俯览全局,并按照以下三种类型进行标注:

 

 

问题子域,是对业务问题的进一步澄清和划分,同时也是对于资源投入优先级的重要参考,相对限界上下文来说,问题子域是对业务问题更大粒度的划分,是在限界上下文识别后与问题域匹配的一个过程。

 

通过对于子域进行识别、划分和类型标注,团队能够实现软件架构在业务边界上的内聚和解耦,便于逆向应用“康威定律”。

 

在 DDD 的概念中,限界上下文和问题子域是两个不同维度的概念,限界上下文可能只是真实问题子域的一部分表达,也可能限界上下文中的一些领域名词超出实际问题子域的范围,理论上来说没有绝对的依赖关系。需要根据实际需求和成本综合考虑,既要保证便资源分配的合理,又要在降低落地成本的同时保证后期演进的适度兼容。

 

问题子域识别过程的产出物,如下图所示:

 

 

2.5 限界上下文映射(示意,不包括全部)

这里只示例产品账的。明确限界上下文映射关系,是为了更明确各context之间的关系,在IDDD中给出了9种关系,在本例种只涉及到3种,实际项目中可能比这个复杂的多,尤其是涉及集成和遗留系统的场景。

明确contex之间关系,有助于后续保证系统之间的依赖关系,为后续架构模式的补充模块做好准备。

 

 

 

 

三. 战术设计

3.1领域建模

3.1.1 领域对象提取(聚合/实体识别)

偷个懒,这里只示意产品账的实体和部分值对象

 

3.2 业务服务识别

业务服务识别,是为后续系统实现进行的基于业务边界的模块拆分分析,常见的拆分方法有:

 

通过对于业务服务进行划分,团队能够获得对软件架构模块拆分的直接指导,并且还能够依据“逆康威定律”依据架构结果进行开发团队的划分和组建。

下面是预算管控子域的服务拆分示例

子域

服务

预算管控子域

库存服务

产品账服务

 

3.3 业务服务接口识别

单独对业务服务的接口能力进行识别,是符合面向接口编程原则的,提前定义服务的概要设计方案,可以让后续团队成员更快开展工作,也方便后续接口的详细设计

 

这里提前识别服务接口,是为了避开技术实现细节的影响。我们在基于具体技术实现的情况下设计接口,通常会干扰领域驱动的设计。我们试想下基于swagger文档,设计API时,我们是否容易保证API的归属正确领域服务。所以提前的概要识别和设定很重要

 

下面是库存和账服务接口识别示例:

子域

聚合根/实体

接口能力

读写

账上下文

账单

记账

账本

单主体单产品单账期查账

单主体批量产品单账期查账

库存上下文

库存

创建库存

扣减库存

缩扩容库存

查询库存

 

四. 技术实现

在完成了战略设计和战术设计之后,就可以考虑具体的技术详设,这个阶段会设计到具体的架构模式选择,架构风格和基础技术,存储等的选择。

包括且不限于:

在本例里只示例产品账的领域模型参考:

  

 

其中账本(accountbook)不需要持久化,其他领域对象均需要持久化

五. 总结

最后需要时刻提醒的。没到最后实现阶段之前应该杜绝提前考虑技术细节和技术实现,否则很容易偏离DDD

 

标签:需要,落地,示例,业务,子域,库存,记账,领域,DDD
来源: https://blog.csdn.net/Botaruibo/article/details/117856397