Consultation Module Backend Documentation
Consultation schema, slot and booking contracts, and runtime behavior.
Consultation Module - Backend Documentation
1. Backend Scope and Boundaries
Consultation backend owns:
- slot inventory management (admin)
- booking creation and booking status tracking
- customer slot discovery and customer booking history
- consultation notification job enqueueing (email + push)
It does not own payment processing. Consultation booking is a scheduling/contact workflow backed by slot + booking tables.
2. Module Composition (Aggregate + Leaf)
ConsultationModule composes:
ConsultationSharedModuleConsultationCustomerModuleConsultationAdminAggregateModule
Admin aggregate composes leaf modules:
ConsultationSlotAdminModule(/admin/consultations/slots)ConsultationBookingAdminModule(/admin/consultations/bookings)
Customer/mobile surface is owned by ConsultationCustomerController at @Controller("consultations") and mounted to /api/mobile/consultations through MobileModule route composition.
3. Data Model (Drizzle / PostgreSQL)
Schema source of truth:
packages/db/src/schema/consultation/consultation-slots.tspackages/db/src/schema/consultation/consultation-bookings.ts
3.1 Tables
consultation_slot- primary key:
id(serial) - unique:
(date, start_time)viaconsultation_slot_date_start_uq - checks:
consultation_slot_time_check(start_time < end_time) - indexes:
consultation_slot_date_idx,consultation_slot_is_active_idx
- primary key:
consultation_booking- primary key:
id(serial) - foreign keys:
slot_id -> consultation_slot.id(ON DELETE RESTRICT)customer_id -> customers.id(ON DELETE SET NULL)
- unique:
slot_idviaconsultation_booking_slot_uq(one booking per slot) - checks:
consultation_booking_full_name_non_blank_ckconsultation_booking_contact_num_non_blank_ckconsultation_booking_email_non_blank_ckconsultation_booking_contacted_state_ck
- primary key:
3.2 Enums
consultation_booking_status:pending,contacted
4. Runtime Rules and Domain Invariants
4.1 Slot validity
- Slot must exist.
- Slot must be active to accept booking.
- Slot date must be today or future in Nepal date context.
- Slot time windows for creation/update must satisfy
startTime < endTime.
4.2 Booking integrity
- Slot can have at most one booking.
- Guest booking is allowed (
customer_id = null). - Authenticated booking links
customer_id. - Deleting a slot with existing bookings is blocked and returns
CONSULTATION_BOOKING_SLOT_RESTRICTED.
4.3 Bulk slot generation
Admin bulk create uses weekday + interval generation:
- if
startDateomitted: uses current Nepal date - if
endDateomitted: derives end viaweeksAhead(default4) - if
endDateprovided withoutstartDate: rejected - insertion uses conflict-safe
onConflictDoNothingon(date, startTime)
4.4 Notifications
On booking creation, service enqueues:
NotificationJob.SEND_EMAILtoQueueName.NOTIFICATIONSNotificationJob.SEND_PUSHtoQueueName.NOTIFICATIONSwhencustomerIdexists
Queue job ids are deterministic per booking/customer for dedupe behavior.
5. Caching Strategy
| Area | Key Pattern | TTL Strategy | Invalidation Trigger |
|---|---|---|---|
| Customer slot list | consultation:customer:slots:list: | CONSULTATION_SLOTS_CACHE_TTL_SECONDS (default 300) | slot writes and booking creation |
Implementation notes:
- Keys use
CacheKeyUtil.build(...). - Invalidation uses
invalidatePattern(prefix*). - Redis failures degrade gracefully (warn + DB fallback).
6. Rate Limiting Strategy
Environment-configured limits:
CONSULTATION_PUBLIC_BOOK_RATE_LIMIT(default10)CONSULTATION_PUBLIC_BOOK_RATE_WINDOW_SECONDS(default60)
Applied in controllers for:
- customer:
slots,book,bookings - admin: slot and booking operations
Breach behavior:
- throws
RateLimitExceededException - returns
RATE_LIMIT_EXCEEDED
7. Error and Resilience Contracts
Representative consultation errors:
| HTTP | errorCode | Message |
|---|---|---|
| 400 | CONSULTATION_SLOT_INACTIVE | Consultation slot is not active. |
| 400 | CONSULTATION_SLOT_PAST_DATE | Cannot book a slot in the past. |
| 400 | CONSULTATION_SLOT_ALREADY_BOOKED | Slot is already booked. |
| 400 | CONSULTATION_BOOKING_SLOT_RESTRICTED | Cannot delete slot that has bookings. Please deactivate instead. |
| 404 | CONSULTATION_SLOT_NOT_FOUND | Consultation slot not found. |
| 404 | CONSULTATION_BOOKING_NOT_FOUND | Consultation booking not found. |
| 409 | CONSULTATION_SLOT_CONFLICT | Slot conflict (duplicate slot or invalid slot-time window). |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests. Please try again later. |
Resilience notes:
- Unique/check constraints provide DB-level race protection.
- Booking creation translates unique-slot conflict to structured domain error.
- Cache and rate-limit Redis failures are fail-open with warning logs for availability.
8. Performance Notes
- List endpoints use projected column selection and optional pagination paths.
PaginationUtil.normalize(...)andPaginationUtil.getDrizzleParams(...)are used for list contracts.- Count queries execute only when pagination is enabled.
- Slot-list cache avoids repeated expensive joins for read-heavy browse traffic.
9. Backend Diagram
10. Release/QA Checklist
- Admin slot list/detail/create/update/delete routes enforce permissions and guards.
- Bulk slot generation date-window rules (
startDate,endDate,weeksAhead) are validated. - Booking flow blocks inactive/past/already-booked slots with structured error codes.
- Booking status update to
contactedsetscontactedAtand respects state constraints. - Slot cache keyspace invalidates on slot writes and booking creation.
- Notification jobs are enqueued with deterministic job ids.
- Mobile-composed routes under
/api/mobile/consultationsresolve correctly.
11. File Map
apps/api/src/modules/consultation/admin/*apps/api/src/modules/consultation/customer/*packages/db/src/schema/consultation/*
12. Environment Variables
| Variable | Default | Description |
|---|---|---|
CONSULTATION_PUBLIC_BOOK_RATE_LIMIT | 10 | Rate limit for booking requests per window |
CONSULTATION_PUBLIC_BOOK_RATE_WINDOW_SECONDS | 60 | Rate limit window in seconds for booking |
CONSULTATION_SLOTS_CACHE_TTL_SECONDS | 300 | Cache TTL for customer slot list in seconds |
Time fields in this module are stored as timezone-aware values and should be handled as ISO-8601 instants by API consumers.
See Also
- Feature Guide: See Consultation - Feature List Section 6 (State Models) for booking lifecycle diagram.
- API Reference: See Consultation - API & Integration Guide Section 7 (Endpoint Reference + Payload Cheatsheet) for API contracts.