Skip to main content

Booking Providers

End‑to‑end booking flows across internal tee‑time DB and external providers. The BookingService orchestrates adapters and publishes outbox events.

Architecture

  • Service: libs/tee-time-services/src/lib/booking.service.ts
  • Adapter registry token: BOOKING_ADAPTERS
  • Provider keys: 'tee-time' | 'mca' | ... (extensible)

Create command (core fields):

{
provider: 'tee-time' | 'mca';
tenantId: number;
slotId?: number; // required for 'tee-time'
contactName: string;
contactEmail?: string;
contactPhone?: string;
externalRef?: string; // idempotency key
raw?: unknown; // provider-specific payload (e.g., MCA)
}

Flow:

  1. Lookup adapter by provider. If unsupported → error.
  2. If adapter implements findExisting, it runs first (read-through idempotency).
  3. Otherwise create(command) runs.
  4. On success, BookingService publishes booking.confirmed via outbox with { provider, tenantId, slotId?, externalRef? }.

Internal provider: tee-time

  • Adapter: TeeTimeBookingAdapter
  • Source: libs/tee-time-services/src/lib/tee-sheet-booking.service.ts
  • Behavior:
    • Creates a Booking row and connects the chosen TeeTimeSlot.
    • Idempotency:
      • Hard DB check: if externalRef present, reuse existing Booking with (tenantId, externalRef) unique index.
      • Distributed lock: Redis NX idemp:tt:booking:${tenantId}:${externalRef} with TTL (default 60s).

Input shape (internal):

{
provider: 'tee-time',
tenantId,
slotId,
contactName,
contactEmail?, contactPhone?,
externalRef?
}

Errors:

  • Missing slotId or tenantId → error
  • Lock busy without existing booking → Idempotency lock busy; retry

MCA provider: mca

  • Adapter: McaBookingAdapter
  • Source: libs/tee-time-services/src/lib/adapters/mca-booking.adapter.ts
  • Writes are disabled by default; enable with MCA_WRITES_ENABLED=1.
  • If TTS_WRITE_MODE=outbox, MCA writes are forcibly disabled.

Idempotency:

  • Soft lock on MCA UID: Redis NX idemp:mca:uid:${uid} with TTL (default 60s).
    • If lock fail, proceeds optimistically but logs.

Strict validation (optional):

  • Enable with MCA_STRICT_RATE_VALIDATION=1 or pass { strictValidation: true } inside raw.request.
  • Fetches player rate selection snapshot and validates per‑player:
    • Selected plrateid is available for the player index
    • Required associations: SAGA and other questions must be answered

Basic validation (always enforced):

  • uid string present
  • numberOfHoles18
  • At least one player
  • Player 1 email/phone present
  • Each player has plname, plsurname, and a non‑zero plrateid

Payload:

{
provider: 'mca',
tenantId,
contactName,
raw: BookingRequestDto // from @digiwedge/mca-v1-client
}

Outbox events

  • Topic: booking.confirmed (via TeeTimeEvents.BOOKING_CONFIRMED)
  • Payload: { provider, tenantId, slotId?, externalRef? }

Environment

  • BOOKING_IDEMPOTENCY_TTL_SECONDS – default 60
  • MCA_WRITES_ENABLED=1 – enable MCA writes
  • MCA_STRICT_RATE_VALIDATION=1 – enable strict MCA validation
  • TTS_WRITE_MODE=outbox – force outbox‑only (disable MCA writes)

Errors and retries

  • Use idempotent externalRef on internal bookings for safe retries.
  • For MCA, prefer strict validation in high‑touch flows to reduce upstream errors.