本文通过实际业务需求场景建模案例,为读者提供一种业务模型向数据模型设计的方法论,用于指导实际开发中如何进行业务模型向数据模型转化抽象,并对设计的数据模型可用性、扩展性提供了建议性思考。通过文章,读者可以收获到业务模型向数据模型抽象可参考的一种方法论,并针对后期业务需求变化,尽可能降低模型调整或者模型推 a 倒重建的风险。本文可以重点关注建模实施流程,针对自己实际业务场景,不断抽象优化自己的数据模型。
一、背景
从研发人员的角度出发,技术更多的是为业务赋能,同时研发人员也可以通过业务模型设计来提升自己的技术,他们更多的是技术控,追求拥有更多的技术栈。不过今天不讨论具体的技术,准备换一种思维模式来分享下自己在业务开发中的一些经验,并结合实际案例来阐述针对业务场景进行数据建模的方法论。
开发人员在日常工作中,参与 PRD 评审、听产品经理讲述用户故事、提出各种需求。评审结束,一般会一股脑投入到设计开发,而数据库表设计就是其中不可或缺的一个过程。对于熟悉的业务模块,通过对需求分析,可以轻而易举的完成数据表设计,但对于非熟悉业务领域,可能会经过多轮 PRD 分析,整理一套数据表结构基础,然后对其追加字段,就完成了基础的数据模型设计。而在这个过程中,往往会感觉没有可以参考的理论,有时候甚至对设计的数据库表产生怀疑,不断考虑此设计是否符合业务、表结构设计后期是否具有通用性、表之间关系是否恰当可扩展等等。今天来谈些在实际业务开发中,针对数据建模的一些思考。
一个好的方法论一定是告诉你当你面对一个全新的业务场景或未知领域的时候,如何去独立分析和解决问题。
二、名词
** 领域:** 可以理解为传统软件需求分析中的业务场景对应的业务域,比如常见的电商、物流、运输等领域。
** 子域:** 领域的部分业务域,比如电商的部分订单、支付、库存等子域。
** 建模:** 业务域的映射和抽象。
三、思考
面向对象分析的设计思维模式:
图 1. 用户角度到开发角度思考
四、方法论
4.1 实施步骤
- 识别对象;
- 组织对象;
- 定义对象模型间关系;
- 完善模型细节(属性、状态);
- 领域模型到数据模型映射;
4.2 CASE 实践(社区团购 -- 预排线调度建模案例)
(1) PRD 需求描述
预排线系统从 OFC 系统获取团单数据:截单之前每天下午 OFC 推送一份当天需要预排线的数据出来,这些数据包括每个已经成团的团单(生产单)和截止到当前时间团单的商品数据,这里面包含当天已经取消的团单(即所有的商品数量都是 0)。同时在截单之后,OFC 会把截单后的团单数据再推送一次,里面包含当天已经取消的团单(所有的商品数量都是 0);
团单数据创建、更新、删除:如果下发的生产单号在预排线系统不存在,则创建团单信息;如果下发的生产单号在预排线系统存在,则更新下面单商品的数量、团单的收件地址、经纬度、团长 ID、姓名、电话等信息;如果有新增的商品则添加团单下的商品数据;如果更新的团单数量,其下面所有商品的个数都为 0,代表这个团单已经被取消,则逻辑删除这个团单,同时取消这个团单和对应线路的绑定关系;更新的商品数量都是更新的商品的当前数量,不会更新调度时的数量和实际的数量。
(2) 识别对象
Note:
- 复用或者修改已有模型(比如:运输需求、计划、询价单、对账单、财务账单等);
- 行业、公司内概念列表(比如:社区团购、分拣、调度、询价、计费等);
- 名词。
识别出的对象:
OFC 团单 单 预排线数据 生产单 商品 商品数量 预排线系统 团单收件地址 经纬度 团长 ID 姓名 电话 线路 商品当前数量 调度时的数量 实际数量
(3) 组织对象
Note:
- 一词多用、重复、歧义:归结为一个对象模型;
- 复数:students --> student 归结为一个对象模型;
- 属性:可以归结为对象模型的特征,不单独升级为一个对象,但特殊场景下,比如文章的分类可以为文章的一个属性,但是当分类又有子属性时,比如有子属性标签,这时可以把分类单独升级为对象模型。类似设计数据库表,是设计为字段还是新设计一张表一样。
分析对象:
- OFC :系统
- 团单:生产单 单 团单收件地址 经纬度 团长 ID 姓名 电话
- 预排线:预排线系统,预排线模型 线路
- 商品:商品 商品数量 商品当前数量 调度时的数量 实际数量
(4) 定义对象模型关系
Note:
- 外键
- 关系:一对一、一对多、多对多,关系传递
分析关系:
- "同时取消这个团单和对应线路的绑定关系" -----> 预排线包括多个团单,预排线 VS 团单 = one vs many
- "如果有新增的商品则添加团单下的商品数据" -----> 团单下有多个商品,团单 VS 商品 = one vs many
图 2. 模型间关系
(5) 完善模型信息(属性状态)
Note:
- 子域:模型对象的子对象信息,比如:店铺模型下的员工模型、地址库模型、员工薪资模型等。
- 属性:模型对象的特征表现
- 状态:状态机
- 边界:对象模型间交互部分,分清楚哪些属于 A 对象哪些是 B 对象
完善对象模型:
图 3. 对象模型 (模拟字段信息)
(6)领域对象到数据模型
Note:
- ** 派生:** 数据模型之间的一种关联、继承、映射出的一种关系。比如 “预排线模型” 中 “运输任务编码” 属性,属于调度域的模型属性,后期会与调度域系统产生关联关系,所以把运输任务编码作为 “预排线模型” 的一个派生属性。
- 冗余:①低级冗余(显示)-- 模型依赖外部模型属性字段显示使用,这时不用再关联查询或者通过 IO 获取;②高级冗余(计算)-- 比如外部模型有单价、数量属性且稳定不变,则模型可以依赖其计算结果,省去二次计算逻辑;
- ** 扩展表:**①数据模型的垂直拆分,减少大对象;②变更不是很频繁的字段可以放到扩展模型;
社区团购排线部分模型设计图:
图 4 终版数据模型图
五、扩展
5.1 领域模型设计阶段思考
** 对象:** 领域模型对象,上述案例分析到的对象模型;
** 功能:** 哪些业务功能划分到领域(微服务)或者子域(模块)里面
** 接口:** 服务应该暴露的接口能力;
5.2 方法论
动词 + 宾语 <----> 方法接口 + 对象
- 业务功能操作和实体对象分离,更容易进行微服务划分;
- 某个微服务应该暴露哪些接口;
- 领域上下文边界,接口应属于哪个领域提供。
图 5 领域模型调用关系图
五、结语
一个好的方法论一定是告诉你当你面对一个全新的业务场景或未知领域的时候,如何去独立分析和解决问题。