Skip to content

System Architecture

Project: PTX Channel Manager (ptx-cm)
Version: 2.1.0
Last Updated: 2026-03-15


Overview

PTX Channel Manager is a unified OTA channel management platform that auto-syncs room availability across Booking.com, Agoda, Traveloka, and Expedia to prevent overbookings. The system uses a monorepo structure with a Next.js frontend, NestJS backend, PostgreSQL database, and Redis queue system.


1. High-Level Architecture Diagram


2. Sync Engine Flow (Polling → Booking → Availability)


3. Authentication & Authorization Flow


4. Database Entity Relationship Diagram (33 Models)


2. Monorepo Structure

ptx-cm/
├── apps/
│   ├── api/                          # NestJS 10 Backend
│   │   ├── src/
│   │   │   ├── modules/              # 15 feature modules
│   │   │   ├── common/               # Guards, filters, decorators
│   │   │   ├── utils/                # Helpers, crypto, pagination
│   │   │   └── main.ts               # Bootstrap
│   │   └── package.json
│   │
│   └── web/                          # Next.js 16 Frontend
│       ├── app/                      # App Router
│       │   ├── (auth)/               # Public routes
│       │   ├── (main)/               # Protected routes
│       │   └── api/[...proxy]/       # Backend proxy
│       ├── components/               # React components
│       ├── hooks/                    # Custom hooks
│       ├── contexts/                 # Provider contexts
│       ├── lib/                      # Utilities
│       └── package.json

├── packages/
│   ├── database/                     # Prisma
│   │   ├── prisma/                   # Schema + migrations
│   │   ├── src/
│   │   │   └── client.ts
│   │   └── package.json
│   │
│   ├── types/                        # Shared TypeScript
│   │   ├── src/
│   │   │   ├── api.types.ts          # Request/response DTOs
│   │   │   └── enums.ts              # OtaType, AlertSeverity, etc.
│   │   └── package.json
│   │
│   └── config/                       # ESLint/TypeScript
│       ├── eslint-config/
│       ├── typescript-config/
│       └── package.json

├── docs/                             # Documentation
│   ├── system-architecture.md        # This file
│   ├── codebase-summary.md           # File counts & module map
│   ├── code-standards.md             # Naming, patterns, error handling
│   ├── project-overview-pdr.md       # Requirements & PDR
│   ├── development-roadmap.md        # Feature status & milestones
│   ├── SRD.md                        # System Requirements
│   ├── API_SPEC.md                   # REST API spec
│   ├── DB_DESIGN.md                  # Database design
│   └── UI_SPEC.md                    # Design system & screens

├── docker-compose.yml                # PostgreSQL 16 + Redis 7
├── turbo.json                        # Turborepo config
├── pnpm-workspace.yaml               # pnpm workspaces
└── package.json                      # Root config

3. Backend Module Architecture

Core Modules (37 total)

ModulePurposeKey Files
authJWT authentication, login, refresh, password resetauth.service.ts, jwt.strategy.ts
usersUser CRUD, account settingsusers.service.ts, users.controller.ts
rolesRole definitions, permission bitmasksroles.service.ts
activity-logsHTTP request logging, audit trailactivity-log.middleware.ts
notificationsEmail service (Resend API + Mailpit in dev)notifications.service.ts
propertiesProperty CRUD, timezone/currency assignmentproperties.service.ts
room-typesRoom inventory, base ratesroom-types.service.ts
room-mappingsOTA ↔ local room type mappingroom-mappings.service.ts
suppliersSupplier/room-owner managementsuppliers.service.ts
supplier-room-allocationsM:N supplier ↔ room allocationsupplier-room-allocations.service.ts
ota-accountsEncrypted OTA credentials (AES-256-GCM)ota-accounts.service.ts
ota-connectionsProperty ↔ OTA account linksota-connections.service.ts
ota-adaptersFactory + 4 adapters (Booking, Agoda, Traveloka, Expedia)ota-adapter.factory.ts
ota-rate-configsPer-OTA rate formula configurationota-rate-configs.service.ts
ota-statusOTA-specific status definitionsota-status.service.ts
bookingsBooking CRUD, upsertFromOta dedupbookings.service.ts
booking-statusConfigurable status definitionsbooking-status.service.ts
booking-status-transitionStatus state machine transitionsbooking-status-transition.service.ts
booking-hooksPost-status-change side effectsbooking-hooks.service.ts
customersGuest CRM, booking consolidation, link/unlink/mergecustomers.service.ts
availabilityCalendar matrix, block/unblock date rangesavailability.service.ts
ratesBase rate management per room typerates.service.ts
rate-rulesMarkup/discount/seasonal rulesrate-rules.service.ts
rate-plansRate plan configurations with adjustmentsrate-plans.service.ts
bulk-ratesBatch rate updates across propertiesbulk-rates.service.ts
process-typesBPM workflow type definitionsprocess-types.service.ts
process-instancesActive workflow instance trackingprocess-instances.service.ts
process-statusProcess status definitionsprocess-status.service.ts
process-transitionsState machine transition rulesprocess-transitions.service.ts
sync-engineOrchestration of polling, pulling, syncingsync-engine.service.ts
sync-jobsJob tracking with statussync-jobs.service.ts
alertsOverbooking detection, notificationsalerts.service.ts
dashboardAggregated KPI metricsdashboard.service.ts
settingsApp config (booking_pull_minutes, etc)settings.service.ts
healthLiveness probeshealth.controller.ts
countriesReference data for country filteringcountries.service.ts
prismaPrisma client providerprisma.service.ts

