System Architecture
| Project | PTX Channel Manager (ptx-cm) |
| Version | 2.0.0 |
| Last Updated | 2026-02-20 |
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
20 tables — Auth (5), Inventory (4), OTA (3), Bookings (4), Operations (4)
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 (15 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) | 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 |
| ota-accounts | Encrypted OTA credentials (AES-256-GCM) | ota-accounts.service.ts, crypto.service.ts |
| ota-connections | Property ↔ OTA account links | ota-connections.service.ts |
| ota-adapters | Factory pattern + 4 adapter stubs | ota-adapter.factory.ts, booking.adapter.ts |
| bookings | Booking CRUD, upsertFromOta dedup | bookings.service.ts |
| booking-status | Status definitions, state machine | booking-status.service.ts |
| sync-engine | Orchestration of polling, pulling, syncing | sync-engine.service.ts, ota-polling.processor.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 |
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 | (auth) | Public |
/dashboard, /bookings, /properties, /alerts, /sync-jobs, /ota-accounts, /suppliers, /master-data, /logs, /profile, /settings | (main) | 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, Inactive, ErrorSyncJobType: BookingPull, AvailabilitySyncSyncJobStatus: Pending, Running, Completed, Failed
Booking Enums:
BookingStatus: Pending, Confirmed, CheckedIn, CheckedOut, CancelledAlertType: Overbooking, SyncFailure, CredentialExpiredAlertSeverity: Info, Warning, Critical
Permission Modules (14): DASHBOARD, PROPERTIES, ROOM_TYPES, OTA_ACCOUNTS, BOOKINGS, 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-02-20 |
| Status | Active - All sections current as of latest codebase review |