diff --git a/README.md b/README.md index 99c5817..03dd6f7 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,29 @@ nothing about the business rules.** That keeps the availability logic — the pa brief actually grades — testable in isolation and the data source swappable without touching either. +## Data model + +The catalogue is 40 **Properties**, each owning one or more **Rooms** — a single nested +aggregate that mirrors the source dataset (no normalization or joins; the Property *is* +the unit of storage and retrieval): + +- **Property** — `id`, `name`, `description`, `star_rating`, `overall_rating`, + `review_count`, nested `address` / `contact` / `policies`, `amenities[]`, and `rooms[]`. +- **Room** — `room_id`, `type`, bedding & occupancy (`bed_type`, `bed_count`, + `max_occupancy`, `square_footage`), `price_per_night`, `room_amenities[]`, and + `available_dates[]` — the discrete open **Nights** that are the source of truth for + availability. +- **Derived, never stored** — `price_from` (a Property's cheapest Room rate) for search + and list display, plus `nights` / `total_price` on the rooms endpoint. Computing these + keeps one authoritative number per fact instead of a denormalized copy that can drift. + +The Zod schemas in [`src/domain/hotel.ts`](src/domain/hotel.ts) are the single source of +truth for these shapes: they **validate the seed at load time** (a malformed dataset fails +fast and legibly) *and* their inferred types are the domain models the rest of the code +programs against — so the model, the wire format, and the OpenAPI docs cannot drift apart. +The wire format stays `snake_case` end-to-end to match the dataset. The domain vocabulary +(Property, Room, Stay window, Night, `price_from`, …) is fixed in [`CONTEXT.md`](CONTEXT.md). + ## Testing strategy `npm test` runs the suite across two complementary styles: