在本期中,边肖将向您介绍如何使用聚合概念来指导MongoDB的Schema设计。文章内容丰富,从专业角度进行分析和叙述。看完这篇文章,希望你能有所收获。
习惯很强大,但往往难以察觉。常常不经意间,不知不觉就陷入了习惯的陷阱。
在我们的项目中,为了保存用户设置的分析报告和报告查询条件,我们将这些信息作为报告元数据存储在MongoDB中。要存储的元数据包括:
报告分类(报告类别)
报告
查询条件
一个报表类别包含多个报表,同一报表只能属于一个类别。每个报表提供多个标准查询条件和多个自定义查询条件。
我需要为这些元数据设计蒙古数据库的数据库模式。首先,我们考虑将这三个概念一起定义为元数据表的记录。之后,对于一个报表,需要频繁的增加或者删除报表的查询条件,似乎查询条件要分开。报表分类和报表怎么样?把报告分开合适吗?对于MongoDB这样的Document数据库,将Report作为ReportCategory的嵌入属性也是可行的,至少不会像关系数据库那样产生数据冗余。如果要分开,当需要查询某个类别下的所有报表时,就要做一个冗余的Link。
好纠结啊!似乎所有的设计都是可行的,似乎总有一些失望。
想着,我突然想到,对于这样一个面向文档的NoSQL数据库,使用聚合来观察表记录会更合适。这个想法似乎闪电般的快速和尖锐,猛烈地冲击在我的脑海中,突然点燃了我的设计思维。
这里所谓的“聚合”不是面向对象中表达对象关系的概念,而是领域驱动设计(DDD)对对象边界的思考。关于骨料的设计,根据我过去的经验,我整理了五个设计原则:
聚合作为一种边界,主要用于维护业务完整性,此时应该遵循业务规则中定义的不变量。
作为聚合边界内的非聚合根实体对象,如果它可能被其他调用方单独调用,则应该将其作为单独的聚合进行分离。
聚合边界内的非聚合根对象与聚合根之间应该有直接或间接的引用关系,可以采用对象的引用方式。如果必须使用Id进行引用,则表示被引用的对象不属于聚合。
如果一个对象在没有另一个对象作为其主对象的情况下不能存在,则该对象必须属于主对象的聚合边界。
如果一个实体对象可能被多个聚合引用,则应该首先将该实体对象视为一个单独的聚合。
这些设计原则是我在探索聚合设计时的一些思考。经过多次实践,我暗暗认为它们具有指导价值。这里不展开,但会在以后的文章中详细介绍。仅在这种情况下,我们应该如何使用这些原则来思考报告类别、报告和查询条件之间的关系?
显然,应用这些原则,我认为前面纠结的困惑是可以解决的。从业务完整性的角度来看,虽然报表属于ReportCategory,但两者之间并不存在强约束关系,即不存在业务不变量。例如,报告类别可以变成没有报告的空类别,或者我们可以将报告类别放在一边,单独查询所有报告。如果把报表放到ReportCategory聚合中,因为报表可能会单独调用,所以聚合的边界保护就成了障碍,不合理。
因此,我们可以得出* * *结论:报告类别和报告应该属于两个不同的聚合。
基于第四个原则,我们可以问这样一个问题:当QueryCondition缺少Report对象时,它有意义吗?答案一目了然,没有报表就没有查询条件。如果你不保持你的皮肤,你会失去它!第二个结论自然而来:报表和查询条件应该属于同一个聚合。因此,这个模式呼之欲出:
上图是一个领域模型,而不是数据模型。从领域驱动设计的角度来看,这才是正确的开放姿态。那么,如果用领域模型来指导MongoDB的Schema设计,是不是怀疑领域混入了技术实现?从设计方向来看,首先考虑领域模型是正解,DB的技术实现应该设计成满足领域模型。只有当领域模型可能阻碍技术实现,或者根据领域模型得到的Schema设计不满足性能或其他质量属性的要求时,才需要依次调整领域模型。对于一个面向文档的数据库MongoDB来说,用聚合的概念来指导Schema设计是理所当然的事情。相反,它使存储库的实现更加简单和自然。
在项目开发过程中,我对技术做了先入为主的选择,开始习惯性地为MongoDB设计Schema,却忘记了领域驱动设计的指导原则。技术人员往往以技术的实现为乐,从而忽略了领域设计的驱动力而谨小慎微!
这就是上面提到的边肖为大家分享的关于如何使用聚合概念来指导MongoDB的Schema设计的内容。如果有类似的疑问,我们可以参考上面的分析进行理解。想了解更多,请关注行业信息渠道。
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/67453.html