I\'m currently designing a database schema that\'s used to store recipes. In this database there are different types of entities that I want to be able to tag (ingredients, reci
I don't see anything wrong with having a single table for all tag assignments (as opposed to multiple tables - one for each taggable entity).
However, one important detail in your design remains ambiguous to me: if you are going to have something along these lines
- - - - - - - - - -
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
...
then are your entity classes going to have their own primary keys or are you going to use EntityOne.TaggableID
and EntityTwo.TaggableID
as de facto primary keys for EntityOne
and EntityTwo
?
In most general case, I would be cautious and let entities have their own IDs:
- - - - - - - - - -
EntityOne
ID // PK
Taggable_ID // FK -> Taggable.ID (Nullable)
...
- - - - - - - - - -
EntityTwo
ID // PK
Taggable_ID // FK -> Taggable.ID (Nullable)
...
This would not require each entity to have a corresponding instance of Taggable
and therefore this would not require every piece of code concerned with an entity to also be aware of tags. However, if tagging is going to be really ubiquitous in the system, and if you are sure that you won't need any other "common ancestors" for entities (that is, other than Taggable
), then you might get away without "intrinsic" IDs for entities.
NB: I never tried to implement anything like this, so all my recommendations are purely theoretical. So please do not shoot me if I do not see some obvious flaws. :-)
In response to Bill Karwin's comment:
You are right: the design described above does not prevent multiple entities to refer to same Taggable
. But:
Like I said, all depends on requirements. If we are sure that Taggable
is going to be the only "common ancestor" of entities, then it is okay to use Taggable_ID
FKs as PKs for entities. But, for example, what if some entities that happen to be "taggable" also have to be "watchable" (think notifications, notification schedules, etc.) or "whatever-able" :-)? Can we cut all those "abilities" off by tying any entity hard to Taggable
?
If you really want to have DB-level enforcement of one-taggable-one-entity constraint... AFAIK, there is at least one common way to do that without making FKs serve as PKs: by introducing "types" of taggables (which may be useful for some other functionality anyway).
Something along these lines would let us have a cake and eat it:
- - - - - - - - - -
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)
Of course, all this is more complicated than just having entities tied to taggables. But I was just trying to discuss what, in my humble opinion, should be considered in addition to the narrow picture provided by the original question.