Guard & Decorator Pattern

typescript
// Global guards (applied in order):
1. JwtAuthGuard          // Validates JWT signature & expiry
2. PermissionsGuard      // Checks module:action bitmask
3. CountryScopeGuard     // Applies country filter to query

// Decorators to opt out or customize:
@Public()                // Skip JwtAuthGuard
@RequirePermission(MODULE.PROPERTIES, ACTIONS.CREATE)
@CountryScope()          // Injects countryScope param

4. Frontend Route Architecture

Route PatternLayerAuthentication
/login, /forgot-password, /reset-password, /change-password(auth)Public
/dashboard(dashboard)Protected + JWT
/bookings, /bookings/[id](dashboard)Protected + JWT
/properties, /properties/[id](dashboard)Protected + JWT
/customers, /customers/[id](dashboard)Protected + JWT
/availability(dashboard)Protected + JWT
/rates(dashboard)Protected + JWT
/ota-accounts, /ota-accounts/connect(dashboard)Protected + JWT
/process-instances(dashboard)Protected + JWT
/suppliers, /suppliers/[id](dashboard)Protected + JWT
/alerts, /sync-jobs, /logs(dashboard)Protected + JWT
/master-data, /settings, /profile(dashboard)Protected + JWT

Context Provider Chain

AuthProvider (JWT + user state)

CountryProvider (VN/ID/MY filter)

ReferenceDataProvider (Countries cache)

ThemeProvider (Light/dark mode)

I18nProvider (en/vi locale)

ActivityTrackerProvider (Session timeout)

5. Tech Stack Summary

LayerTechnologyVersion
FrontendNext.js + React + Tailwind CSS16 + 18
Frontend StateSWR + Context API-
Frontend Formreact-hook-form + zod-
Frontend TablesTanStack Table (react-table)-
BackendNestJS + Passport10
Backend Validationclass-validator-
Backend QueueBullMQ + Redis-
DatabasePostgreSQL + Prisma ORM16 + 7
AuthenticationJWT + bcrypt-
EncryptionAES-256-GCM-
Build ToolTurborepo + pnpm-
LanguageTypeScript5.7
TestingJest + @nestjs/testing-

6. Key Design Patterns

Authentication Pattern

  • JWT payload includes: sub, email, roleId, permissions (PermissionMap), country, locale
  • Token extraction: HttpOnly cookie access_token → fallback to Bearer header
  • Guards applied per controller method or globally

Authorization Pattern

  • Bitmask-based permissions per module (VIEW=1, CREATE=2, EDIT=4, DELETE=8)
  • Role-based presets: super_admin, admin, manager, ota, cs, fin
  • Country-scoped queries: all user queries filtered by countryScope middleware

OTA Adapter Pattern

  • Factory pattern: OtaAdapterFactory.create(otaType) returns adapter instance
  • Strategy interface: IOtaAdapter { fetchBookings(), pushAvailability() }
  • 4 implementations: BookingAdapter, AgodaAdapter, TravelokaAdapter, ExpediaAdapter

Sync Pipeline Pattern

  • Repeating jobs via BullMQ scheduler (150s interval)
  • Multi-stage processing: polling → booking pull → availability calc → OTA push
  • Job tracking: SyncJob records created for audit + retry logic

Data Fetching (Frontend)

  • SWR for data caching & revalidation
  • Context providers for global state (auth, country, reference data)
  • TanStack Table for sorting, pagination, filtering

7. Key Enums & Types

OTA Enums:

  • OtaType: booking, agoda, traveloka, expedia
  • ConnectionStatus: active, expired, error, requires_2fa
  • TwoFactorMethod: none, totp, manual
  • SyncJobType: pull_bookings, push_availability, push_rates, verify
  • SyncJobStatus: pending, running, completed, failed

Booking Enums:

  • AlertType: overbooking, sync_failure, session_expired
  • AlertSeverity: critical, warning, info
  • AuditAction: create, update, delete

Rate Enums:

  • RateRuleType: markup, discount, seasonal

Permission Modules (15):
DASHBOARD, PROPERTIES, ROOM_TYPES, OTA_ACCOUNTS, BOOKINGS, CUSTOMERS, AVAILABILITY, SYNC_JOBS, RATES, ALERTS, SETTINGS, USERS, SUPPLIERS, COUNTRIES, ROLES


8. Deployment Architecture

Development:

  • Monorepo dev servers: pnpm dev starts both apps + BullMQ listeners
  • PostgreSQL in Docker: port 5433
  • Redis in Docker: port 6379
  • Mailpit (email catcher): ports 1025/8025

Production:

  • Separate container images: API (port 3002), Web (port 3100)
  • PostgreSQL managed service (Cloud SQL / RDS)
  • Redis managed service (ElastiCache / Upstash)
  • Email via Resend API
  • Sync jobs persist in BullMQ (Redis)

Last Updated: 2026-03-15
Status: Active — All sections current as of latest codebase review

PTX Channel Manager — Internal Documentation