预算管控服务(库存和产品账平台)的DDD设计案例

简介: 一. 背景     本文预算管控服务建设作为一个DDD设计的例子介绍,目标是是呈现一次DDD设计的过程,为了减少绘图和描述的工作量,文中会对预算管控业务需求和功能做简化。请重点关注设计的流程,这是我们想传达的重点,忽略设计细节的合理性。另外,对于预算管控服务来讲,不一定要用DDD来进行分析设计,基于传统的数据驱动就完全可以满足需求,但作为介绍DDD实施过程,预算管控是一个不错的例子(不需要画太多的

一. 背景

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

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

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

业务流程长

业务场景多

业务概念多

业务系统干系人多

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

业务背景

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

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

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

二. 战略设计

2.1 业务梳理

2.1.1 业务定位&目标分析

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

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

输出需求文档

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

主要功能:

  • 记账
  • 销账
  • 各维度的查账
  • 库存创建
  • 库存扣减、查询
  • 库存缩扩容

细化要求:

  • 作为平台能为客户提供逻辑上的 数据隔离,即A产品方默认不能访问B产品方数据。如需要访问需要经过授权同意
  • 作为平台需要提供 同步记账能力和 异步记账能力,并提供明确的“能力范围”承诺
  • 作为平台需要为产品方提供方案 避免重复记账
  • 除记账之外,需要提供对应的 销账能力
  • 需要提供 常用的记账周期(账期),如时账,日账,周账,月账,季度账,年度账,终身账。
  • 需要提供 自定义记账周期(账期)的能力
  • 需要提供 一单多账记账能力,即一张单据,需要同时记录日账和终身账
  • 需要提供 多维度的查账能力,如按产品方,记账主体,产品,账期时间,以及基于这些维度的组合条件查账
  • 需要提供 批量查账能力,如主体下单一产品的批量账期时间,主体下的批量产品的单一账期时间,及其它可能的 批量组合
  • 技术上需要保证账单存储和记账动作的 事务
  • 技术上需要保证分库分表的 数据存储均衡
  • 技术上需要尽量保证分库分表的 数据库读写均衡分布,对可能出现的数据倾斜场景,需要给出明确的说明,和 使用限制性规范
  • 能提供 性能基准承诺,由测试团队对典型场景压测给出《平台性能报告》,作为平台对外服务的一部分
  • 库存的 创建,扣减,查询,扩容,缩容(缩容量不能少于剩余库存)
  • 库存冻结,解冻
  • 库存管理

主要功能:

  • 库存创建
  • 库存扩容
  • 库存缩容
  • 库存扣减
  • 库存回补
  • 库存查询

细化要求:

  • 满足去UMP的所有要求(去UMP为一个内部项目,各种限定型规则在此不细列)

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)中,同时还需要从更大的域视角来俯览全局,并按照以下三种类型进行标注:

  • 核心域(Core Domain): 是当前产品的核心差异化竞争力,是整个业务的盈利来源和基石,如果核心域不存在,那么整个业务就不能运作。对于核心域,需要投入最优势的资源(包括能力高的人),和做严谨良好的设计。
  • 通用子域(Generic Subdomain): 该类问题在界内非常常见,所以很可能有现成的解决方案,通过购买或简单修改的方式就可以使用。
  • 支撑子域(Supporting Subdomain): 该类问题解决的是支撑核心域运作的问题,其重要程度不如核心域,又不属于通用子域,具备强烈的个性化需求,难以在业内找到现成的解决方案,需要专门的团队定制开发。

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

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

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

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

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

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

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

三. 战术设计

3.1领域建模

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

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

3.2 业务服务识别

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

  • 基于限界上下文进行拆分: 每个限界上下文为一个服务,优点是每个服务都很小,代码量少;缺点是拆分粒度太细,导致服务数量过多,增加架构设计的复杂度和运维成本。
  • 基于子域进行拆分: 每个子域为一个服务,优点是服务数量相对较少,架构复杂度和运维成本相对更低;缺点是拆分粒度在某些场景下会非常大,导致单个服务变成“小单体”,增加开发成本和代码分层复杂度。

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

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

子域

服务

预算管控子域

库存服务

产品账服务

3.3 业务服务接口识别

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

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

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

子域

聚合根/实体

接口能力

读写

账上下文

账单

记账

账本

单主体单产品单账期查账

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

库存上下文

库存

创建库存

扣减库存

缩扩容库存

查询库存

四. 技术实现

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

包括且不限于:

  • 架构风格选择,单体,soa,微服务,restful,rpc,webservice,ODATA等
  • 架构模式选择,传统分层,六边形,简洁,洋葱等
  • 补全组件,如rpc客户端,mtop,gatway,acl等,这里要分清应用层,,基础设施和领域
  • 技术框架选型,技术栈,服务治理体系
  • API设计,openapi,swagger,blueprint等
  • 领域模型类设计,参考领域模型设计类图
  • 持久化选择,这里要考虑哪些需要存储RDB,哪些用Nosql,哪些只需要内存中。在上例产品账中的账本就 不需要持久化
  • 应用层 设计模式选择,因应用需要,或运营策略需要支持能力要考虑合适的模式支持
  • 考虑其他需求的实现,易测试性,性能,易维护和运维,安全等

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

 

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

五. 总结

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

目录
相关文章
|
2月前
|
新零售 供应链 大数据
良久团购项目系统开发|方案设计|详情模式
新零售是在互联网技术深刻影响了人们的生产和生活之后,基于行业上下游现状衍生出来的全新模式
|
9月前
|
供应链 NoSQL Redis
库存预占架构升级方案设计 - 交易库存中心
伴随物流行业的迅猛发展,一体化供应链模式的落地,对系统吞吐、系统稳定发出巨大挑战,库存作为供应链的重中之重表现更为明显。近三年数据可以看出:
123 0
|
4月前
|
安全
外贸订单管理的管理要点:流程、准确性、跟进、合理安排资源
在外贸公司的订单管理中,建立订单管理流程是至关重要的。一个完善的流程可以帮助公司更好地管理订单,提高工作效率,确保订单的准确性和及时交付。
104 2
|
5月前
|
数据库 网络架构 UED
支付设计白皮书:支付系统的路由系统设计
支付设计白皮书:支付系统的路由系统设计
102 1
|
存储 消息中间件 JavaScript
支付设计白皮书:支付系统的对账系统设计
支付设计白皮书:支付系统的对账系统设计
|
新零售 存储 供应链
严选库存中心设计实践
严选库存中心设计实践
267 0
|
消息中间件 前端开发 JavaScript
支付设计白皮书:支付系统的总架构
支付设计白皮书:支付系统的总架构
|
存储 监控 供应链
聊聊「订单」业务的设计与实现
订单业务一直都是系统研发中的核心模块,订单的产生过程,与系统中的很多模块都会高度关联,比如账户体系、支付中心、运营管理等,即便单看订单本身,也足够的复杂;
11292 3
聊聊「订单」业务的设计与实现
|
XML JSON 分布式计算
如何设计财务对账系统 —— 从0到1搭建对账中心实战
卡拉云快速搭建企业内部对账系统
4906 3
如何设计财务对账系统 —— 从0到1搭建对账中心实战
|
微服务
微服务项目:尚融宝(41)(核心业务流程:借款额度审批)
列表的结果需要关联查询,数据字典的数据也需要展示对应的文本内容而不是值,除了定义VO的方式,我们也可以使用扩展实体类的方式
微服务项目:尚融宝(41)(核心业务流程:借款额度审批)