Skip to main content

reservation-service

Bounded Context: Reservations (Core) · Owner: PMS · Phase: 0 · Storage: Cloud SQL Postgres (shared schema + RLS) + transactional outbox · Bundle: services/reservation-service/

reservation-service is the authoritative aggregate root for a hotel booking on Ghasi Melmastoon. It owns the Reservation lifecycle (quoted → held → confirmed → checked_in → in_house → checked_out, plus terminal states cancelled, no_show, expired_hold), guest details, dates, room/rate selection, special requests, modification audit, and — most importantly — it is the booking-saga orchestrator across inventory-service, pricing-service, payment-gateway-service, lock-integration-service, and notification-service.

The service does not own availability allocation (delegated to inventory-service), price calculation (pricing-service), money capture (payment-gateway-service), key issuance (lock-integration-service), or housekeeping tasks (housekeeping-service). It emits the events those services react to, and it consumes the events they emit, weaving them into a single saga state machine on the Reservation aggregate.

Purpose

  • Be the single authoritative aggregate for everything a guest agreed to: who, where, when, which rooms, which rate, which special requests, which payment method, which channel.
  • Orchestrate the booking saga end-to-end with declared compensation paths — never a synchronous chain longer than 2 hops (§7 of 02-enterprise-architecture).
  • Provide a stable state machine with explicit guards, so every other service can subscribe to the events it cares about without ever reading our DB.
  • Replicate active and upcoming reservations to the Electron desktop SQLite under a strict per-aggregate conflict policy: server_authoritative for state and money-related fields, lww+diff for free-text special requests and notes, append_only for the modification audit trail.

Key responsibilities

  1. Reservation lifecyclequoted → held (10 min hold while payment is collected) → confirmed → checked_in → in_house → checked_out, with first-class cancellable and modifiable transitions, idempotent commands, and OCC-protected concurrency.
  2. Booking-saga orchestration — coordinate inventory-service (allocate), pricing-service (final quote with FX snapshot), payment-gateway-service (intent + capture or pending-cash), lock-integration-service (issue/update/revoke key), and notification-service (multi-language confirmation, pre-arrival, post-stay).
  3. Modifications with sub-types — date_change, room_change, room_added, room_removed, guest_count_change, rate_change, guest_profile_update, special_request_added. Every modification is a Reservation event with full audit (who, when, before/after).
  4. Walk-in bookings — single combined flow that creates and check-ins the reservation in one transaction, with deferred guest KYC for phone-by-staff.
  5. Group bookings — multi-room single-payer reservations with per-room sub-aggregates (ReservationItem), partial-cancel support, and a single folio reference.
  6. No-show, early-checkout, over-stay — policy-driven transitions that emit the events billing-service needs to settle the folio.
  7. Reservation merging when a guest splits one stay across rooms, and group split when a group booking needs to fan into independent reservations.
  8. Multi-channel attribution — preserve the source channel (direct, meta, walk-in, phone-by-staff, OTA in Phase 3+) for revenue analysis and partner reconciliation.
  9. Cash-on-arrival as a first-class payment selection — the reservation can confirm with payment_pending_cash and the folio captures the cash event later.
  10. FX snapshot — for Iran-tenant reservations using IRR (and other multi-currency reservations), snapshot the FX rate at confirm-time and persist it on the reservation; never re-quote.

Aggregates owned

AggregateCardinality per ReservationPurpose
Reservation1 (root)The authoritative booking; carries state machine, totals, channel, payment summary, FX snapshot
ReservationItem1..N (one per assigned room per stay-day-window)Room assignment + nightly rate breakdown + occupant link
Guest1 (primary contact)Booker; carries identity, locale, preferences, contact channels
AdditionalGuest0..NOther occupants, optional ID where required
SpecialRequest0..NFree-text + structured tags (halal, vegetarian, baby-cot, late check-in, …); resolved by notification-service and surfaced to backoffice
ReservationModification0..N (audit)Append-only audit row per modification: actor, type, before, after, occurredAt
ReservationHold0..1Short-lived TTL hold record while payment is in flight

Key APIs (REST, /api/v1/reservations)

