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.jsontừ 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 + servicesapi.app.no+X-Tenant-IDheader (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)