When starting a full-stack project, the default advice in the JavaScript ecosystem today is almost always: "Just use a monorepo with Next.js for everything."
Bundle the public customer shop, the private admin portal, the background jobs, and the database queries into one repository.
For simple websites, that works fine. But when we started designing the CIVA hardware platform, I decided to deliberately break the architecture into three independent repositories:
civa-api: Standalone Express 5 REST server + MongoDB database coreciva-admin: Standalone React 19 / Vite single-page applicationciva-storefront: Standalone Next.js 16 (App Router) customer application
Here is why that decision paid off.
1. Radical Separation of Performance Priorities
A customer storefront and an internal admin dashboard have completely opposite performance and rendering requirements:
- The Customer Storefront needs ultra-fast First Contentful Paint, edge caching, server-side rendering for SEO, lightweight bundles, and seamless mobile interactions.
- The Admin Dashboard has zero SEO requirements. It needs dense data grids (thousands of rows), heavy chart libraries, instant in-memory filtering, complex modal state, and long-lived websocket connections.
Putting dense administrative SPA logic inside Next.js server components creates awkward compromises. Building the admin as a standalone Vite SPA gave us sub-second hot reloads and zero SSR bundle overhead.
2. A Hard Security Perimeter
When your backend API is a distinct, authoritative service, there is zero risk of database connection strings or secret environment variables accidentally leaking into client bundles:
┌──────────────────────────┐ ┌──────────────────────────┐
│ Customer Storefront │ │ Admin Operations SPA │
│ (Next.js 16 / SSR) │ │ (Vite / React 19) │
└────────────┬─────────────┘ └────────────┬─────────────┘
│ │
│ HTTPS / REST API │ HTTPS / REST + 2FA
└──────────────────┬─────────────────┘
▼
┌──────────────────────────┐
│ Authoritative Server │
│ (Express 5 / MongoDB 8) │
└──────────────────────────┘
The server treats both the public shop and the admin portal as untrusted consumers that must authenticate through strict rate-limited endpoints, JWT rotation, and role-based permissions.
The Takeaway
Clean boundaries create mental clarity.
Instead of fighting one massive codebase where changing a UI utility risks breaking a database transaction, three independent services let each part of the system optimize for its exact job.