Files
azionelab/docs/domain-model.md

194 lines
6.2 KiB
Markdown

# 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`: required authenticated staff user reference for successful check-in;
- `source`: optional source such as `qr_scan` or `manual`;
- `created_at`: creation timestamp;
- `updated_at`: last update timestamp.
Relationships:
- each check-in belongs to one reservation;
- a reservation can have at most one successful check-in.
Rules:
- check-in is performed only by authenticated staff or admin users;
- staff use a mobile-friendly web page to scan the QR code or enter the token manually;
- the QR code contains only an opaque verification token or URL;
- the backend validates the token server-side;
- only confirmed reservations can be checked in;
- a reservation cannot be checked in twice;
- successful check-in creates a `CheckIn` record, or updates an existing incomplete check-in record for the same reservation;
- successful check-in records must include `checked_in_at` and `checked_in_by`;
- 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.