MethodPathPurpose
POST/quotesRequest a quote (price + availability snapshot, no commitment)
POST/holdsPlace a 10-minute hold (engages saga step 1)
POST/Create + confirm (after payment)
GET/:idRead full reservation (tenant-scoped)
PATCH/:idModify (with sub-type discriminator)
POST/:id/cancelCancel (policy-checked)
POST/:id/check-inCheck-in (issues key via lock-integration-service)
POST/:id/check-outCheck-out (closes folio, revokes key)
POST/:id/walk-inCombined create+check-in for walk-ins
POST/:id/no-showDeclare no-show (policy applied)
POST/:id/special-requestsAppend a special request
GET/by-guest/:guestIdList a guest's stays
POST/groups/:groupId/splitSplit a group booking
POST/mergeMerge two reservations

Consumed by bff-tenant-booking-service (guest funnel) and bff-backoffice-service (front-desk staff). Never consumed directly by bff-consumer-service (the meta layer reads via search-aggregation-service).

Key events published

EventTrigger
melmastoon.reservation.quote.requested.v1A guest or staff asks for a price
melmastoon.reservation.quote.created.v1Quote computed and pinned with TTL
melmastoon.reservation.held.v1Inventory hold placed (TTL 10 min)
melmastoon.reservation.hold_expired.v1Hold TTL elapsed without confirm
melmastoon.reservation.confirmed.v1Reservation confirmed (post-capture or pending-cash)
melmastoon.reservation.cancelled.v1Cancellation accepted (policy-checked)
melmastoon.reservation.modified.v1Any non-date modification
melmastoon.reservation.dates_changed.v1Stay window mutated (drives re-allocation + key update)
melmastoon.reservation.check_in_started.v1Front-desk staff initiated check-in
melmastoon.reservation.checked_in.v1Check-in complete (folio opened, key issued)
melmastoon.reservation.checkout_started.v1Check-out initiated
melmastoon.reservation.checked_out.v1Check-out complete (folio closed, key revoked)
melmastoon.reservation.no_show.v1No-show declared after grace window
melmastoon.reservation.early_checkout.v1Guest left before scheduled check-out
melmastoon.reservation.overstayed.v1Guest still in-house after scheduled check-out + grace
melmastoon.reservation.special_request.added.v1A new special request appended

Key events consumed

EventEffect
melmastoon.inventory.allocation.committed.v1Saga step 1 success → advance to payment step
melmastoon.inventory.allocation.failed.v1Compensation: cancel held reservation, refund pending payment if any
melmastoon.payment.transaction.captured.v1Advance from held → confirmed; trigger key issuance for stays starting today
melmastoon.payment.transaction.failed.v1Compensation: cancel held reservation, release inventory hold
melmastoon.payment.transaction.refunded.v1Record refund on reservation (informational; folio is the ledger)
melmastoon.lock_integration.key_credential.issued.v1Link credential id to reservation; surface in backoffice + send to guest
melmastoon.lock_integration.key_credential.failed.v1Alert front desk; allow manual key with requires_manual_key=true flag
melmastoon.tenant.settings.changed.v1Refresh hold-window default, cancellation policy version, default channel attribution
melmastoon.property.room.taken_out_of_order.v1Re-accommodate active reservations on the affected room

Upstream / downstream

Upstream (we consume): inventory-service, pricing-service, payment-gateway-service, lock-integration-service, tenant-service, property-service.

Downstream (we publish for): inventory-service, billing-service, lock-integration-service, housekeeping-service, notification-service, analytics-service, audit-service, search-aggregation-service, sync-service, bff-backoffice-service, bff-tenant-booking-service.

Non-functional requirements

NFRTarget
Booking-saga p99 (held → confirmed)< 5 s including external payment capture
Hold-expiry job lag< 30 s beyond TTL
Modification success rate> 99.5% monthly
API availability99.95% monthly
Tenant isolationRLS-enforced; tenant-isolation.spec.ts mandatory in CI
Sync footprintActive + upcoming (next 30 days) + recent history (last 60 days) replicated to desktop SQLite
ReplicasMin 3 Cloud Run instances (hot path); separate hold-expiry worker as Cloud Run cron job (every 30 s)

Where to go next