利用聚合概念指导MongoDB的Schema设计

本文涉及的产品
云数据库 MongoDB,通用型 2核4GB
简介: 利用聚合概念指导MongoDB的Schema设计

习惯的力量强大却往往无法察觉。往往不经意之间,陷入习惯的陷阱中却不自知。

在我们的项目中,为了能够保存分析报表以及用户设置的报表查询条件,我们将这些信息视为报表元数据存储在MongoDB中。要存储的元数据包括:

  • 报表分类(ReportCategory)
  • 报表(Report)
  • 报表查询条件(QeuryCondition)


一个报表分类会包含多个报表,同一个报表只能属于一个分类。每个报表提供了多个标准查询条件和多个用户自定义查询条件。


我需要为这些元数据设计MongoDB的DB Schema。最初考虑将这三个概念合起来定义为元数据表的一条记录。之后想到对于一个报表而言,需要频繁对报表的查询条件进行增删操作,似乎又应该将查询条件单独分离出来。那么报表分类与报表呢?是否将报表也独立出来才合适?对于MongoDB这样的Document数据库而言,将Report作为ReportCategory的embedded属性也是可行的,至少不会像关系型数据库那样会产生数据冗余。倘若要分开,当需要查询某个分类下的所有报表时,还得多余地做一次Link。

好纠结啊!似乎怎么设计都是可行的,又仿佛总有不如意处。

正在思索中,突然想起对于这样面向文档的NoSQL数据库而言,使用聚合(Aggregate)来观察表记录会更加恰当。这个想法恍若闪电般迅捷而锐利,猛地撞向脑中的思绪,一下子点燃了我的设计思维。

这里所谓“聚合”,非面向对象中表达对象关系的概念,而是领域驱动设计(DDD)对对象边界的思考。关于聚合(Aggregate)的设计,我根据过往的经验,整理出五条设计原则:

  • 聚合作为一种边界,主要用于维护业务完整性,此时应遵循业务规则中定义的不变量(Invariant)
  • 作为聚合边界内的非聚合根实体对象,若可能被别的调用者单独调用,则应该作为单独的聚合分离出来
  • 在聚合边界内的非聚合根对象,与聚合根之间应该存在直接或间接的引用关系,且可以通过对象的引用方式;若必须采用Id来引用,则说明被引用的对象不属于该聚合
  • 若一个对象缺少另一个对象作为其主对象就不可能存在,则该对象一定属于该主对象的聚合边界内
  • 若一个实体对象,可能被多个聚合引用,则该实体对象应首先考虑作为单独的聚合


这些设计原则都是我在探索聚合设计时的一些思考,多次实践下来,窃以为颇有指导价值。这里不再铺开,留待以后的文章详述。单说本例,我们该如何运用这些原则来思考ReportCategory、Report与QueryCondition之间的关系?

显然,套用这些原则,我认为前面纠缠不清的混乱思路已可迎刃而解。从业务完整性看,Report虽属于ReportCategory,但二者未尝有强的约束关系,即不存在业务上的不变量(Invariant)。例如ReportCategory可以没有Report,成为一个空的分类,我们也可以撇开ReportCategory,单独查询所有的Report。倘若我们将Report放到ReportCategory聚合中,由于Report可能会被单独调用,聚合的边界保护反而成为了障碍,不合理。

于是,我们可以得出第一个结论:ReportCategory和Report应该属于两个不同的聚合。

基于第四条原则,我们可以提出问题:当QueryCondition缺少Report对象后,还有存在意义吗?答案一目了然,没有Report,就没有QueryCondition。皮之不存毛将焉附!第二个结论自然得来:Report与QueryCondition应属于同一个聚合。于是,模型呼之欲出:

image.png

上图是领域模型而非数据模型。站在领域驱动设计的角度,这才是正确的打开姿势。那么,使用该领域模型去指导MongoDB的Schema设计,是否有将领域混入技术实现之嫌呢?从设计方向看,先考虑领域模型才是正解,DB的技术实现应为了满足该领域模型而设计。只有当领域模型可能阻碍技术实现,又或者依据领域模型得到的Schema设计不满足性能或其他质量属性需求时,才应该反过来调整领域模型。对于MongoDB这种面向Document的数据库,以聚合概念指导Schema设计,可谓水到渠成,不仅没有违和之感,反而让Repository的实现变得更加简单、自然。

在项目开发过程中,我先入为主地做了技术选型,从而习惯性地开始针对MongoDB进行Schema设计,反而忘了领域驱动设计的指导原则。技术人员对技术实现往往见猎心喜,因而忽略了领域设计的驱动力,慎之慎之!

本文链接: http://zhangyi.xyz/mongodb-schema-design-using-aggregate/

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。   相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
1天前
|
存储 NoSQL MongoDB
【MongoDB】如何在MongoDB中设计Schema?
【4月更文挑战第2天】【MongoDB】如何在MongoDB中设计Schema?
|
1天前
|
存储 NoSQL MongoDB
阿里云 Flink 原理分析与应用:深入探索 MongoDB Schema Inference
本文整理自阿里云 Flink 团队归源老师关于阿里云 Flink 原理分析与应用:深入探索 MongoDB Schema Inference 的研究。
46953 2
阿里云 Flink 原理分析与应用:深入探索 MongoDB Schema Inference
|
存储 NoSQL 分布式数据库
|
1天前
|
存储 NoSQL MongoDB
MongoDB如何创建数据库
MongoDB如何创建数据库
|
1天前
|
存储 NoSQL 关系型数据库
Percona XtraBackup是否支持MongoDB数据库备份?
【5月更文挑战第13天】Percona XtraBackup是否支持MongoDB数据库备份?
18 1
|
1天前
|
存储 NoSQL MongoDB
MongoDB数据库转换为表格文件的Python实现
MongoDB数据库转换为表格文件的Python实现
15 0
|
1天前
|
存储 NoSQL 关系型数据库
【MongoDB 专栏】MongoDB 与传统关系型数据库的比较
【5月更文挑战第10天】本文对比了MongoDB与传统关系型数据库在数据模型、存储结构、扩展性、性能、事务支持、数据一致性和适用场景等方面的差异。MongoDB以其灵活的文档模型、优秀的扩展性和高性能在处理非结构化数据和高并发场景中脱颖而出,而关系型数据库则在事务处理和强一致性上更具优势。两者各有适用场景,选择应根据实际需求来定。随着技术发展,两者正相互融合,共同构建更丰富的数据库生态。
【MongoDB 专栏】MongoDB 与传统关系型数据库的比较
|
1天前
|
存储 NoSQL 关系型数据库
MongoDB非关系型数据库实战
【5月更文挑战第6天】MongoDB,流行的NoSQL数据库,以其灵活的数据模型和高性能备受青睐。本文介绍了MongoDB的基础,包括文档型数据库特性、安装配置、数据操作。通过电商订单管理的实战案例,展示了MongoDB在处理复杂数据结构和大规模数据时的优势,适用于电商、游戏、视频直播等场景。MongoDB的索引、全文搜索和地理空间功能进一步增强了其实用性。注意性能优化和扩展性以确保系统稳定性和可靠性。
|
1天前
|
弹性计算 NoSQL Shell
一键安装 MongoDB 数据库脚本
【4月更文挑战第29天】
19 4
http://www.vxiaotou.com