Skip to main content

Architecture

This document describes the high-level architecture of Sessiq, including system design, components, data flow, and key architectural decisions.

System Overview

Sessiq is a multi-tenant SaaS platform designed as a modular monolith with clear domain boundaries. It provides booking management for leisure activities, supporting multiple venues, products, resources, and customers.

Architecture Style

Modular Monolith: All modules run in a single process but are organized as distinct domain modules with well-defined interfaces. This provides:

  • Clear separation of concerns
  • Independent testing capabilities
  • Easier future migration to microservices if needed
  • Simpler deployment and operations

Tech Stack

ComponentTechnologyVersion
API FrameworkNestJS (Fastify 5 adapter)11.x
FrontendNext.js (App Router)15.x
UI LibraryReact19.x
DatabasePostgreSQL + Prisma ORM15+ / 5.x
RuntimeNode.js20+
Package Managerpnpm (workspaces)9.x
PaymentsStripe + Paytrail14.x
TestingVitest (unit) + Jest (int/e2e)2.x / 30.x

Backend:

  • Framework: NestJS 11 (with Fastify 5 adapter for performance)
  • Language: TypeScript (strict mode)
  • Database: PostgreSQL 15+ with Prisma ORM
  • Caching: Redis (for availability caching)
  • Background Jobs: Worker service with queue processing

Frontend:

  • Framework: Next.js 15 (App Router)
  • UI Library: React 19
  • Styling: Tailwind CSS
  • State Management: React hooks and context

Infrastructure:

  • Containerization: Docker
  • Orchestration: Docker Compose (local), Kubernetes (production)
  • Observability: Prometheus metrics, structured logging

External Services:

  • Payments: Stripe, Paytrail
  • Notifications: Brevo (email), Twilio (SMS)
  • Storage: PostgreSQL (data), S3 (files, future)

System Architecture

High-Level Components

┌─────────────────────────────────────────────────────────────┐
│ Client Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Web App │ │ Mobile App │ │ Admin UI │ │
│ │ (Next.js) │ │ (Future) │ │ (Next.js) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ API Gateway (BFF) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ NestJS API Server (Fastify) │ │
│ │ • Authentication & Authorization │ │
│ │ • Request Routing │ │
│ │ • Input Validation │ │
│ │ • Response Transformation │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ Domain Modules │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Catalog │ │ Scheduling │ │ Booking │ │
│ │ Module │ │ Engine │ │ Orchestrator │ │
│ └─────────────┘ └──────────────┘ └──────────────┘ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Pricing │ │ Payments │ │ Notifications│ │
│ │ Engine │ │ Module │ │ Module │ │
│ └─────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ Data Layer │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ PostgreSQL with Row-Level Security (RLS) │ │
│ │ • Multi-tenant data isolation │ │
│ │ • ACID transactions │ │
│ │ • Audit trails │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ Background Services │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Worker │ │ Outbox │ │ SSE Stream │ │
│ │ Service │ │ Processor │ │ (Admin) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘

Core Modules

1. Catalog Module

Purpose: Manage products, variants, resources, and add-ons.

Key Components:

  • Product CRUD operations
  • Variant management
  • Resource allocation
  • Add-on configuration
  • Site management

Responsibilities:

  • Product definition and metadata
  • Resource capacity configuration
  • Add-on inventory tracking
  • Catalog versioning

2. Scheduling & Availability Engine

Purpose: Generate available time slots from schedule templates, overrides, and resource availability.

Key Components:

  • Schedule template processor
  • Slot generation engine
  • Availability calculator
  • Capacity checker
  • Label matcher

Responsibilities:

  • Recurring pattern processing
  • Timezone handling
  • Blackout window management
  • Capacity pool management
  • Slot label application

Data Flow:

Schedule Templates → Effective Hours → Slot Generation →
Capacity Check → Label Matching → Available Slots

3. Booking Orchestrator

Purpose: Manage the booking lifecycle from hold to confirmation using a Saga pattern for distributed transactions.

