Why Architecture Matters Before You Need It
Most early-stage SaaS teams make the same mistake: they design for the users they have, not the users they are trying to get. A product that handles 500 users cleanly often needs a near-complete rebuild to handle 50,000 — not because the code is bad, but because the architecture was never designed to scale.
The good news is that building for scale does not mean building complexity upfront. It means making a handful of key decisions correctly in the first sprint — decisions that keep the path to scale open without adding unnecessary overhead to getting to launch.
Here is the architecture pattern we use for every Next.js SaaS product we ship.
SaaS Architecture Stack
Layer 1: The Next.js Edge
The App Router is not just a file-based routing system. Used correctly, it is a full-stack architecture layer. React Server Components render on the server (or at the edge), reducing JavaScript bundle size and eliminating client-side data fetching for most pages.
We keep the client boundary as far down the component tree as possible — only components that need interactivity (forms, dropdowns, real-time state) are marked as Client Components. Everything else stays server-rendered. This alone cuts First Contentful Paint by 40–60% in most apps we migrate.
The Multi-Tenant Data Model
Getting multi-tenancy right from the start is the single most important architectural decision in a B2B SaaS. There are three approaches: shared schema (all customers in one table, filtered by org_id), schema-per-tenant (separate Postgres schemas), and database-per-tenant (maximum isolation, highest cost).
We default to shared schema with row-level security for startups. The data model is simpler, the operational overhead is lower, and row-level security in PlanetScale or Postgres provides the isolation that regulated customers need. Moving to schema-per-tenant later is tractable; retrofitting multi-tenancy onto a single-tenant schema is not.
Always add org_id to every row from day one
Even if you launch with single-tenant mode, the column needs to be there. Retrofitting it costs weeks.
Never trust the client to send org_id
Always derive it server-side from the session. Client-side org selection is a permissions bug waiting to happen.
Use database-level RLS for sensitive tables
Prisma middleware handles most filtering, but for financial data or PII, add a second enforcement layer at the database.
Performance Numbers That Actually Matter
<800ms
target TTFB on all routes
95+
Lighthouse performance score
99.9%
uptime SLA we design for
100k+
concurrent users before rearchitect
Building a SaaS product?
We design and build Next.js SaaS products from first principles — architecture review, full-stack development, or both. Tell us what you are working on.
Start a Conversation →