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 config3. Backend Module Architecture
Core Modules (37 total)
| Module | Purpose | Key Files |
|---|---|---|
| auth | JWT authentication, login, refresh, password reset | auth.service.ts, jwt.strategy.ts |
| users | User CRUD, account settings | users.service.ts, users.controller.ts |
| roles | Role definitions, permission bitmasks | roles.service.ts |
| activity-logs | HTTP request logging, audit trail | activity-log.middleware.ts |
| notifications | Email service (Resend API + Mailpit in dev) | notifications.service.ts |
| properties | Property CRUD, timezone/currency assignment | properties.service.ts |
| room-types | Room inventory, base rates | room-types.service.ts |
| room-mappings | OTA ↔ local room type mapping | room-mappings.service.ts |
| suppliers | Supplier/room-owner management | suppliers.service.ts |
| supplier-room-allocations | M:N supplier ↔ room allocation | supplier-room-allocations.service.ts |
| ota-accounts | Encrypted OTA credentials (AES-256-GCM) | ota-accounts.service.ts |
| ota-connections | Property ↔ OTA account links | ota-connections.service.ts |
| ota-adapters | Factory + 4 adapters (Booking, Agoda, Traveloka, Expedia) | ota-adapter.factory.ts |
| ota-rate-configs | Per-OTA rate formula configuration | ota-rate-configs.service.ts |
| ota-status | OTA-specific status definitions | ota-status.service.ts |
| bookings | Booking CRUD, upsertFromOta dedup | bookings.service.ts |
| booking-status | Configurable status definitions | booking-status.service.ts |
| booking-status-transition | Status state machine transitions | booking-status-transition.service.ts |
| booking-hooks | Post-status-change side effects | booking-hooks.service.ts |
| customers | Guest CRM, booking consolidation, link/unlink/merge | customers.service.ts |
| availability | Calendar matrix, block/unblock date ranges | availability.service.ts |
| rates | Base rate management per room type | rates.service.ts |
| rate-rules | Markup/discount/seasonal rules | rate-rules.service.ts |
| rate-plans | Rate plan configurations with adjustments | rate-plans.service.ts |
| bulk-rates | Batch rate updates across properties | bulk-rates.service.ts |
| process-types | BPM workflow type definitions | process-types.service.ts |
| process-instances | Active workflow instance tracking | process-instances.service.ts |
| process-status | Process status definitions | process-status.service.ts |
| process-transitions | State machine transition rules | process-transitions.service.ts |
| sync-engine | Orchestration of polling, pulling, syncing | sync-engine.service.ts |
| sync-jobs | Job tracking with status | sync-jobs.service.ts |
| alerts | Overbooking detection, notifications | alerts.service.ts |
| dashboard | Aggregated KPI metrics | dashboard.service.ts |
| settings | App config (booking_pull_minutes, etc) | settings.service.ts |
| health | Liveness probes | health.controller.ts |
| countries | Reference data for country filtering | countries.service.ts |
| prisma | Prisma client provider | prisma.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 param4. Frontend Route Architecture
| Route Pattern | Layer | Authentication |
|---|---|---|
/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
| Layer | Technology | Version |
|---|---|---|
| Frontend | Next.js + React + Tailwind CSS | 16 + 18 |
| Frontend State | SWR + Context API | - |
| Frontend Form | react-hook-form + zod | - |
| Frontend Tables | TanStack Table (react-table) | - |
| Backend | NestJS + Passport | 10 |
| Backend Validation | class-validator | - |
| Backend Queue | BullMQ + Redis | - |
| Database | PostgreSQL + Prisma ORM | 16 + 7 |
| Authentication | JWT + bcrypt | - |
| Encryption | AES-256-GCM | - |
| Build Tool | Turborepo + pnpm | - |
| Language | TypeScript | 5.7 |
| Testing | Jest + @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
countryScopemiddleware
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, expediaConnectionStatus: active, expired, error, requires_2faTwoFactorMethod: none, totp, manualSyncJobType: pull_bookings, push_availability, push_rates, verifySyncJobStatus: pending, running, completed, failed
Booking Enums:
AlertType: overbooking, sync_failure, session_expiredAlertSeverity: critical, warning, infoAuditAction: 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 devstarts 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