docs(merise): MLD relational schema diagrams (4 sub-domains) (#4)
This commit is contained in:
parent
32ff6a63ba
commit
79d8ad9985
9 changed files with 515 additions and 0 deletions
46
docs/merise/_diagrams/mld-catalogue.mmd
Normal file
46
docs/merise/_diagrams/mld-catalogue.mmd
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
erDiagram
|
||||||
|
category {
|
||||||
|
int id PK
|
||||||
|
varchar name UK
|
||||||
|
varchar slug UK
|
||||||
|
smallint display_order
|
||||||
|
tinyint is_active
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
int id PK
|
||||||
|
int category_id FK
|
||||||
|
varchar name
|
||||||
|
int price_cents
|
||||||
|
smallint vat_rate
|
||||||
|
tinyint is_available
|
||||||
|
smallint display_order
|
||||||
|
}
|
||||||
|
menu {
|
||||||
|
int id PK
|
||||||
|
int category_id FK
|
||||||
|
int burger_product_id FK
|
||||||
|
varchar name
|
||||||
|
int price_normal_cents
|
||||||
|
int price_maxi_cents
|
||||||
|
tinyint is_available
|
||||||
|
smallint display_order
|
||||||
|
}
|
||||||
|
menu_slot {
|
||||||
|
int id PK
|
||||||
|
int menu_id FK
|
||||||
|
varchar name
|
||||||
|
enum slot_type
|
||||||
|
tinyint is_required
|
||||||
|
smallint display_order
|
||||||
|
}
|
||||||
|
menu_slot_option {
|
||||||
|
int menu_slot_id PK,FK
|
||||||
|
int product_id PK,FK
|
||||||
|
}
|
||||||
|
|
||||||
|
category ||--o{ product : "category_id (RESTRICT)"
|
||||||
|
category ||--o{ menu : "category_id (RESTRICT)"
|
||||||
|
product ||--o{ menu : "burger_product_id (RESTRICT)"
|
||||||
|
menu ||--o{ menu_slot : "menu_id (CASCADE)"
|
||||||
|
menu_slot ||--o{ menu_slot_option : "menu_slot_id (CASCADE)"
|
||||||
|
product ||--o{ menu_slot_option : "product_id (RESTRICT)"
|
||||||
1
docs/merise/_diagrams/mld-catalogue.svg
Normal file
1
docs/merise/_diagrams/mld-catalogue.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 107 KiB |
59
docs/merise/_diagrams/mld-ingredients-stock.mmd
Normal file
59
docs/merise/_diagrams/mld-ingredients-stock.mmd
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
erDiagram
|
||||||
|
ingredient {
|
||||||
|
int id PK
|
||||||
|
varchar name UK
|
||||||
|
varchar unit
|
||||||
|
int stock_quantity
|
||||||
|
int stock_capacity
|
||||||
|
smallint pack_size
|
||||||
|
smallint low_stock_pct
|
||||||
|
smallint critical_stock_pct
|
||||||
|
tinyint is_active
|
||||||
|
}
|
||||||
|
product_ingredient {
|
||||||
|
int product_id PK,FK
|
||||||
|
int ingredient_id PK,FK
|
||||||
|
smallint quantity_normal
|
||||||
|
smallint quantity_maxi
|
||||||
|
tinyint is_removable
|
||||||
|
tinyint is_addable
|
||||||
|
int extra_price_cents
|
||||||
|
}
|
||||||
|
allergen {
|
||||||
|
int id PK
|
||||||
|
varchar code UK
|
||||||
|
varchar name
|
||||||
|
}
|
||||||
|
ingredient_allergen {
|
||||||
|
int ingredient_id PK,FK
|
||||||
|
int allergen_id PK,FK
|
||||||
|
}
|
||||||
|
stock_movement {
|
||||||
|
int id PK
|
||||||
|
int ingredient_id FK
|
||||||
|
enum movement_type
|
||||||
|
int delta
|
||||||
|
int order_id FK
|
||||||
|
int user_id FK
|
||||||
|
varchar note
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
int id PK
|
||||||
|
varchar name
|
||||||
|
}
|
||||||
|
customer_order {
|
||||||
|
int id PK
|
||||||
|
varchar order_number
|
||||||
|
}
|
||||||
|
user {
|
||||||
|
int id PK
|
||||||
|
varchar email
|
||||||
|
}
|
||||||
|
|
||||||
|
product ||--o{ product_ingredient : "product_id (CASCADE)"
|
||||||
|
ingredient ||--o{ product_ingredient : "ingredient_id (RESTRICT)"
|
||||||
|
ingredient ||--o{ ingredient_allergen : "ingredient_id (CASCADE)"
|
||||||
|
allergen ||--o{ ingredient_allergen : "allergen_id (RESTRICT)"
|
||||||
|
ingredient ||--o{ stock_movement : "ingredient_id (RESTRICT)"
|
||||||
|
customer_order ||--o{ stock_movement : "order_id (SET NULL, nullable)"
|
||||||
|
user ||--o{ stock_movement : "user_id (SET NULL, nullable)"
|
||||||
1
docs/merise/_diagrams/mld-ingredients-stock.svg
Normal file
1
docs/merise/_diagrams/mld-ingredients-stock.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 135 KiB |
72
docs/merise/_diagrams/mld-order.mmd
Normal file
72
docs/merise/_diagrams/mld-order.mmd
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
erDiagram
|
||||||
|
customer_order {
|
||||||
|
int id PK
|
||||||
|
varchar order_number UK
|
||||||
|
varchar idempotency_key UK
|
||||||
|
enum source
|
||||||
|
int acting_user_id FK
|
||||||
|
enum service_mode
|
||||||
|
enum status
|
||||||
|
int total_ht_cents
|
||||||
|
int total_vat_cents
|
||||||
|
int total_ttc_cents
|
||||||
|
datetime paid_at
|
||||||
|
datetime delivered_at
|
||||||
|
datetime cancelled_at
|
||||||
|
}
|
||||||
|
order_item {
|
||||||
|
int id PK
|
||||||
|
int order_id FK
|
||||||
|
enum item_type
|
||||||
|
int product_id FK
|
||||||
|
int menu_id FK
|
||||||
|
enum format
|
||||||
|
varchar label_snapshot
|
||||||
|
int unit_price_cents_snapshot
|
||||||
|
smallint vat_rate_snapshot
|
||||||
|
smallint quantity
|
||||||
|
}
|
||||||
|
order_item_selection {
|
||||||
|
int id PK
|
||||||
|
int order_item_id FK
|
||||||
|
int menu_slot_id FK
|
||||||
|
int product_id FK
|
||||||
|
varchar label_snapshot
|
||||||
|
}
|
||||||
|
order_item_modifier {
|
||||||
|
int id PK
|
||||||
|
int order_item_id FK
|
||||||
|
int ingredient_id FK
|
||||||
|
enum action
|
||||||
|
int extra_price_cents
|
||||||
|
}
|
||||||
|
user {
|
||||||
|
int id PK
|
||||||
|
varchar email
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
int id PK
|
||||||
|
varchar name
|
||||||
|
}
|
||||||
|
menu {
|
||||||
|
int id PK
|
||||||
|
varchar name
|
||||||
|
}
|
||||||
|
menu_slot {
|
||||||
|
int id PK
|
||||||
|
varchar name
|
||||||
|
}
|
||||||
|
ingredient {
|
||||||
|
int id PK
|
||||||
|
varchar name
|
||||||
|
}
|
||||||
|
|
||||||
|
user ||--o{ customer_order : "acting_user_id (SET NULL, nullable)"
|
||||||
|
customer_order ||--o{ order_item : "order_id (CASCADE)"
|
||||||
|
product ||--o{ order_item : "product_id (RESTRICT, polymorphic)"
|
||||||
|
menu ||--o{ order_item : "menu_id (RESTRICT, polymorphic)"
|
||||||
|
order_item ||--o{ order_item_selection : "order_item_id (CASCADE)"
|
||||||
|
menu_slot ||--o{ order_item_selection : "menu_slot_id (RESTRICT)"
|
||||||
|
product ||--o{ order_item_selection : "product_id (RESTRICT)"
|
||||||
|
order_item ||--o{ order_item_modifier : "order_item_id (CASCADE)"
|
||||||
|
ingredient ||--o{ order_item_modifier : "ingredient_id (RESTRICT)"
|
||||||
1
docs/merise/_diagrams/mld-order.svg
Normal file
1
docs/merise/_diagrams/mld-order.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 167 KiB |
61
docs/merise/_diagrams/mld-rbac.mmd
Normal file
61
docs/merise/_diagrams/mld-rbac.mmd
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
erDiagram
|
||||||
|
role {
|
||||||
|
int id PK
|
||||||
|
varchar code UK
|
||||||
|
varchar label
|
||||||
|
varchar default_route
|
||||||
|
enum order_source
|
||||||
|
tinyint is_active
|
||||||
|
}
|
||||||
|
user {
|
||||||
|
int id PK
|
||||||
|
varchar email UK
|
||||||
|
varchar password_hash
|
||||||
|
varchar pin_hash
|
||||||
|
varchar first_name
|
||||||
|
varchar last_name
|
||||||
|
int role_id FK
|
||||||
|
tinyint is_active
|
||||||
|
smallint failed_login_attempts
|
||||||
|
datetime lockout_until
|
||||||
|
datetime anonymized_at
|
||||||
|
}
|
||||||
|
role_visible_source {
|
||||||
|
int role_id PK,FK
|
||||||
|
enum source PK
|
||||||
|
}
|
||||||
|
permission {
|
||||||
|
int id PK
|
||||||
|
varchar code UK
|
||||||
|
varchar label
|
||||||
|
}
|
||||||
|
role_permission {
|
||||||
|
int role_id PK,FK
|
||||||
|
int permission_id PK,FK
|
||||||
|
}
|
||||||
|
audit_log {
|
||||||
|
int id PK
|
||||||
|
int actor_user_id FK
|
||||||
|
int actor_role_id FK
|
||||||
|
varchar action_code
|
||||||
|
varchar entity_type
|
||||||
|
int entity_id
|
||||||
|
varchar summary
|
||||||
|
json details
|
||||||
|
datetime created_at
|
||||||
|
}
|
||||||
|
login_throttle {
|
||||||
|
int id PK
|
||||||
|
varchar ip_address UK
|
||||||
|
smallint failed_attempts
|
||||||
|
datetime window_started_at
|
||||||
|
datetime lockout_until
|
||||||
|
datetime last_attempt_at
|
||||||
|
}
|
||||||
|
|
||||||
|
role ||--o{ user : "role_id (RESTRICT)"
|
||||||
|
role ||--o{ role_visible_source : "role_id (CASCADE)"
|
||||||
|
role ||--o{ role_permission : "role_id (CASCADE)"
|
||||||
|
permission ||--o{ role_permission : "permission_id (CASCADE)"
|
||||||
|
user ||--o{ audit_log : "actor_user_id (SET NULL, nullable)"
|
||||||
|
role ||--o{ audit_log : "actor_role_id (SET NULL, nullable)"
|
||||||
1
docs/merise/_diagrams/mld-rbac.svg
Normal file
1
docs/merise/_diagrams/mld-rbac.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 143 KiB |
|
|
@ -97,6 +97,279 @@ in addition to the composite FK PK. Applied to `product_ingredient`.
|
||||||
|
|
||||||
Tables are ordered by dependency (no-FK tables first, then tables that depend on them).
|
Tables are ordered by dependency (no-FK tables first, then tables that depend on them).
|
||||||
|
|
||||||
|
### Relational diagrams (by sub-domain)
|
||||||
|
|
||||||
|
The relational schema is shown as four Mermaid `erDiagram` views, one per sub-domain (same
|
||||||
|
decomposition as the MCD; a single 21-table diagram would not lay out cleanly). These differ
|
||||||
|
from the MCD: associative entities are resolved into join tables with composite PKs, the
|
||||||
|
`order_item` polymorphism appears as two nullable FKs (`product_id` / `menu_id`), and every
|
||||||
|
foreign key is explicit. Audit timestamps (`created_at` / `updated_at`) are present on most
|
||||||
|
tables (see the per-table sections below) but omitted from the diagrams to keep them readable.
|
||||||
|
Relationship labels carry the FK column and its `ON DELETE` behaviour. Cross-sub-domain FK
|
||||||
|
targets are shown as stub tables (id + name). Portable SVG renders live in `_diagrams/`
|
||||||
|
(`mld-catalogue.svg`, `mld-ingredients-stock.svg`, `mld-order.svg`, `mld-rbac.svg`).
|
||||||
|
|
||||||
|
#### Catalogue
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
erDiagram
|
||||||
|
category {
|
||||||
|
int id PK
|
||||||
|
varchar name UK
|
||||||
|
varchar slug UK
|
||||||
|
smallint display_order
|
||||||
|
tinyint is_active
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
int id PK
|
||||||
|
int category_id FK
|
||||||
|
varchar name
|
||||||
|
int price_cents
|
||||||
|
smallint vat_rate
|
||||||
|
tinyint is_available
|
||||||
|
smallint display_order
|
||||||
|
}
|
||||||
|
menu {
|
||||||
|
int id PK
|
||||||
|
int category_id FK
|
||||||
|
int burger_product_id FK
|
||||||
|
varchar name
|
||||||
|
int price_normal_cents
|
||||||
|
int price_maxi_cents
|
||||||
|
tinyint is_available
|
||||||
|
smallint display_order
|
||||||
|
}
|
||||||
|
menu_slot {
|
||||||
|
int id PK
|
||||||
|
int menu_id FK
|
||||||
|
varchar name
|
||||||
|
enum slot_type
|
||||||
|
tinyint is_required
|
||||||
|
smallint display_order
|
||||||
|
}
|
||||||
|
menu_slot_option {
|
||||||
|
int menu_slot_id PK,FK
|
||||||
|
int product_id PK,FK
|
||||||
|
}
|
||||||
|
|
||||||
|
category ||--o{ product : "category_id (RESTRICT)"
|
||||||
|
category ||--o{ menu : "category_id (RESTRICT)"
|
||||||
|
product ||--o{ menu : "burger_product_id (RESTRICT)"
|
||||||
|
menu ||--o{ menu_slot : "menu_id (CASCADE)"
|
||||||
|
menu_slot ||--o{ menu_slot_option : "menu_slot_id (CASCADE)"
|
||||||
|
product ||--o{ menu_slot_option : "product_id (RESTRICT)"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Ingredients & Stock
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
erDiagram
|
||||||
|
ingredient {
|
||||||
|
int id PK
|
||||||
|
varchar name UK
|
||||||
|
varchar unit
|
||||||
|
int stock_quantity
|
||||||
|
int stock_capacity
|
||||||
|
smallint pack_size
|
||||||
|
smallint low_stock_pct
|
||||||
|
smallint critical_stock_pct
|
||||||
|
tinyint is_active
|
||||||
|
}
|
||||||
|
product_ingredient {
|
||||||
|
int product_id PK,FK
|
||||||
|
int ingredient_id PK,FK
|
||||||
|
smallint quantity_normal
|
||||||
|
smallint quantity_maxi
|
||||||
|
tinyint is_removable
|
||||||
|
tinyint is_addable
|
||||||
|
int extra_price_cents
|
||||||
|
}
|
||||||
|
allergen {
|
||||||
|
int id PK
|
||||||
|
varchar code UK
|
||||||
|
varchar name
|
||||||
|
}
|
||||||
|
ingredient_allergen {
|
||||||
|
int ingredient_id PK,FK
|
||||||
|
int allergen_id PK,FK
|
||||||
|
}
|
||||||
|
stock_movement {
|
||||||
|
int id PK
|
||||||
|
int ingredient_id FK
|
||||||
|
enum movement_type
|
||||||
|
int delta
|
||||||
|
int order_id FK
|
||||||
|
int user_id FK
|
||||||
|
varchar note
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
int id PK
|
||||||
|
varchar name
|
||||||
|
}
|
||||||
|
customer_order {
|
||||||
|
int id PK
|
||||||
|
varchar order_number
|
||||||
|
}
|
||||||
|
user {
|
||||||
|
int id PK
|
||||||
|
varchar email
|
||||||
|
}
|
||||||
|
|
||||||
|
product ||--o{ product_ingredient : "product_id (CASCADE)"
|
||||||
|
ingredient ||--o{ product_ingredient : "ingredient_id (RESTRICT)"
|
||||||
|
ingredient ||--o{ ingredient_allergen : "ingredient_id (CASCADE)"
|
||||||
|
allergen ||--o{ ingredient_allergen : "allergen_id (RESTRICT)"
|
||||||
|
ingredient ||--o{ stock_movement : "ingredient_id (RESTRICT)"
|
||||||
|
customer_order ||--o{ stock_movement : "order_id (SET NULL, nullable)"
|
||||||
|
user ||--o{ stock_movement : "user_id (SET NULL, nullable)"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Order
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
erDiagram
|
||||||
|
customer_order {
|
||||||
|
int id PK
|
||||||
|
varchar order_number UK
|
||||||
|
varchar idempotency_key UK
|
||||||
|
enum source
|
||||||
|
int acting_user_id FK
|
||||||
|
enum service_mode
|
||||||
|
enum status
|
||||||
|
int total_ht_cents
|
||||||
|
int total_vat_cents
|
||||||
|
int total_ttc_cents
|
||||||
|
datetime paid_at
|
||||||
|
datetime delivered_at
|
||||||
|
datetime cancelled_at
|
||||||
|
}
|
||||||
|
order_item {
|
||||||
|
int id PK
|
||||||
|
int order_id FK
|
||||||
|
enum item_type
|
||||||
|
int product_id FK
|
||||||
|
int menu_id FK
|
||||||
|
enum format
|
||||||
|
varchar label_snapshot
|
||||||
|
int unit_price_cents_snapshot
|
||||||
|
smallint vat_rate_snapshot
|
||||||
|
smallint quantity
|
||||||
|
}
|
||||||
|
order_item_selection {
|
||||||
|
int id PK
|
||||||
|
int order_item_id FK
|
||||||
|
int menu_slot_id FK
|
||||||
|
int product_id FK
|
||||||
|
varchar label_snapshot
|
||||||
|
}
|
||||||
|
order_item_modifier {
|
||||||
|
int id PK
|
||||||
|
int order_item_id FK
|
||||||
|
int ingredient_id FK
|
||||||
|
enum action
|
||||||
|
int extra_price_cents
|
||||||
|
}
|
||||||
|
user {
|
||||||
|
int id PK
|
||||||
|
varchar email
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
int id PK
|
||||||
|
varchar name
|
||||||
|
}
|
||||||
|
menu {
|
||||||
|
int id PK
|
||||||
|
varchar name
|
||||||
|
}
|
||||||
|
menu_slot {
|
||||||
|
int id PK
|
||||||
|
varchar name
|
||||||
|
}
|
||||||
|
ingredient {
|
||||||
|
int id PK
|
||||||
|
varchar name
|
||||||
|
}
|
||||||
|
|
||||||
|
user ||--o{ customer_order : "acting_user_id (SET NULL, nullable)"
|
||||||
|
customer_order ||--o{ order_item : "order_id (CASCADE)"
|
||||||
|
product ||--o{ order_item : "product_id (RESTRICT, polymorphic)"
|
||||||
|
menu ||--o{ order_item : "menu_id (RESTRICT, polymorphic)"
|
||||||
|
order_item ||--o{ order_item_selection : "order_item_id (CASCADE)"
|
||||||
|
menu_slot ||--o{ order_item_selection : "menu_slot_id (RESTRICT)"
|
||||||
|
product ||--o{ order_item_selection : "product_id (RESTRICT)"
|
||||||
|
order_item ||--o{ order_item_modifier : "order_item_id (CASCADE)"
|
||||||
|
ingredient ||--o{ order_item_modifier : "ingredient_id (RESTRICT)"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### RBAC & security
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
erDiagram
|
||||||
|
role {
|
||||||
|
int id PK
|
||||||
|
varchar code UK
|
||||||
|
varchar label
|
||||||
|
varchar default_route
|
||||||
|
enum order_source
|
||||||
|
tinyint is_active
|
||||||
|
}
|
||||||
|
user {
|
||||||
|
int id PK
|
||||||
|
varchar email UK
|
||||||
|
varchar password_hash
|
||||||
|
varchar pin_hash
|
||||||
|
varchar first_name
|
||||||
|
varchar last_name
|
||||||
|
int role_id FK
|
||||||
|
tinyint is_active
|
||||||
|
smallint failed_login_attempts
|
||||||
|
datetime lockout_until
|
||||||
|
datetime anonymized_at
|
||||||
|
}
|
||||||
|
role_visible_source {
|
||||||
|
int role_id PK,FK
|
||||||
|
enum source PK
|
||||||
|
}
|
||||||
|
permission {
|
||||||
|
int id PK
|
||||||
|
varchar code UK
|
||||||
|
varchar label
|
||||||
|
}
|
||||||
|
role_permission {
|
||||||
|
int role_id PK,FK
|
||||||
|
int permission_id PK,FK
|
||||||
|
}
|
||||||
|
audit_log {
|
||||||
|
int id PK
|
||||||
|
int actor_user_id FK
|
||||||
|
int actor_role_id FK
|
||||||
|
varchar action_code
|
||||||
|
varchar entity_type
|
||||||
|
int entity_id
|
||||||
|
varchar summary
|
||||||
|
json details
|
||||||
|
datetime created_at
|
||||||
|
}
|
||||||
|
login_throttle {
|
||||||
|
int id PK
|
||||||
|
varchar ip_address UK
|
||||||
|
smallint failed_attempts
|
||||||
|
datetime window_started_at
|
||||||
|
datetime lockout_until
|
||||||
|
datetime last_attempt_at
|
||||||
|
}
|
||||||
|
|
||||||
|
role ||--o{ user : "role_id (RESTRICT)"
|
||||||
|
role ||--o{ role_visible_source : "role_id (CASCADE)"
|
||||||
|
role ||--o{ role_permission : "role_id (CASCADE)"
|
||||||
|
permission ||--o{ role_permission : "permission_id (CASCADE)"
|
||||||
|
user ||--o{ audit_log : "actor_user_id (SET NULL, nullable)"
|
||||||
|
role ||--o{ audit_log : "actor_role_id (SET NULL, nullable)"
|
||||||
|
```
|
||||||
|
|
||||||
|
> `login_throttle` has no FK (an IP is not a modelled entity); it stands alone, keyed by
|
||||||
|
> `ip_address`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 4.1 `category`
|
### 4.1 `category`
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue