From 3ae07c38f7582340b6cea71dc2b7b9dc9084b202 Mon Sep 17 00:00:00 2001 From: xeruf <27jf@pm.me> Date: Wed, 10 Apr 2024 15:33:55 +0200 Subject: [PATCH] Update Readme and Diagrams --- README.md | 16 +++-- ...eware.plantuml => compareware-erd.plantuml | 30 +++++---- compareware-erd.png | Bin 0 -> 22494 bytes compareware-nf.plantuml | 62 ------------------ 4 files changed, 30 insertions(+), 78 deletions(-) rename compareware.plantuml => compareware-erd.plantuml (63%) create mode 100644 compareware-erd.png delete mode 100644 compareware-nf.plantuml diff --git a/README.md b/README.md index 48bf11d..6e81813 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,14 @@ - `Items` can have `Tags`: Implemented ### To Implement -- Authenticated `Users` can create `Reviews` referencing one or more `Items` and `Tag` them (for example with a URL linking to a more in-depth review) -- `Users` can trust (the reviews of) other users, creating a web of trust +- Authenticated `Users` can write `Reviews` referencing one or more `Items` and `Tag` them (for example with a URL linking to a more in-depth review) +- `Users` can trust (the reviews of) other users, + creating a web of trust ## Database Design -`Tags` are Key-Value like in [OpenStreetMap](https://wiki.openstreetmap.org/wiki/Tags) or Wikidata, +`Tags` are Key-Value like in [OpenStreetMap](https://wiki.openstreetmap.org/wiki/Tags) or Wikidata, that is why they need own tables. -Initially I planned having a shared `Tags` table for both `Reviews` and `Items`, +Initially I planned having a shared `Tags` table for both `Reviews` and `Items`, but that would have been impractical because a Tag either belongs to a Review or an Item, so two separate tables would have needed to been set up to track the referenced object, with a risk of anomalies: @@ -23,6 +24,13 @@ Considering the simplicity of a Tag, creating two separate tables prevents those cases without overhead and normalizes the schema. +![CompareWare Overview Entity Relationship Diagram](compareware-erd.png) + +Of course, every many-to-many relationship needs another table +to be represented in a normalized well, +which the diagram of the actual structure used in the code illustrates: + +TODO ## Developer Setup diff --git a/compareware.plantuml b/compareware-erd.plantuml similarity index 63% rename from compareware.plantuml rename to compareware-erd.plantuml index de45e24..e81366a 100644 --- a/compareware.plantuml +++ b/compareware-erd.plantuml @@ -8,15 +8,8 @@ left to right direction entity User { Id: Autoumber -- - Name - Pass -} - -entity Tag { - Id: Autoumber - -- - Key: String - Value: String + (UNIQUE) Name: String + Pass: String } entity Item { @@ -28,13 +21,26 @@ entity Review { Id: Autoumber -- (FK) AuthorId - Text + Text: String } +entity ItemTag { + Id: Autoumber + -- + (INDEX) Key: String + Value: String +} + +entity ReviewTag { + Id: Autoumber + -- + (INDEX) Key: String + Value: String +} User ||--o{ Review::AuthorId: Writes -Item --o{ Tag -Review --o{ Tag +Item ||--o{ ItemTag +Review ||--o{ ReviewTag Item }|--o{ Review User }|--|{ User: Trusts diff --git a/compareware-erd.png b/compareware-erd.png new file mode 100644 index 0000000000000000000000000000000000000000..736f4b93909522635b1dde34dc581a10f83253f2 GIT binary patch literal 22494 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 T@#uFd{~N(U*q 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