Tech & Architecture

ABc is split into a public API, an internal admin API, a public storefront, an admin SPA, and a small fleet of background workers that sync inventory and process payment webhooks. This page captures the proposed stack, the service boundaries, and the data flow.

Stack is a proposal, not a decision.

Everything below is a sensible default. We will revisit each choice after talking to the client's existing team / hosting setup.

Stack at a glance

LayerChoiceWhy
Frontend — AdminNext.js (React) + TypeScript + TailwindCSSSSR for fast first paint, rich component ecosystem, sharable design system with public site.
Frontend — PublicNext.js (App Router) + ISR for property pagesSEO-critical: property pages must render server-side; ISR keeps them fresh without rebuilding the world.
Backend APINode.js (NestJS) or Laravel 11 (PHP)NestJS gives modular structure + DI. Laravel is a strong pick if the client's team is PHP-native (XAMPP already in use suggests this).
DatabasePostgreSQL 16 (primary) + Redis (cache, sessions, rate-limit)Strong relational integrity for bookings & payments; Redis for hot reads and ephemeral OTPs.
Queue / JobsBullMQ (Node) or Laravel Queue with Redis driverFor OTA sync, email, webhooks, reminders.
SearchPostgres full-text + trigram (MVP) → Meilisearch (V1)Avoid a heavy search stack until traffic warrants it.
Object storageS3-compatible (AWS S3 or Cloudflare R2)Property photos, ID docs (encrypted), invoices.
EmailPostmark (transactional) — fallback SESExcellent deliverability for receipts & OTPs.
PaymentsRazorpay (orders, payments, webhooks, refunds)Per client preference; UPI & cards in one integration.
ObservabilitySentry (errors) + Grafana / Loki (logs) + Uptime RobotCatch errors per-user, audit-grade logs for finance.
DeployDocker · Hetzner / DigitalOcean · Cloudflare in frontPredictable cost, edge caching for photos & static.

Architecture diagram

Logical view of the surfaces, services and external systems.

Clients
Public WebsiteNext.js · SSR + ISR
Admin ConsoleNext.js · auth-only SPA
Edge & Gateway
CloudflareCDN · WAF · DDoS
API Gatewayauth · rate-limit · routing
Application services
auth-svcsign-in · OTP · RBAC
property-svcrooms · rates · photos
booking-svcquote · hold · confirm
payment-svcRazorpay · ledger
channel-svcOTA adapters
notify-svcemail · SMS · WhatsApp
availability-svcinventory grid
audit-svcappend-only log
Data & storage
PostgreSQL 16primary OLTP · PITR
Rediscache · sessions · queue
S3 / R2photos · KYC docs · invoices
Background workers
OTA syncpush · pull · drift detect
Email workerPostmark / SES
Reminder cronbalance · pre-stay
Webhook handlerRazorpay · OTAs
External providers
Razorpaypayments · refunds
Booking.comOTA · XML
Airbnb · MMT · TAOTA / iCal
Postmark · MSG91email · SMS

Service boundaries

We start as a modular monolith. Each module is cleanly scoped so it can be lifted into its own service later, when traffic or team structure demands it.

auth-service

Sign-up, sign-in, OTP, password reset, sessions, JWT issuance, RBAC role checks, 2FA.

property-service

Properties, rooms, room-types, amenities, photos, policies, rate plans, blackouts.

availability-service

Computes the live inventory grid (date × room-unit), applies blocks & OTA holds.

booking-service

Quote → hold → confirm. Source tag (direct / OTA / manual). Idempotency keys.

payment-service

Razorpay order/payment/refund, partial-payments, manual cash/UPI capture, ledger.

channel-service

OTA adapters (Booking.com, Airbnb, MMT, TripAdvisor, Expedia). iCal fallback.

notify-service

Email templates, send queue, retries, audit trail of every notification.

audit-service

Append-only log: who did what, when, from where. Read by Finance & Support.

Core data model

The skeleton — full schema lives in each feature page.

users
  • iduuid PK
  • emailtext uniq
  • phonetext
  • roleenum
  • kyc_statusenum
  • created_attimestamp
properties
  • iduuid PK
  • owner_id→ users
  • nametext
  • typehotel | bnb
  • statusenum
  • city, countrytext
room_types
  • iduuid PK
  • property_id→ properties
  • nametext
  • occupancyint
  • base_ratemoney
  • total_unitsint
bookings
  • iduuid PK
  • property_id→ properties
  • room_type_id→ room_types
  • sourceenum
  • statusenum
  • total_amountmoney
  • check_in, check_outdate
payments
  • iduuid PK
  • booking_id→ bookings
  • methodenum
  • amountmoney
  • razorpay_reftext
  • statusenum
channel_listings
  • iduuid PK
  • property_id→ properties
  • channelenum
  • external_idtext
  • last_sync_attimestamp

Booking flow — sequence

What happens between "guest clicks Pay" and "owner sees the booking in their console".

SEQUENCE
// Public booking, paid online
Public SitePOST /api/quotes        // price + tax + fees for dates
Public SitePOST /api/holds         // 10-min inventory hold
Public SitePOST /api/orders        // creates Razorpay order
Razorpay UI  →  user pays
Razorpay     →  webhook /razorpay/payment.captured
payment-svc  →  marks booking CONFIRMED, releases hold
notify-svc   →  email guest + email owner
channel-svc  →  push availability decrement to all OTAs
audit-svc    →  log booking.confirmed event

Security & compliance

  • PCI scope. Card data never touches our servers — Razorpay hosted checkout keeps us out of PCI scope.
  • Encryption. At-rest (AES-256 on database + S3). TLS 1.3 in flight. KMS-managed keys.
  • Auth. Argon2id password hashing. JWT access (15 min) + refresh (30 d) tokens. 2FA optional for owners, enforced for finance roles.
  • RBAC. Every endpoint annotated with a required permission; checks happen in middleware, not in the controller.
  • Rate limits. Per-IP and per-user. Aggressive on /auth, /otp, /reset-password.
  • Webhook verification. Razorpay signatures verified before mutation. OTA webhooks behind allow-listed IPs + shared secret.
  • Audit log. Every state change on bookings & payments is append-only. Immutable trail for finance reconciliation.
  • Backups. Postgres PITR. Daily off-site snapshot. Tested restore quarterly.

Environments

local
Developer laptops · Docker Compose
dev
Auto-deploy from main · seeded data
stage
Mirror of prod · sandbox Razorpay
prod
Live · feature-flagged rollouts
Why this scales

Single Postgres can comfortably hold the first 50 properties and 100k bookings/month. Modular monolith lets us extract channel-service and payment-service into independent workers the moment OTA volume justifies it — no rewrite.