Architecture Diagram
This document gives a high-level view of how Tutor fits together: the browser, the single Next.js application (pages and API), the serverless PostgreSQL database, object storage, the license server, and integration providers. Tutor is a tutoring & education booking marketplace delivered as one deployable Next.js 16 app — there is no separate API server, no ORM, and no message broker.
System Diagram
┌──────────────────────────────────────────────┐
│ BROWSER │
│ Public storefront · Customer · Host (tutor) · │
│ Admin panel · Influencer · Installer │
└───────────────────────┬──────────────────────┘
│ HTTPS (page loads + fetch /api/v1/*)
▼
┌───────────────────────────────────────────────────────────────────────┐
│ NEXT.JS 16 APP (App Router, React 19) │
│ │
│ src/app │
│ ├─ (site) public storefront + lesson catalogue + booking │
│ ├─ (auth) customer / host account auth │
│ ├─ admin/(panel) admin panel (cookie: admin_token) │
│ ├─ customer/(app) customer portal (cookie: member_session) │
│ ├─ host/(app) host (tutor) portal (cookie: member_session) │
│ ├─ influencer/(app) affiliate portal (cookie: influencer_session) │
│ ├─ install first-run setup wizard │
│ └─ api/v1/* ROUTE HANDLERS = the API (~290 route.ts files) │
│ │
│ each handler → auth/permission → ensure<Domain>Schema() → SQL │
│ │
│ src/lib db · auth (4 sessions) · booking spine · r2 · secrets · i18n │
│ src/core + src/product channels · plugins · payments · adapters │
│ + integration/feature add-ons │
└───┬───────────────┬────────────────┬────────────────┬────────────────┘
│ SQL (HTTPS) │ S3 API │ HTTPS │ HTTPS / SMTP
▼ ▼ ▼ ▼
┌────────────┐ ┌─────────────┐ ┌──────────────┐ ┌─────────────────────┐
│ PostgreSQL │ │ Cloudflare │ │ License │ │ Integration │
│ (Neon, │ │ R2 / S3 │ │ server │ │ providers │
│ serverless)│ │ storage │ │ (CreativeCape│ │ (payment gateways / │
│ │ │ │ │ RS256, off- │ │ email / sms / video /│
│ raw SQL, │ │ uploads, │ │ line verify)│ │ chat / analytics / │
│ no ORM │ │ media, i18n │ │ │ │ captcha) via add-ons │
└────────────┘ └─────────────┘ └──────────────┘ └─────────────────────┘
Client
The browser is served the storefront plus four portals from the same application, separated by App Router route groups (and gated by their respective cookies):
- Public storefront (
src/app/(site)) — home, lesson catalogue/search, lesson detail, booking & checkout, blogs, FAQ, gallery, about, contact, how-it-works, become-a-tutor, legal pages. - Customer portal (
src/app/customer/(app)) — dashboard, bookings, payments, messages, notifications, reviews, wishlist, support, settings. Gated bymember_session. - Host (tutor) portal (
src/app/host/(app)) — dashboard, listings (lessons), calendar/sessions, bookings, inbox, insights, payouts, billing (subscription), reviews, settings. A host is a signed-in customer who owns an activehostsrow, so it also usesmember_session. - Admin panel (
src/app/admin/(panel)) — listings, bookings, hosts, customers, refunds, reviews, coupons, extras, CMS, theme builder, master-data, settings, license. Gated byadmin_token. - Influencer / affiliate portal (
src/app/influencer/(app)) — dashboard, referral links, clicks/visits, commissions, bookings, payouts, marketing materials, analytics. Gated byinfluencer_session.
All UI is rendered by Next.js server and client components; all data access goes through
/api/v1/*.
API
The API is route handlers, not a standalone server. Every route.ts under src/app/api is an
endpoint; the repository contains ~290 of them, versioned under /api/v1, with /api/install
and /api/license outside the version prefix. An auto-generated OpenAPI document
(scripts/gen-openapi.ts) is served at /api/openapi.json and rendered by Scalar at
/api/docs/v1 — both admin-gated.
Representative /api/v1 resource segments:
portal · customer · host · influencer · admin ·
listings · bookings · amenities · extras · coupons · reviews ·
blogs · pages · faqs · gallery · hero-sliders · testimonials · brand · destinations ·
countries · currencies · languages · locations · i18n · forms · booking-settings ·
calendar-config · captcha · chat · chatbot · livechat · video · analytics · plugins · cron · ext
Each handler typically (1) checks auth (getSession() admin, getCustomerId() customer,
getHostId() host, getInfluencerId() influencer) and per-route permissions on admin routes,
(2) calls the relevant ensure*Schema(), (3) validates and re-quotes server-side, (4) runs raw
SQL, and (5) returns JSON. The public/mobile API is the non-admin surface; admin endpoints are
excluded from the API reference.
Layer Diagram — Core vs Product
The extensibility system splits stable framework code (src/core) from swappable add-ons
(src/product). Add-ons are aggregated at build time into generated index files, which the
core registries import.
┌──────────────────────────────── src/core (stable contracts + registries) ───────────────┐
│ │
│ channels/registry.ts → CHANNELS = mergeChannels( │
│ email (FREE: SMTP), payments (FREE built-ins), storage (FREE: R2/S3/GCS), │
│ analytics, video, calendar, captcha, sms, chat, …ADDON_CHANNELS ) │
│ │
│ plugins/registry.ts → FEATURES = [ …ADDON_FEATURES ] (FeaturePlugin contract: │
│ nav · admin/portal/public pages · api · hooks · ensureSchema · settings) │
│ │
│ payments/registry.ts → PAYMENT_DRIVERS = [ …built-in drivers, …serverAdapters(payment)]│
│ │
│ adapters/client.ts·server.ts → clientAdapters(kind) / serverAdapters(kind) │
│ resolve a provider implementation by kind + provider at runtime │
└───────────────────────────────────────────▲────────────────────────────────────────────┘
│ imports the GENERATED indexes
┌────────────────────────── src/product (add-ons + generated aggregation) ─────────────────┐
│ │
│ addons/<id>/ addon.json + index.ts (+ schema.ts / api.ts / *.tsx) │
│ provider add-ons → payments-stripe, payments-mollie, email-sendgrid, │
│ analytics-posthog, chatbot-anthropic, calendar-google_calendar, … │
│ feature add-ons → announcements, audit-logs, digest, api, maps │
│ │
│ addons/index.ts [GENERATED] → ADDON_CHANNELS │
│ features/index.ts [GENERATED] → ADDON_FEATURES │
│ adapters-server/index.ts [GENERATED] → ADDON_SERVER_ADAPTERS (by kind) │
│ adapters-client/index.ts [GENERATED] → ADDON_CLIENT_ADAPTERS (by kind) │
│ (produced by scripts/bundle-addons.mjs — do not hand-edit) │
└──────────────────────────────────────────────────────────────────────────────────────────┘
The same relationships expressed as a Mermaid graph:
graph TD
B[Browser] -->|HTTPS| APP[Next.js 16 App]
subgraph APP[Next.js 16 App Router]
SITE["(site) storefront + booking"]
ADMIN["admin/(panel)"]
CUST["customer portal"]
HOST["host portal"]
INFL["influencer portal"]
API["api/v1/* route handlers"]
CORE[src/core registries]
PROD[src/product add-ons]
end
PROD -->|generated indexes| CORE
API -->|raw SQL| DB[(PostgreSQL / Neon)]
API -->|S3 SDK| R2[(Cloudflare R2 / S3)]
APP -->|offline RS256 verify + heartbeat| LIC[License server]
API -->|via add-ons| INTG[Payments / Email / SMS / Video / Analytics]
Database
- Engine: PostgreSQL, hosted on Neon, reached through
@neondatabase/serverless(HTTP-based driver) fromsrc/lib/db.ts. - No ORM, no migration tool. Tables are created and evolved by idempotent
ensure*Schema()functions (CREATE TABLE IF NOT EXISTS+ALTER TABLE … ADD COLUMN IF NOT EXISTS). - The model centres on the booking spine —
listings(lessons) →experience_sessions(lesson times withseats_total/seats_booked) →bookings(keyed onsession_id+seats+booking_mode) — plusbooking_extras/booking_payments/booking_refunds,amenities/extras/coupons/gift_cards, hosts + subscriptions + earnings/payouts, customers, influencers, a CMS, and i18n reference data. See the Database Documentation.
Storage
- File uploads are sent to Cloudflare R2 (or any S3-compatible bucket) through the AWS S3 SDK in
src/lib/r2.ts. Per-language UI translation JSON is also stored in R2. - The active storage provider and credentials are read from the
integration_connectionstable (channelSTORAGE), encrypted with AES-256-GCM, withR2_*environment variables as a fallback. - Images can be transformed before upload; clients receive the public object URL.
Third-Party Services
| Provider | Used for |
|---|---|
| Neon | Managed serverless PostgreSQL |
| Cloudflare R2 / S3-compatible | Object storage (lesson media, images, i18n JSON, documents) |
| CreativeCape license server | Domain license activation + heartbeat (RS256, verified offline) |
| Payment gateways | Booking checkout — PayPal REST etc. (built-in) + Stripe, Mollie, Paystack… add-ons |
| SMTP / email providers | Transactional email (verification, OTP, booking notifications) |
| SMS / WhatsApp | OTP + booking/cancellation notifications (add-ons) |
| Video hosts | Lesson media (add-ons) |
| Analytics / pixels | GA4/GTM, Meta Pixel, PostHog, Clarity, TikTok — add-ons |
| Captcha / live chat / chatbot | reCAPTCHA; Crisp/Intercom/Tawk…; OpenAI/Anthropic/OpenRouter — add-ons |
| AI translation | OpenAI / Anthropic — machine-translate dynamic content, cached in translations |
Which providers are active is resolved at runtime from admin-configured integration_connections,
and provider code ships in the corresponding add-on — so integrations activate without changing
core code. Secrets are stored encrypted and never returned to the client in plaintext.
© CreativeCape Solutions · creative-cape.com · support@creative-cape.com