15

DDD 中的多对多关系建模

 3 years ago
source link: http://printf.cn/index.php/archives/ddd-many-to-many-relation-1.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

DDD 中的多对多关系建模

多对多关系是软件建模中比较的麻烦的场景,如果梳理不清楚对软件架构伤害很大。在不久前的一个项目中,十足的体验了一次多对多关系带来的痛苦。

我们的项目有是一种多空间模型,也就是用户可以处于不同的空间,在不同的空间中可以访问空间中的资源。一个空间可以拥有多个用户,用户可以出现在多个空间中。看起来和编程老师在数据库课程中的多对多关系没有区别。

image-20210519085032849

对于数据库来说,多对多关系需要一个中间表,于是团队对数据建模“规范”的把中间表起名为 “workspace_user_relation”,得到的数据库 E-R 模型如下。

image-20210519085340362

团队使用了 JPA 的 @ManyToMany 注解,导致 workspace 和 user 两个对象无时不刻在一起了。另外,通过 user 可以操作 workspace,通过 workspace 也可以获得 user。

这种设计,不仅在技术上实现困难,对业务的支持也不足。

  1. 用户加入到空间中具有权限,通过这种方式比较难管理。
  2. 空间管理员并没有对用户的修改权利,只有对用户加入、退出、访问空间资源的权利,这种设计诱导了业务提出不合理的需求。比如空间管理员对用户的禁用,其实只是对用户参与到空间中的行为禁用,而非对用户禁用。
  3. 关系表中的创建时间的含义是用户加入空间的时间,使用中间表语义不明显。

在很多编程指南和规范中,都有写明不允许使用多对多关系。在一些框架中,虽然实现了多对多关系,但是往往不推荐使用。

因为我们在开始学习编程的阶段中,接受了数据库的关系理论。数据库关系理论是 1969 被英国计算机科学家 Edgar Frank "Ted" Codd 首次提出。关系数据库理论继承了集合论的的思想,在处理数据上有独特优势,被广泛使用。关系数据库理论可以做到降低冗余,提高一致性的能力。

关系模型被用来存储数据、处理数据非常好用。但是,面向对象作为一种流行的编程模型,它是用来模拟现实业务的。面向对象构想的信息结构是树形,而关系模型是集合。

它们有一个天然的鸿沟,就是这两种结构如何转化的问题,因此出现了大量 ORM(对象关系映射) 软件来试图解决这个问题。数据库中的普通关系(一对一、一对多)可以使用面向对象中的 “组合” 来映射,但是多对多关系却极难被处理,这也是一些框架不建议使用的原因,但往往难以说明其中的道理。

其中的道理是什么呢?因为,关系模型中的多对多“关系”,映射到面向对象在本质是一个“隐藏的模型”。

我们用认识论中的主体-客体思维来看待这个问题,主体-客体可以让认识问题变得更深入。主体是有认识能力和实践能力的人,或者,是在社会实践中认识世界、改造世界的人。客体是实践和认识活动所指向的对象,是存在于主体之外的客观事物。在业务系统中,我们可以把 Controller、Service 这类带有行为能力的对象看做拟人化的主体,而 Entity、Model 看做客体。

回到上面的例子,对于工作空间、用户而言,当把用户加入工作空间的时候。我们发生三步行为:

  1. 使用了用户信息、工作空间的信息,这一步用户、工作空间都是被感知的客体。
  2. 创建了一个关系“工作空间-用户”,这一步“工作空间-用户”是客体。
  3. 把这个关系加入到工作空间,扩充了工作空间的信息,这一步工作空间是客体。

问题的关键是我们往往没有找到一个好的名词来描述“工作空间-用户”这个概念,一旦这个概念被明确下来,我们的模型就清晰了,多对多关系就不存在了。

举例来说,我么可以给“工作空间-用户”找到如下的名字:

真真实的例子中,我们使用了空间成员来作为这个隐藏模型的名字,因此空间和用户的关系被拆解为 “空间拥有多个成员” 和 “成员可以引用用户” 两个关系。

大部分的多对多关系都可以通过这种方法消除,不过,除了起名字这个难题外,还有另一个问题。

多出来的这个隐藏模型和谁走?我们使用一个例子来说明这个问题。

在很多系统中,我们都需要使用 “标签”,而标签和特定的资源都是多对多关系。明白上面说的逻辑后,我们把标签存在于某个资源中的关系叫做 “标签项”。但是,如果同时有多个资源都需要使用便签,标签项跟谁走呢?

如果所有类型的标签都跟着标签走的话,可以做出一种通用的标签系统。其结果类似于搜索系统了,通过标签系统处理所有的业务。这样设计会使聚合搜索带来便利,但是标签在具体业务中的使用变的困难。

如果标签跟随具体的业务走,那么隐藏的中间模型就是具体的业务中的一个概念,比如文章专题中的标签、文章中的标签。通过这样的处理,可以让系统解耦良好。不过,代价是聚合搜索能力需要额外的技术来实现。

image-20210523171440118

这个例子充分说明了模型的建立需要为业务服务,业务人员往往需要明确其业务重心,并做出一些权衡和取舍才能设计出合适的模型。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK