一、前言
这两天在用drawio整理《挖机报价系统》ER图,顺便就写了这篇很基础的文章,一个具有良好设计的数据库才能让程序实现更简单,系统可维护性更好。
关于领域驱动设计还是数据驱动设计哪个好,这个我很难讲得清楚,可能是我自己对其不太了解,领域驱动设计大约在2007年左右就开始流行起来的吧,它一切都围绕着领域知识进行建模,不考虑存储方式,当时有同事拿着一本大部头的领域设计的书在啃,我一脸崇拜地看着他,后来我转行做客户端开发,后面又在小公司就一直没去搞领域设计这套玩意了,有时间要好好学习一下。
二、数据库设计理论基础
以数据驱动的设计,一切从数据存储方案出发,关注数据如何存储,如何查询,如何进行实体关系建模。
在做系统设计时要了解系统需要存储哪些数据,实体与实体之间的对应关系是怎样的(一对一、一对多、多对多),实体包含哪些属性,哪些属性或属性的组合能唯一标识一个实体。
数据库设计一般要遵循数据库设计范式。
1、第一范式(1NF)
数据库表中所有字段都是单一属性,要求数据库表的每一列都是不可分割的原子项,比如一张学生表中 需要记录家庭信息,如果家庭信息存放【3口人,北京】这就不符合第一范式设计原则,可以将其拆家庭信息字段拆分成 家庭人口,户籍 两个字段。
注:其实在定义一个字段时最担心的是该字段含义不明确,很多时候新手会认为我节省了一个字段,但是这样会把系统搞得特别复杂难以维护。
2、第二范式(2NF)
数据库表中每一列都应该与主键(业务主键)相关,而不能只与主键某一部分相关(主要针对联合主键而言
如上图所示情况一个报价单中可能包含不同的产品,因此主键必须是 报价单编码和产品编码联合组成,单价与数量都与 联合主键相关,但订单总金额仅与报价单编码相关,这样就不满足第二范式,需要将其拆分成两个表
3、第三范式(3NF)
数据库表中每一列都和主键直接相关,而不能间接相关。
这里供应商编码依赖于产品编码,但供应商名称、供应商邮件地址并不直接依赖于产品编码,而是依赖于供应商编码,这样就应该拆成两个实体。
4、反范式
反范式是通过增加冗余数据或通过分组数据来优化数据库读取性能的过程,反范式场景如下
4.1 增加冗余列
严格遵循范式设计在查询数据时往往需要多表关联,会造成很多IO,查询会比较慢,这时可以适当地增加冗余字段减少多表关联查询。比如我在报价单表中加上客户的名称、邮箱、地址、联系人等字段,这样在查询报价单时候就不用去关联客户表了。另外还有做数据快照的,可以使用一个JSON串把数据存在一个字段中。冗余字段一个比较头大的问题是数据一致性难维护。
4.2 增加派生列
增加派生列指增加的列可以通过表中其他数据计算生成。它的作用是在查询时减少计算量,从而加快查询速度,比如把count或sum结果放在新增的一列中。
4.3 重新组表
重新组表指如果许多用户需要查看两个表连接出来的结果数据,则把这两个表重新组成一个表来减少连接而提高性能,比如各种统计表、中间表等。
4.4 分割表
比如像用户表字段比较多,我就可以做垂直拆分,拆成用户登录表、用户信息表、如果数据量比较大的,我们就可以做水平拆分,根据uid取模分成N个表,这也是反范式的设计。
三、挖机报价系统数据表设计
1、基础产品
产品分类表:类目层次分成三或四层,第一层类目分为 主机、附产品、零件、服务。
产品明细表:与产品当前是一对一关系,主要是考虑当前供应商都是厂家,如果供应商是第三方机构,就是一对多关系。
产品Feature表:指的是挖机的履带或其他特征,比如履带可以有钢履带,橡胶履带,与产品关系是多对一关系。
2、库存
关于库存,需求上其实是要管理 在一个仓库中每个产品总数量是多少,多少个(台)是被预定的,多少个是已经采购了正在途中,多少是二手的,多少是破损的。对于主机需要知道这台主机在哪个仓库哪个区位(仓库比较大),对于零件还需要知道是在哪个货架上。
基础信息表:仓库、产品、仓库区位货架信息。
库存相关:仓库分仓库存表,记录了一个产品在哪个仓库,数量是多少。产品区位货架表,记录了零件在仓库哪个区域的哪些货架上,仓库区位序列号表则记录了具体某台主机在仓库哪个区域。
入库出库表:涉及到直接出入库以及通过采购入库或通过合同出库。
3、报价
一个客户可以创建多条线索,一条线索可以进行多次报价,一个合同只能生成一张暂定发票。这里的报价和合同开始是想设计成两张表,后来需求方说报价就是合同,然后又改口了,说报价和合同还不太一样。。。
4、日志
事件:指的是与客户进行销售前相关的事情,比如创建线索、创建报价、拒绝报价、生成暂定发票。一个主干事件都可以与客户进行沟通,沟通信息记录在沟通日志表中。
日历:确定了下次联系时间,到期会通过工作日历进行提醒。
待办任务:未完成的日历任务会进入待办,一些超时任务比如线索超过多长时间没有处理、给客户进行了报价但超过一定时间没有后续动作都会进入待办。
5、财务
一个采购单可以做多笔应付款,一个合同可以做多笔应收款,一个采购单明细或合同明细会对应多个应收应付明细,这是因为在对零件做应付应收时,如果一次采购一个零件100件,但供应商可以开两个发票,一个发票40个,另外一个发票60个。
6、商品汇总表
主要记录一个主机生命周期的所有信息,从采购、发货、入库、报价、出库、财务信息都会在该表有完整的记录。
7、其它表
8、数据报表(略)
注:做表设计第一步就是从需求中把实体抽象出来,然后理清实体之间的对应关系,每一个实体的主键(指的是逻辑业务主键,能够唯一确定实体的属性),然后再具体对每个实体属性进行定义,特别是要关注的是状态字段,状态字段涉及到流程和实体之间的转换,然后尽量按数据库3范式设计,再考虑查询性能及便利性进行反范式设计,表结构设计好后可以不先进行代码开发,可以写好各种SQL语句进行业务流程模拟验证。