architecture/architecture.md

Technical Architecture

Tech Stack

Layer Technology Lý do
Backend NestJS (TypeScript) Modular architecture khớp với industry plugin pattern, built-in DI
Database PostgreSQL JSONB cho metadata, row-level security cho multi-tenant, ACID
ORM Prisma Type-safe, great DX với TypeScript, migration tooling tốt
Cache / Realtime Redis Session, real-time state, pub/sub cho live updates
Queue BullMQ Notification async, scheduled jobs (reminders)
Customer Portal Next.js SSR cho SEO (subdomain booking page), fast load
Admin Portal Next.js Web-only theo brief, share codebase với customer portal
Owner App React Native (Expo) Cross-platform, share TypeScript types với backend
Staff App React Native (Expo) Cùng codebase với Owner App, phân quyền bằng role
Realtime Socket.io (NestJS Gateway) Live booking updates, staff notifications
File Storage S3-compatible (MinIO self-host) Avatar, salon branding assets
Payment Vipps MobilePay SDK Post-MVP, nhưng architecture sẵn sàng

System Architecture

                    ┌─────────────────┐
                    │   Nginx/Caddy   │  ← Reverse proxy, SSL, subdomain routing
                    │   Load Balancer │
                    └────────┬────────┘
                             │
            ┌────────────────┼────────────────┐
            │                │                │
   ┌────────▼──────┐ ┌──────▼───────┐ ┌──────▼──────┐
   │  Next.js App  │ │  Next.js App │ │  NestJS API │
   │  Customer     │ │  Admin       │ │  Backend    │
   │  Portal       │ │  Portal      │ │             │
   │               │ │              │ │  REST + WS  │
   │ {slug}.app.no │ │ admin.app.no │ │  api.app.no │
   └───────────────┘ └──────────────┘ └──────┬──────┘
                                             │
                          ┌──────────────────┼──────────────┐
                          │                  │              │
                   ┌──────▼──────┐   ┌───────▼─────┐ ┌─────▼─────┐
                   │ PostgreSQL  │   │    Redis     │ │   MinIO   │
                   │             │   │              │ │  (S3)     │
                   │ • Tenants   │   │ • Sessions   │ │ • Assets  │
                   │ • Resources │   │ • Cache      │ │ • Uploads │
                   │ • Bookings  │   │ • Pub/Sub    │ └───────────┘
                   │ • Customers │   │ • BullMQ     │
                   └─────────────┘   └──────────────┘

   ┌─────────────────┐  ┌─────────────────┐
   │  React Native   │  │  React Native   │
   │  Owner App      │  │  Staff App      │
   │                 │  │                 │
   │  iOS + Android  │  │  iOS + Android  │
   └─────────────────┘  └─────────────────┘
         │                      │
         └──────────┬───────────┘
                    │ REST + WebSocket
                    ▼
               api.app.no

Repository Structure

Multi-repo với shared AI context. Mỗi app là git repo riêng, nằm trong 1 workspace folder.

booking/                            # Workspace folder (không phải git repo)
├── booking-api/                    # Git repo — NestJS backend
│   ├── src/
│   │   ├── core/                   # Core booking engine (Resource-agnostic)
│   │   │   ├── booking/
│   │   │   ├── resource/
│   │   │   ├── service/
│   │   │   ├── customer/
│   │   │   ├── notification/
│   │   │   ├── payment/
│   │   │   └── tenant/
│   │   ├── industries/             # Industry plugins
│   │   │   ├── beauty/             # MVP: Staff, beauty-specific logic
│   │   │   └── restaurant/         # Future: table booking
│   │   ├── admin/                  # Admin-only endpoints
│   │   ├── auth/                   # Authentication & authorization
│   │   └── common/                 # Guards, interceptors, filters, pipes
│   ├── prisma/
│   │   ├── schema.prisma
│   │   └── migrations/
│   ├── .env
│   └── package.json
│
├── booking-web/                    # Git repo — Next.js
│   ├── src/
│   │   ├── app/
│   │   │   ├── (customer)/         # Customer-facing booking pages
│   │   │   └── (admin)/            # Admin portal pages
│   │   ├── components/
│   │   └── generated/              # Auto-generated API client
│   ├── .env
│   └── package.json
│
├── booking-mobile/                 # Git repo — React Native (Expo)
│   ├── src/
│   │   ├── features/
│   │   │   ├── booking/
│   │   │   ├── schedule/
│   │   │   ├── pos/
│   │   │   └── staff/
│   │   ├── navigation/
│   │   │   ├── owner/              # Owner-specific screens
│   │   │   └── staff/              # Staff-specific screens
│   │   ├── components/
│   │   └── generated/              # Auto-generated API client
│   ├── .env
│   └── package.json
│
├── docs/                           # Shared documentation
│   └── architecture.md
├── docker-compose.yml              # Shared: PostgreSQL + Redis + MinIO
├── product-brief-salon-booking-2026-03-10.md
└── CLAUDE.md                       # AI context cho toàn bộ workspace

