Architecture
Royal Glow is a monolithic modular monorepo, not microservices. Strict layer boundaries give the modularity of microservices without the ops burden — and three hard conventions hold everywhere: money is integer paise, IDs are app-generated text, and business logic never imports the database or framework.
Layered monorepo
The Bun + Turborepo workspace holds five apps/packages workspaces: The codebase enforces a strict dependency direction so business rules stay testable and framework-agnostic. Each layer may only import inward.API routes are thin orchestrators: parse → validate with Zod → call business logic → return JSON. No database queries live in route handlers.
Hosting topology
The platform spans a Render origin and a Cloud Run service, backed by Neon, and is migrating to AWS. Pick a concern below.- Tab Title
- Tab Title
- Tab Title
- Tab Title
The customer site (
rgss-web) and admin portal (rgss-admin) are Next.js 16 App Router apps
served as Node servers (next build + next start) on Render, with the service catalogue
cached in Redis (5-min TTL).The target is AWS Lambda + CloudFront, deployed with SST (which wraps OpenNext): an ARM64 SSR
function and a CloudFront distribution per app, in ap-southeast-1 beside the Neon database. Only
these two apps move — the CMS stays on Render and every managed dependency stays put, so no
application code changes. See M2AWS.md.Cloudflare Workers via OpenNext was the original plan and is retired — the Worker bundle
exceeded Cloudflare’s free-plan script size limit, and the adapter has been removed from the repo.Observability
Five layers, each on a free tier. All third-party browser scripts load only after explicit cookie consent and only when their keys are configured.Conventions
These hold across the whole monorepo and are covered in depth on the Conventions page.
- Money is stored as integer paise; never floating point.
- Timestamps are
timestamptz(stored UTC, displayed IST). - Dates display as DD/MM/YYYY and currency in Indian grouping (₹1,00,000.00).
- Every external integration is a guarded extension point: with no key configured it no-ops and logs, so the whole monorepo builds and runs locally without secrets.