Key Components:

  • Saga orchestrator: Coordinates multi-step workflows
  • State machine: Tracks booking status transitions
  • Compensation handlers: Automatic rollback on failures
  • Policy evaluator: Validates business rules
  • Capacity locker: Ensures resource availability
  • Hold management: TTL-based automatic expiration

Booking Lifecycle:

Hold → Pricing → Payment → Confirm → Notify → Complete
│ │ │ │ │ │
└───────┴────────┴─────────┴────────┴─────────┘
Saga Steps with Compensation

Booking Statuses:

  • hold: Temporary reservation (15-minute TTL by default)
  • tentative: Hold extended for payment processing
  • part_paid: Partial payment received (voucher + external)
  • paid: Fully paid and confirmed
  • canceled: Customer cancelled
  • no_show: Customer did not arrive
  • completed: Activity finished

Saga Pattern:

  • Each step is transactional with checkpointing
  • Automatic compensation for rollback on failures
  • Retry mechanisms with exponential backoff
  • Idempotency guarantees via correlation IDs
  • State tracking: not_started → running → completed/failed
  • Compensation states: none → pending → in_progress → completed

Multi-Slot Bookings:

Bookings can span multiple time slots with atomic capacity management:

  • BookingSlot junction table links slots to bookings
  • Capacity consumed tracked per slot
  • Atomic locking across all slots during hold creation
  • Cascading release on cancellation

Hold Expiration:

  • Automatic TTL-based expiration (default: 15 minutes)
  • Worker service processes expired holds
  • Capacity automatically released
  • Prevents slot hogging

4. Pricing Engine

Purpose: Calculate booking prices with rules, modifiers, promotions, and tax.

Key Components:

  • Price resolver service
  • Ruleset evaluator
  • Promotion validator
  • Tax calculator
  • Pricing hash generator

Pricing Flow:

Base Price → Tier Pricing → Temporal Modifiers →
Promotions → Vouchers → Tax → Final Price

Price Rules:

  • Base prices
  • Party size tiers
  • Temporal modifiers (peak/off-peak)
  • Participant type pricing
  • Label modifiers

5. Payments Module

Purpose: Process payments through multiple providers with webhook handling and comprehensive payment lifecycle management.

Key Components:

  • Payment adapters: Provider-specific implementations
    • Stripe adapter: Credit/debit cards with Authorize & Capture
    • Paytrail adapter: Finnish payment gateway for local methods
  • Payment orchestrator: Coordinates payment workflow
  • Refund engine: Automatic and manual refund processing
  • Webhook processors: Secure webhook verification and handling
  • Payment ledger: Transaction history and auditing
  • Fee assessment service: No-show and cancellation fees

Payment Flow:

Authorization → Capture → Confirmation → Webhook →
Ledger Update → Notification

Supported Methods:

  • Stripe: Credit/Debit cards with 3D Secure
  • Paytrail: Finnish local payment methods (online banking, mobile)
  • Payment on Arrival (PoA): Pay at venue
  • Vouchers: Gift cards and prepaid credits
  • Mixed Tender: Voucher + external payment method

Payment Security:

  • Webhook signature verification (HMAC)
  • Replay attack prevention with idempotency keys
  • PCI DSS compliant (tokenization)
  • Audit trail for all transactions

Refund Processing:

  • Automatic refund calculation based on cancellation policy
  • Fee deduction for late cancellations
  • Partial refund support
  • Multi-step refund workflow
  • Async processing with compensation

No-Show Fee Management:

  • Automatic fee assessment on no-show status
  • Configurable fee amounts and policies
  • Payment capture for fees
  • Grace period handling

6. Notifications Module

Purpose: Send email and SMS notifications with templating, reliable delivery tracking, and the Outbox Pattern for at-least-once delivery guarantees.

Key Components:

  • Notification service: Event-driven notification creation
  • Template engine: Dynamic content personalization
  • Outbox pattern: Transactional event store
  • Outbox worker: Background processing with retry logic
  • Provider adapters: Multi-channel delivery
    • Brevo: Transactional email delivery
    • Twilio: SMS delivery (future)
  • Delivery tracking: Status monitoring and DLQ handling
  • Alert evaluator: Threshold-based alerting for failures

