generated from bisco/codex-bootstrap
docs: add initial architecture documentation
This commit is contained in:
186
docs/domain-model.md
Normal file
186
docs/domain-model.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# Domain Model
|
||||
|
||||
This document describes the core domain concepts for AzioneLab's theatre website and booking system.
|
||||
|
||||
## Show
|
||||
|
||||
A show is a theatrical production presented by the company.
|
||||
|
||||
Suggested fields:
|
||||
|
||||
- `id`: internal identifier;
|
||||
- `title`: public show title;
|
||||
- `slug`: stable public URL identifier;
|
||||
- `summary`: short public description;
|
||||
- `description`: full public description;
|
||||
- `poster_image`: optional public image;
|
||||
- `is_published`: controls public visibility;
|
||||
- `created_at`: creation timestamp;
|
||||
- `updated_at`: last update timestamp.
|
||||
|
||||
Relationships:
|
||||
|
||||
- one show can have many performances.
|
||||
|
||||
Rules:
|
||||
|
||||
- unpublished shows are not listed publicly;
|
||||
- a public show detail page may include only published upcoming performances.
|
||||
|
||||
## Venue
|
||||
|
||||
A venue is the place where a performance happens.
|
||||
|
||||
Suggested fields:
|
||||
|
||||
- `id`: internal identifier;
|
||||
- `name`: public venue name;
|
||||
- `slug`: stable public URL identifier;
|
||||
- `address`: public address;
|
||||
- `city`: venue city;
|
||||
- `notes`: optional public or internal venue notes;
|
||||
- `created_at`: creation timestamp;
|
||||
- `updated_at`: last update timestamp.
|
||||
|
||||
Relationships:
|
||||
|
||||
- one venue can host many performances.
|
||||
|
||||
## Performance
|
||||
|
||||
A performance is a scheduled presentation of one show at one venue.
|
||||
|
||||
Suggested fields:
|
||||
|
||||
- `id`: internal identifier;
|
||||
- `show`: required reference to `Show`;
|
||||
- `venue`: required reference to `Venue`;
|
||||
- `starts_at`: performance date and time;
|
||||
- `room_capacity`: configured total room capacity;
|
||||
- `manually_occupied_seats`: seats unavailable because they are reserved outside the public booking system;
|
||||
- `additional_seats`: optional extra seats made available during booking;
|
||||
- `is_booking_enabled`: controls whether public booking is open;
|
||||
- `created_at`: creation timestamp;
|
||||
- `updated_at`: last update timestamp.
|
||||
|
||||
Relationships:
|
||||
|
||||
- each performance belongs to one show;
|
||||
- each performance belongs to one venue;
|
||||
- one performance can have many reservations.
|
||||
|
||||
Availability formula:
|
||||
|
||||
```text
|
||||
available seats =
|
||||
room capacity
|
||||
+ additional seats
|
||||
- manually occupied seats
|
||||
- confirmed reservations
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- capacity values must not be negative;
|
||||
- `manually_occupied_seats` must not exceed `room_capacity + additional_seats`;
|
||||
- only confirmed reservations reduce public availability;
|
||||
- pending reservations do not guarantee a seat until confirmation;
|
||||
- final capacity validation must happen server-side when confirming a reservation;
|
||||
- changes to capacity configuration must preserve existing confirmed reservations.
|
||||
|
||||
## Reservation
|
||||
|
||||
A reservation is a booking request for a specific performance.
|
||||
|
||||
Suggested fields:
|
||||
|
||||
- `id`: internal identifier;
|
||||
- `performance`: required reference to `Performance`;
|
||||
- `status`: explicit status such as `pending`, `confirmed`, `cancelled`, or `expired`;
|
||||
- `name`: reservation contact name;
|
||||
- `email`: reservation contact email;
|
||||
- `phone`: optional reservation contact phone;
|
||||
- `party_size`: number of requested seats;
|
||||
- `notes`: optional visitor note;
|
||||
- `confirmed_at`: timestamp set when the reservation is confirmed;
|
||||
- `qr_code_generated_at`: timestamp set when the QR code is generated;
|
||||
- `created_at`: creation timestamp;
|
||||
- `updated_at`: last update timestamp.
|
||||
|
||||
Relationships:
|
||||
|
||||
- each reservation belongs to one performance;
|
||||
- one reservation can have one or more reservation tokens for different purposes;
|
||||
- one confirmed reservation can have at most one successful check-in.
|
||||
|
||||
Rules:
|
||||
|
||||
- a new reservation starts as `pending`;
|
||||
- a reservation becomes `confirmed` only through a valid confirmation token;
|
||||
- a confirmed reservation receives a QR code;
|
||||
- `party_size` must be positive;
|
||||
- the backend must reject confirmation if the requested seats would exceed availability;
|
||||
- personal data must never be stored in QR codes.
|
||||
|
||||
## ReservationToken
|
||||
|
||||
A reservation token is an opaque token used for confirmation or QR verification.
|
||||
|
||||
Suggested fields:
|
||||
|
||||
- `id`: internal identifier;
|
||||
- `reservation`: required reference to `Reservation`;
|
||||
- `purpose`: token purpose, such as `confirmation` or `check_in`;
|
||||
- `token_hash`: server-side hash of the opaque token;
|
||||
- `expires_at`: optional expiration timestamp;
|
||||
- `used_at`: timestamp set when a one-time token is consumed;
|
||||
- `created_at`: creation timestamp.
|
||||
|
||||
Relationships:
|
||||
|
||||
- each token belongs to one reservation.
|
||||
|
||||
Rules:
|
||||
|
||||
- raw tokens must be random, non-guessable, and generated with a cryptographically secure generator;
|
||||
- store only a hash of the token when practical;
|
||||
- confirmation tokens should be single use;
|
||||
- QR tokens may remain valid until the performance check-in window closes;
|
||||
- tokens must not encode personal data.
|
||||
|
||||
## CheckIn
|
||||
|
||||
A check-in records entrance validation for a confirmed reservation.
|
||||
|
||||
Suggested fields:
|
||||
|
||||
- `id`: internal identifier;
|
||||
- `reservation`: required unique reference to `Reservation`;
|
||||
- `checked_in_at`: timestamp of successful check-in;
|
||||
- `checked_in_by`: optional authenticated staff user reference;
|
||||
- `source`: optional source such as `qr_scan` or `manual`;
|
||||
- `created_at`: creation timestamp.
|
||||
|
||||
Relationships:
|
||||
|
||||
- each check-in belongs to one reservation;
|
||||
- a reservation can have at most one successful check-in.
|
||||
|
||||
Rules:
|
||||
|
||||
- only confirmed reservations can be checked in;
|
||||
- a reservation cannot be checked in twice;
|
||||
- failed check-in attempts should return a clear status without changing successful check-in state;
|
||||
- check-in must not expose unnecessary personal data to scanning clients.
|
||||
|
||||
## Anti-Overbooking Rule
|
||||
|
||||
The backend must enforce capacity inside a transaction when confirming reservations.
|
||||
|
||||
Recommended approach:
|
||||
|
||||
- lock the relevant `Performance` row during confirmation;
|
||||
- count confirmed seats for that performance;
|
||||
- compare requested seats with available seats;
|
||||
- confirm only if enough seats remain;
|
||||
- otherwise leave the reservation pending or mark it as expired/rejected according to the future product decision.
|
||||
Reference in New Issue
Block a user