数据库模型与域模型设计冲突带来的巨坑

前阵子有个项目临时有个需求是要对某些接口进行AOP,最后将结果进行数据库的存储,该数据表为空白表,具体由开发进行初版设计。
在描述问题之前,先说一个很重要的结论:无论是自上而下或者是自下而上的开发习惯,对于域模型和数据库模型之间的冲突都要尽量避免。
怎么避免呢?
个人领悟:在需要被设计的位置尽量的扁平化
这句话怎么理解呢?
以这次需求为例子,被AOP的接口本身的域模型和相关的表结构已经是稳定的状态。
假定本次相关联的主要数据模型是一个用户表,用户表本身通过is_parernt字段来判断是否是父账号,通过is_channel来判断该账号的渠道等级,is_chananel = 1 时表示该主账号可以有下级账号,该数据表的结果大体如下图:
数据库模型与域模型设计冲突带来的巨坑
上面的数据模型中并不依赖另外一张表进行用户之间的关联,因为彼此上下级的用户关系为1:1。那么对于 is_channel = 0 的帐号来说,他的表存储字段为:
id 账户Id
is_channel 下级帐号标识 = 0
is_parent 是否是父帐号 = 0
parent_id 上级帐号/父帐号 = 上级帐号id
同时域模型的设计则只有一个User类,没有特殊的1:n的映射DTO

那么具体已存在的模型设计已经讲解完成,现在讨论一下业务需求(根据订单状态创建订单副本数据):
①该类接口为订单类接口,这里不考虑订单接口的模型,因为这里的AOP对订单本身的模型侵入不大
②根据订单状态进行选择性的加工数据,具体条件判断如下:
a)状态变更为0时,通知当前用户所在的主账号以及其下面的所有子账号
b)状态变更为1时,通知该订单创建用户所在的上级帐号及其子帐号(订单只能由下级帐号创建)
c)状态变更为2时,通知该订单创建用户所在的下级帐号及其子帐号
d)状态变更为3时,清理相关的订单副本数据
e)变更的内容对客户端进行通知(使用的websocket)
f)上面的状态2和3的需求为后期需求变更部分(巨坑伏笔),这种"天灾"真的是给技术设计不合适带来巨大的麻烦啊,总之还是自己太菜

那么说一下业务设计和技术实现
Firstly,是失败的思路,需求变更之前(调研不清晰的结果,太着急写代码了):
1)因为上面的用户表设计,通过Is_channel来进行上下级的判断,为了匹配这种设计,我在订单副本的数据中进行的类似的设计,因为这样可以一定程度上达到1:1的原则,那么我的表设计字段如下:
order_id 订单Id
user_id 用户Id
user_status 用户处理状态
parent_id 父帐号id
parent_status 父帐号处理状态
2)针对上面的数据表模型,域模型方面没有什么特别的额外属性需要处理。嗯,目前看起来还不错,根据表结构和业务处理我只需要进行如下的逻辑代码开发就完事:
a)某个订单发生了变化,依赖订单状态,获得需要通知的用户帐号,得到一个Object,该用户如果是父账号,则不需要加工user_id字段,如果是子账号,则同时加工parent_id字段。so easy 甚至还有个逻辑权限控制,舒服啊。
b)其他CRUD接口:
①子账号读取通讯获得的订单副本数据后,将该副本的处理状态进行更新。
②获取对应的订单数据,父账号就根据parent_id匹配的进行查询,子账号就根据user_id匹配的进行查询。
③订单状态处理完成,根据order_id进行批量删除
④轻轻松松,我依然是一个快乐的CRUD工程师

Second,需求变更之后
1)状态变更2,3之后要通知对应的上下级账号,及其所有子账号,那么对于域模型来说,这就不是1:1的关系了,我加工的时候根据order_id需要生成1:n的订单副本数据。那么需要改动的内容呢(笑容逐渐消失.jpg)???
a)AOP部分,实体加工方法处理的return type 从Object变更成List,对应的上下文处理逻辑需要变更。
b)实体加工方面,获得父id , 加工一条parentId非空数据,再获得子id ,加工n条件userId非空数据,结果返回一个List。最后saveAll存储。这么一看好像问题也不大,可是我要通知客户端啊,原本通过全局的Auth认证获得用户数据就可以通知,现在需要通知的对象变成了n个,那么这个处理就不合适了,冲突开始出现了,代码开始变得复杂丑陋了,这如何能忍?没办法,改!
c)首先进行AOP处理的逻辑分析:
①根据订单状态变更获得一个List的返回值,对于需要通知其所有子账户
②根据List进行用户id List的获取和数据库的存储
③根据用户id List进行通知
d)表结构方面,删除parent_id & parent_status字段
e)xml方面,删除根据parent_id进行处理的所有sql

最后根据上帝视角进行一下总结
数据库模型与域模型设计冲突带来的巨坑
所以在数据模型和域模型的持久化设计中,不要一味地追求1:1的关系,让你的设计在当前并且在未来的需求变更中更加的扁平化,让代码可以更健壮。
在这次的开发例子中,强关联的表是用户表,本身用户表通过is_channel实现表内关系的上下级关系,并根据is_parent进行父子账号的判断,对于这种设计的子账号间就是n:n的关系,那么在针对配合该表进行订单副本的需求加工时,某个单一子账号发起订单状态的变更关系时,其需要通知的账号(n个)又如何能通过一条记录来实现数据存储呢
所以在上面的这种情况中,数据表设计就应该以order_id+user_id进行唯一索引,那么接下来无论该订单怎么变更,需要通知的用户都是一个或以上,那么数据结构就是稳定的,针对于user_id + parent_id的这种因为临时需求而实现的1:1设计,单独使用user_id来实现1:n的关系更加扁平,也更加的健壮。