我认为为所有标记分配使用一个表没有任何问题(与多个表相反 - 每个可标记实体一个表)。
然而,你设计中的一个重要细节对我来说仍然是模棱两可的:如果你打算在这些方面有所作为
- - - - - - - - - -
Tag
ID // PK
Name
...
- - - - - - - - - -
Taggable
ID // PK
...
- - - - - - - - - -
TagAssignment
Tag_ID // FK -> Tag.ID
Taggable_ID // FK -> Taggable.ID
...
- - - - - - - - - -
EntityOne
Taggable_ID // FK -> Taggable.ID
...
- - - - - - - - - -
EntityTwo
Taggable_ID // FK -> Taggable.ID
...
那么您的实体类是否将拥有自己的主键,或者您打算使用EntityOne.TaggableID 和EntityTwo.TaggableID 作为EntityOne 和EntityTwo 的事实上的主键?
在大多数情况下,我会谨慎并让实体拥有自己的 ID:
- - - - - - - - - -
EntityOne
ID // PK
Taggable_ID // FK -> Taggable.ID (Nullable)
...
- - - - - - - - - -
EntityTwo
ID // PK
Taggable_ID // FK -> Taggable.ID (Nullable)
...
这不会要求每个实体都有一个对应的Taggable 实例,因此这不会要求与实体相关的每一段代码也都知道标签。但是,如果标记将在系统中真正无处不在,并且如果您确定不需要任何其他实体的“共同祖先”(即,除了Taggable),那么您可能无需实体的“内在”ID。
注意:我从来没有尝试过实现这样的东西,所以我所有的建议都是纯理论的。所以如果我没有看到一些明显的缺陷,请不要拍我。 :-)
回应比尔·卡尔文的评论:
你是对的:上面描述的设计并没有阻止多个实体引用同一个Taggable。但是:
就像我说的,一切都取决于需求。如果我们确定Taggable 将成为实体的唯一“共同祖先”,那么可以使用Taggable_ID FKs 作为实体的PK。但是,例如,如果某些碰巧是“可标记”的实体也必须是“可观察的”(想想通知、通知计划等)或“随便什么” :-) 怎么办?我们可以通过将任何实体硬绑定到Taggable 来切断所有这些“能力”吗?
如果您真的想在 DB 级别强制执行一个可标记一个实体的约束... AFAIK,至少有一种常见的方法可以做到这一点,而无需将 FK 用作 PK:通过引入“类型”的可标记(无论如何这可能对其他一些功能有用)。
类似这样的东西可以让我们吃蛋糕:
- - - - - - - - - -
Taggable
ID // PK
Type
...
- - - - - - - -
Constraint: (ID, Type) is unique
- - - - - - - - - -
EntityOne
ID
Taggable_ID
Taggable_Type // Constraint: always = 'EntityOne'
...
- - - - - - - -
FK: (Taggable_ID, Taggable_Type) -> (Taggable.ID, Taggable.Type)
当然,所有这些都比将实体绑定到可标记对象要复杂得多。但我只是想讨论一下,在我的拙见中,除了原始问题提供的狭隘图片之外,还应该考虑什么。