Notification Flow:

Event → Template Selection → Personalization →
Outbox Write (Same TX) → Worker Picks Up →
Provider Adapter → Delivery → Mark Sent
↓ (on failure)
Retry with Backoff → DLQ after max attempts

Notification Types:

  • Booking confirmations
  • Payment receipts
  • Cancellation notices
  • Pre-arrival reminders
  • No-show alerts
  • Payment on Arrival (PoA) reminders

Outbox Pattern Details:

  • Transactional writes: Notifications written to outbox in same transaction as booking
  • At-least-once delivery: Guaranteed delivery even if app crashes
  • Worker-based processing: Background workers poll outbox table
  • Exponential backoff: Configurable retry intervals (1s, 2s, 4s, 8s, etc.)
  • Dead Letter Queue (DLQ): Failed messages after max retries
  • Idempotent consumers: Providers handle duplicate delivery
  • Monitoring: Alert when DLQ threshold exceeded

DLQ Alert System:

  • Monitors outbox message failures
  • Threshold-based alerting (configurable: 5, 10, 20+ messages)
  • Admin dashboard for DLQ inspection
  • Manual retry capability
  • Root cause analysis support

7. Saga Orchestration Module

Purpose: Coordinate distributed transactions across multiple services with automatic compensation on failures using the Saga pattern.

Key Components:

  • Saga orchestrator: Central coordinator for multi-step workflows
  • State machine: Tracks saga execution state and progress
  • Retry worker: Background service for automatic retry with exponential backoff
  • Compensation handlers: Rollback logic for each saga step
  • Checkpoint manager: State persistence for failure recovery
  • Correlation tracking: End-to-end tracing with correlation IDs

Saga States:

  • not_started: Saga initialized but not yet executing
  • running: Saga currently executing steps
  • completed: All steps completed successfully
  • failed: Saga failed and compensation completed
  • compensating: Rolling back completed steps
  • timeout: Saga exceeded maximum execution time
  • cancelled: Saga manually cancelled

Compensation States:

  • none: No compensation needed
  • pending: Compensation queued
  • in_progress: Actively compensating
  • completed: All compensations successful
  • partial: Some compensations failed
  • failed: Compensation failed completely

Saga Execution Flow:

Initialize Saga → Execute Step 1 → Checkpoint

Execute Step 2 → Checkpoint

Execute Step 3 → Checkpoint
↓ (Success)
Mark Complete

(On Failure at Step 3)

Trigger Compensation

Compensate Step 2 → Compensate Step 1

Mark Failed with Compensation

Key Features:

  • Checkpointing: Each step success persisted before next step
  • Automatic retry: Failed steps retried with exponential backoff (1s, 2s, 4s, 8s, 16s...)
  • Idempotency: Steps can be safely retried without side effects
  • Correlation IDs: Track saga execution across logs and services
  • Timeout handling: Maximum execution time prevents infinite loops
  • Manual intervention: Admin can retry or cancel sagas
  • Monitoring: Real-time saga state visibility

Common Saga Patterns:

Booking Confirmation Saga:

1. Lock capacity
2. Calculate final price
3. Process payment
4. Confirm booking
5. Send notifications

On failure: Release capacity → Refund payment → Cancel booking

Cancellation Saga:

1. Validate cancellation policy
2. Release capacity
3. Process refund
4. Update booking status
5. Send cancellation notice

On failure: Re-lock capacity → Cancel refund → Restore booking

Implementation Details:

  • Saga state stored in Booking table (saga_state, saga_correlation_id)
  • Worker service polls for pending sagas and retries
  • Compensation handlers mirror forward step logic
  • All saga operations logged with correlation ID
  • Failed sagas after max retries moved to DLQ for manual review

8. Promotions & Vouchers Module

Purpose: Manage discount codes, promotional campaigns, and gift card/voucher systems with sophisticated stacking rules.

Key Components:

  • Promotion service: Validation and application logic
  • Voucher service: Gift card balance and redemption
  • Stacking engine: Multi-promotion conflict resolution
  • Promotion validator: Eligibility checking (date, usage, minimums)
  • Voucher ledger: Balance tracking and transaction history

Promotion Types:

  • Percentage discounts: 10%, 25%, 50% off
  • Fixed amount: €10 off, €25 off
  • Free items: Complimentary add-ons
  • BOGO: Buy-one-get-one promotions
  • Seasonal campaigns: Holiday specials, peak/off-peak

Voucher Features:

  • Pre-loaded balance: Gift cards with initial amount
  • Partial redemption: Use portion of balance
  • Expiration dates: Time-limited validity
  • Stock tracking: Limited quantity vouchers
  • Usage limits: Single-use or multi-use
  • Transfer restrictions: Non-transferable vouchers

Promotion Stacking:

Booking Price: €100
├─ Promotion 1 (20% off): -€20 → €80
├─ Promotion 2 (€10 off): -€10 → €70
└─ Voucher (€30 balance): -€30 → €40 (final price)

Stacking Rules:
- Maximum 2 percentage promotions
- Fixed amount promotions stack additively
- Vouchers applied last
- Final price cannot be negative

Validation Rules:

  • Minimum spend requirements
  • Product/variant eligibility
  • Date range (valid from/to)
  • Usage per customer limits
  • Total usage limits across all customers
  • Blackout dates exclusions
  • Site/location restrictions

Conflict Resolution:

When multiple promotions conflict:

  1. Check mutual exclusivity rules
  2. Apply highest-priority promotion first
  3. Validate stacking permissions
  4. Calculate cumulative discount
  5. Ensure minimum price thresholds

Voucher Redemption Flow:

Apply Voucher Code → Validate Stock → Check Balance →
Check Expiration → Lock Balance (Tentative) →
Payment Complete → Deduct Balance → Mark Used
↓ (On Failure)
Release Locked Balance → Voucher Available Again

9. Admin Module

Purpose: Provide comprehensive administrative interfaces and operations for managing bookings, resources, and platform configuration.

Key Components:

  • Admin controllers: CRUD operations for all admin entities
  • Calendar management: Real-time calendar with SSE streaming
  • Resource maintenance: Scheduled downtime and capacity adjustments
  • Reporting endpoints: Analytics and business intelligence
  • Audit logging: Comprehensive action tracking
  • No-show management: Fee processing and customer tracking
  • Booking move API: Precheck and commit booking rescheduling

Calendar Management (SSE):

Server-Sent Events (SSE) streaming for real-time calendar updates:

Client connects → SSE stream opens →
Real-time events pushed to client:
- New bookings created
- Booking status changes
- Cancellations
- Resource availability updates
- Maintenance windows

Calendar Features:

  • Multi-site view: View bookings across multiple locations
  • Drag-and-drop rescheduling: Visual booking moves with validation
  • Real-time sync: All admins see updates instantly via SSE
  • Capacity visualization: Color-coded capacity indicators
  • Time slot management: Block/unblock slots for maintenance
  • Filtering: By site, product, status, date range

No-Show Management:

Automated no-show detection and fee processing:

Booking Start Time + Grace Period → Auto-detect no-show →
Assess Fee → Process Payment → Update Status → Notify Customer

No-Show Features:

  • Grace period: Configurable time after start (default: 15 minutes)
  • Manual marking: Admin can mark no-show before auto-detection
  • Fee assessment: Percentage or fixed fee from booking price
  • Payment capture: Automatic charge for no-show fees
  • Customer notifications: Email/SMS alerts
  • Dispute handling: Admin override and refund capability
  • Tracking dashboard: No-show rates and fee collection stats

Booking Move API:

Two-phase commit for safe booking rescheduling:

Phase 1 - Precheck:

POST /api/v1/admin/bookings/{id}/move/precheck

Validates:
- New slot availability
- Capacity constraints
- Policy compliance
- Price difference calculation

Returns: Move plan with price adjustment

Phase 2 - Commit:

POST /api/v1/admin/bookings/{id}/move/commit

Executes:
- Release old slot capacity
- Lock new slot capacity
- Update booking details
- Process payment adjustment (if needed)
- Send notifications

All operations atomic via Saga pattern

Resource Maintenance:

  • Scheduled downtime: Plan resource unavailability
  • Capacity adjustments: Temporary capacity changes
  • Recurring maintenance: Weekly/monthly patterns
  • Impact analysis: Preview affected bookings
  • Automatic notifications: Alert customers of changes

Admin Dashboard:

  • Real-time metrics: Active bookings, revenue, capacity utilization
  • Quick actions: Common operations accessible from dashboard
  • Alert notifications: DLQ failures, payment issues, high no-show rates
  • Activity feed: Recent booking changes and admin actions

Reporting & Analytics:

  • Revenue reports by site/product/time period
  • Booking trends and forecasting
  • No-show rate tracking
  • Promotion effectiveness analysis
  • Capacity utilization metrics
  • Customer behavior insights

Data Flow

Booking Creation Flow

1. Customer selects product/variant

2. Query availability (Scheduling Engine)

3. Select slot and add-ons

4. Calculate price (Pricing Engine)

5. Apply promotions/vouchers

6. Create hold (Booking Orchestrator)

7. Process payment (Payments Module)

8. Confirm booking (Orchestrator)

9. Send notifications (Notifications Module)

10. Update capacity (Scheduling Engine)

Availability Query Flow

1. Request availability

2. Get effective schedules (Scheduling Engine)

3. Generate slots for date range

4. Check capacity (Capacity Checker)

5. Apply blackouts/maintenance

6. Match slot labels

7. Return available slots

Payment Processing Flow

1. Payment request

2. Validate payment method

3. Authorize payment (Payment Adapter)

4. Create payment intent

5. Process payment

6. Webhook received

7. Update booking status

8. Send confirmation

Multi-Tenancy Architecture

Tenant Resolution

The system automatically resolves tenant context from the request domain, enabling white-label deployments:

Resolution Methods (in priority order):

  1. JWT Token - Tenant ID extracted from authenticated user
  2. Subdomain - acme.booking.com → tenant acme
  3. Custom Domain - booking.acme.com → mapped tenant
  4. Header Fallback - x-tenant-id header (development/API clients)

Resolution Flow:

Request → TenantResolverMiddleware → TenantGuard → RLS Context
(subdomain/domain) (validates) (enforces)

Local Development:

For local testing, use *.localhost subdomains:

# Access demo tenant
http://demo.localhost:3000

# API calls
curl http://demo.localhost:3001/api/v1/availability

See the Tenant Resolution Guide for detailed configuration.

Tenant Isolation

Row-Level Security (RLS):

  • Database-level tenant filtering
  • Automatic query filtering
  • Prevents data leakage
  • No application-level filtering needed

Implementation:

-- Example RLS policy
CREATE POLICY tenant_isolation ON booking
FOR ALL
USING (tenant_id = current_setting('app.current_tenant_id')::text);

Tenant Context:

  • Set per-request via middleware
  • Available throughout request lifecycle
  • Enforced at database level
  • Cannot be bypassed

Tenant Data Model

Tenant (root)
├── Sites (venues)
│ ├── Products (activities)
│ │ ├── Variants
│ │ └── Resources
│ └── Schedules
├── Bookings
├── Payments
└── Configuration

Key Design Decisions

1. Modular Monolith

Decision: Use modular monolith instead of microservices.

Rationale:

  • Phase 1 doesn't require microservices complexity
  • Easier development and deployment
  • Can split later if needed
  • Clear module boundaries prepare for migration

2. Saga Pattern for Bookings

Decision: Use Saga pattern for distributed booking transactions.

Rationale:

  • Multiple steps (hold, pricing, payment, confirm)
  • Need compensation on failures
  • Long-running transaction
  • Better than 2PC for this use case

3. Row-Level Security

Decision: Enforce tenant isolation at database level.

Rationale:

  • Security by default
  • Prevents data leakage
  • Simplifies application code
  • Performance at database level

4. Outbox Pattern

Decision: Use outbox pattern for notifications.

Rationale:

  • Guarantees delivery
  • Prevents lost notifications
  • Allows retry mechanism
  • Eventually consistent

5. Pricing Hash

Decision: Use pricing hash for price consistency.

Rationale:

  • Prevents price changes mid-flow
  • Ensures price consistency
  • Audit trail
  • Customer fairness

Scalability Considerations

Horizontal Scaling

API Server:

  • Stateless design
  • Can scale horizontally
  • Load balancer distribution
  • Shared database connection pool

Worker Service:

  • Process queues independently
  • Horizontal scaling supported
  • Job distribution via queues

Database Scaling

Read Replicas:

  • Availability queries can use replicas
  • Reduces load on primary
  • Eventual consistency acceptable

Partitioning:

  • Potential tenant-based partitioning
  • Not implemented in Phase 1
  • Can be added for large tenants

Caching Strategy

Availability Caching:

  • Cache slot generation results
  • TTL-based invalidation
  • Redis for shared cache
  • Cache warming for popular queries

Security Architecture

Authentication

JWT Tokens:

  • Stateless authentication
  • Tenant ID in claims
  • Role-based claims
  • Token expiration

Authorization

RBAC (Role-Based Access Control):

  • Roles: Admin, Operator, Viewer
  • Permissions: Resource-level
  • Policy-based evaluation
  • Audit logging

Data Protection

Encryption:

  • TLS in transit
  • Encryption at rest (database)
  • Secure credential storage
  • PII handling

Audit Trail

Comprehensive Logging:

  • All mutations logged
  • Immutable audit records
  • User attribution
  • Compliance ready

Observability

Logging

Structured Logging:

  • JSON format
  • Correlation IDs
  • Log levels
  • Context enrichment

Metrics

Prometheus Metrics:

  • Request counts
  • Response times
  • Error rates
  • Business metrics

Tracing

Distributed Tracing (Future):

  • Request correlation
  • Performance analysis
  • Dependency mapping

Error Handling

Error Types

Validation Errors:

  • Input validation
  • Business rule violations
  • Returned to client

System Errors:

  • Infrastructure failures
  • Retryable errors
  • Circuit breakers

Compensation:

  • Saga compensation
  • Rollback operations
  • Idempotent handlers

Resilience Patterns

Retries:

  • Exponential backoff
  • Max retry limits
  • Idempotent operations

Circuit Breakers:

  • Failure threshold
  • Automatic recovery
  • Fallback handlers

Degradation:

  • Graceful degradation
  • Feature flags
  • Fallback modes

Development Workflow

Module Structure

Each module follows consistent structure:

module-name/
├── module-name.controller.ts # REST endpoints
├── module-name.service.ts # Business logic
├── module-name.module.ts # NestJS module
├── dto/ # Data Transfer Objects
└── __tests__/ # Tests

Shared Packages

@booking/db: Database schema and DAL @booking/contracts: Shared TypeScript types @aetech/scheduling-engine: Scheduling logic @repo/observability: Logging and metrics

Future Architecture Evolution

Potential Migrations

Microservices (if needed):

  • Extract modules to services
  • Maintain clear boundaries
  • API gateway for routing
  • Service mesh for communication

Event-Driven (future):

  • Event sourcing for bookings
  • CQRS pattern
  • Event bus for decoupling

Caching Layer:

  • More aggressive caching
  • Cache invalidation strategies
  • CDN for static assets

Performance Targets

API Response Times:

  • 95th percentile < 200ms
  • Availability queries < 100ms
  • Booking creation < 500ms

Throughput:

  • Support 1000+ concurrent users
  • Handle peak booking periods
  • Scale horizontally

Database:

  • Query optimization
  • Index strategy
  • Connection pooling

Next Steps