This project is a production-grade multi-tenant B2B SaaS platform that solves the core challenges of **shared infrastructure isolation**, **subscription-based monetization**, and **enterprise-grade access control**. It provides:
Master briefing file with problem statement, dev commands, and architecture principles.
This project is a production-grade multi-tenant B2B SaaS platform that solves the core challenges of shared infrastructure isolation, subscription-based monetization, and enterprise-grade access control. It provides:
billing:read, members:manage, reports:export) rather than simple admin/user dichotomies.The platform targets the "medium complexity" sweet spot: sophisticated enough for real business customers, lean enough to be built and maintained by a small engineering team.
| User Persona | Needs | Value Delivered |
|---|---|---|
| Workspace Owner | Billing control, member management, workspace settings | Unified admin panel with Stripe integration and role assignment |
| Admin | User management, audit trails, configuration | Audit log viewer, role/permission management, workspace analytics |
| Member | Day-to-day collaboration, data access | Intuitive dashboard with Tailwind UI, filtered data views per permissions |
| Platform Operator | Tenant health, revenue metrics, system observability | Aggregated analytics, subscription revenue dashboards, error monitoring |
Key Value Propositions:
tenantId indexing ensures per-tenant query isolation at the database level without managing separate databases per customer.┌─────────────────────────────────────────────────────────┐
│ Client Layer │
│ Next.js 16 (App Router) + Tailwind CSS v4 │
│ React Server Components + Server Actions │
├─────────────────────────────────────────────────────────┤
│ Authentication │
│ Better Auth (session management, multi-tenant support) │
├─────────────────────────────────────────────────────────┤
│ Application Layer │
│ Next.js API Routes (CRUD, dashboards, auth flows) │
│ Express 5 (Stripe webhooks, background API proxy) │
├─────────────────────────────────────────────────────────┤
│ Data & Billing │
│ MongoDB (native driver, no ODM) + Stripe SDK │
└─────────────────────────────────────────────────────────┘
| Choice | Rationale |
|---|---|
| Next.js 16 App Router | Server Components eliminate client-side state bloat (no Redux/Zustand needed). Server Actions handle mutations with automatic route protection. Built-in streaming for analytics dashboard data. |
| Tailwind CSS v4 | CSS-first @theme configuration replaces tailwind.config.js. Zero-runtime overhead. Native dark mode and container queries for responsive dashboards. |
| Better Auth | Purpose-built auth with built-in multi-tenant session handling, social providers, and email verification. Lighter than rolling custom JWT logic and more extensible than NextAuth for B2B flows. |
| Express 5 | Dedicated webhook receiver for Stripe (idempotency, retry handling, signature verification) isolated from Next.js API routes. Also serves as a proxy for any third-party API aggregation. Its lightweight middleware chain is ideal for focused API tasks. |
| MongoDB (native driver) | Direct driver usage (no Mongoose) provides full aggregation pipeline access for analytics, eliminates schema validation overhead, and gives precise control over index strategies critical for multi-tenant query performance. |
| Stripe | Industry-standard billing API with robust webhook system, built-in tax collection, and metered billing support for usage-based tiers. |
| Excluded | Reason |
|---|---|
| Redux / Zustand | React Server Components handle data fetching server-side; client state is minimal and managed via React useState/Context. Server Actions replace client-side mutation logic. |
| Mongoose | Native MongoDB driver provides direct access to aggregation pipelines (critical for analytics) and avoids schema validation overhead. TypeScript interfaces handle type safety at the application layer. |
| Prisma | Native driver gives finer control over MongoDB-specific features (change streams for real-time, aggregation for analytics) and avoids Prisma's query engine overhead. |
project-root/
├── apps/
│ ├── web/ # Next.js 16 application
│ │ ├── app/
│ │ │ ├── (auth)/ # Login, register, forgot password
│ │ │ ├── (dashboard)/ # Authenticated layouts
│ │ │ │ ├── workspace/[id]/
│ │ │ │ ├── analytics/
│ │ │ │ ├── billing/
│ │ │ │ └── settings/
│ │ │ ├── api/ # Next.js API routes (non-webhook)
│ │ │ └── layout.tsx
│ │ ├── components/
│ │ │ ├── ui/ # Tailwind v4 base components
│ │ │ ├── dashboard/ # Analytics charts, KPI cards
│ │ │ └── shared/ # Modals, tables, forms
│ │ ├── lib/
│ │ │ ├── mongo/ # MongoDB connection + collections
│ │ │ ├── auth/ # Better Auth helpers + RBAC
│ │ │ ├── stripe/ # Stripe client + helpers
│ │ │ └── permissions/ # Permission checking utilities
│ │ └── middleware.ts
│ │
│ └── api/ # Express 5 service
│ ├── src/
│ │ ├── routes/ # Stripe webhooks, proxy routes
│ │ ├── middleware/ # Signature verification, rate limit
│ │ └── index.ts
│ └── package.json
│
├── packages/
│ ├── types/ # Shared TypeScript interfaces
│ │ └── index.ts # Tenant, User, Permission, AuditLog types
│ └── rbac/ # Permission engine (shared between web & API)
│ └── index.ts
│
├── .env.example
├── turbo.json # Turborepo config (monorepo)
└── package.json # Root workspace
# .env — Application (Next.js)
NEXT_PUBLIC_APP_URL=http://localhost:3000
# MongoDB (native driver connection string)
MONGODB_URI=mongodb://localhost:27017/saas-platform
MONGODB_AUTH_DB=admin
# Better Auth
BETTER_AUTH_SECRET=your-better-auth-secret-min-32-chars
BETTER_AUTH_URL=http