# 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.