Type Sharing Strategy

OpenAPI codegen — API là single source of truth cho types.

booking-api (NestJS + Swagger decorators)
  │
  ├── npm run generate:openapi → openapi.json
  │
  ▼
openapi.json ──► openapi-typescript-codegen (hoặc orval)
                      │
                ┌─────┴──────┐
                ▼            ▼
          booking-web/   booking-mobile/
          generated/     generated/
          api-client.ts  api-client.ts
  • NestJS Swagger plugin tự generate openapi.json từ decorators
  • Web/Mobile chạy codegen script → import typed API client
  • Không cần npm package, không cần monorepo tooling

NestJS Module Architecture

AppModule
├── CoreModule (forRoot)
│   ├── TenantModule            # Multi-tenant resolution, tenant context
│   ├── ResourceModule          # CRUD Resource (abstract)
│   ├── ServiceModule           # CRUD Service catalog
│   ├── BookingModule           # Booking engine, scheduling logic
│   ├── CustomerModule          # Customer management
│   ├── NotificationModule      # SMS, push, email templates
│   ├── PaymentModule           # Payment tracking (MVP: manual)
│   └── AuditModule             # Mandatory audit trail
│
├── IndustryModule
│   ├── BeautyModule            # MVP
│   │   ├── StaffResource       # Staff extends Resource
│   │   ├── BeautyService       # Nail, haircut, etc.
│   │   ├── WalkInHandler       # Walk-in specific logic
│   │   ├── SelfPickService     # Staff self-pick bookings
│   │   └── BeautyPosService    # POS checkout flow
│   └── (future modules...)
│
├── AuthModule
│   ├── JWT strategy
│   ├── Role-based access (owner, staff, customer, admin)
│   └── Tenant-scoped guards
│
└── AdminModule                 # Super-admin: create tenants, support access

Multi-tenancy Strategy

Shared database, shared schema, tenant_id discriminator.

  • Mọi query tự động filter by tenant_id (NestJS middleware/interceptor)
  • Prisma middleware hoặc custom decorator inject tenant context
  • Row-level security (RLS) ở PostgreSQL level làm safety net
// Mọi entity có tenant_id
interface TenantScoped {
  tenantId: string;
}

// Request context mang tenant info
interface RequestContext {
  tenant: { id: string; industryType: string; settings: TenantSettings };
  user: { id: string; role: Role };
}

Subdomain routing:

  • nailsbyanna.app.no → resolve tenant → load branding + services
  • api.app.no + X-Tenant-ID header (hoặc từ JWT)

Offline-First Strategy (Mobile)

Brief yêu cầu offline mode cho staff app.

  • WatermelonDB (SQLite-based, React Native) cho local data
  • Sync protocol: last-write-wins với conflict detection
  • Critical offline operations: view schedule, check-in customer, mark payment
  • Sync khi online: queue changes → bulk push → pull latest state

Authentication Flow

Customer: Email/phone → OTP verification → JWT
Staff:    Invite link from owner → set password → JWT  
Owner:    Email + password → JWT (+ optional 2FA)
Admin:    Email + password + 2FA mandatory → JWT

All JWTs contain: userId, tenantId, role, permissions

Notification Architecture

Template-based, per industry:

Template: "booking_confirmed"
├── beauty: "Hei {customer}! Din time hos {staff} er bekreftet for {date} kl {time}"
├── restaurant: "Hei {customer}! Bord for {partySize} er bekreftet for {date} kl {time}"

Channels: SMS (primary for Norway), Push notification, Email (secondary) Provider: Configurable (Twilio, MessageBird, or local Norwegian SMS provider)