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.
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
| Layer | Choice | Why |
|---|---|---|
| Frontend — Admin | Next.js (React) + TypeScript + TailwindCSS | SSR for fast first paint, rich component ecosystem, sharable design system with public site. |
| Frontend — Public | Next.js (App Router) + ISR for property pages | SEO-critical: property pages must render server-side; ISR keeps them fresh without rebuilding the world. |
| Backend API | Node.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). |
| Database | PostgreSQL 16 (primary) + Redis (cache, sessions, rate-limit) | Strong relational integrity for bookings & payments; Redis for hot reads and ephemeral OTPs. |
| Queue / Jobs | BullMQ (Node) or Laravel Queue with Redis driver | For OTA sync, email, webhooks, reminders. |
| Search | Postgres full-text + trigram (MVP) → Meilisearch (V1) | Avoid a heavy search stack until traffic warrants it. |
| Object storage | S3-compatible (AWS S3 or Cloudflare R2) | Property photos, ID docs (encrypted), invoices. |
| Postmark (transactional) — fallback SES | Excellent deliverability for receipts & OTPs. | |
| Payments | Razorpay (orders, payments, webhooks, refunds) | Per client preference; UPI & cards in one integration. |
| Observability | Sentry (errors) + Grafana / Loki (logs) + Uptime Robot | Catch errors per-user, audit-grade logs for finance. |
| Deploy | Docker · Hetzner / DigitalOcean · Cloudflare in front | Predictable cost, edge caching for photos & static. |
Architecture diagram
Logical view of the surfaces, services and external systems.
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".
// Public booking, paid online Public Site → POST /api/quotes // price + tax + fees for dates Public Site → POST /api/holds // 10-min inventory hold Public Site → POST /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
main · seeded dataSingle 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.