- SSR (Server-Side Rendering): Server returns HTML for a route. Browser may later attach JS (“hydrate”) for interactivity.
- SPA (Single-Page App): Server returns a shell (HTML + JS bundle). JS handles routing/data on the client; page doesn’t reload.
- MPA (Multi-Page App): Traditional pages; each URL returns fresh HTML (often SSR).
- MVC: Architectural pattern (Model–View–Controller). Not tied to rendering mode—just organization.
- Hydration: Client JS attaches event handlers to server-rendered HTML to make it interactive.
- BFF (Backend for Frontend): A backend tailored to a specific UI, often aggregating multiple services.
-
Spring Boot + Thymeleaf (Java)
- Rendering: SSR (MPA).
- Pattern: MVC.
- Data: Controllers call services/DB; Thymeleaf renders HTML.
- Servers needed: 1 (monolith).
-
Express + Pug (Node)
- Rendering: SSR (MPA).
- Pattern: MVC-ish (routes/controllers + views).
- Servers needed: 1 (monolith).
-
React/Angular/Vue SPA + API (Spring/Express/etc.)
- Rendering: CSR (client-side) after initial bundle.
- Data: Fetch via REST/GraphQL.
- Servers needed: 2 (frontend asset server + backend API). You can host both in one process, but conceptually it’s two apps.
-
Next.js (React meta-framework)
- Can do SSR, SSG (static gen), ISR (revalidate), client components, and server components (rendered on the server).
- Has API routes (Node handlers) and in the App Router Server Actions.
- Can talk directly to a DB (with proper pooling/auth)—so it can be a BFF or even the only backend for many apps.
- Servers needed: flexible: 1 (monolith), or split into 2 (Next as BFF + separate microservices), depending on scale.
Misconception to fix: “Next cannot connect DB” — It can. On server routes/components you can use Prisma, Sequelize, official drivers, etc. On serverless/edge you must manage connection pooling (e.g., Prisma Data Proxy, PgBouncer, or use provider-managed pooling).
Think logical components vs processes/servers. You can co-locate many roles in one process.
A) One server (monolith)
- Spring Boot + Thymeleaf (HTML) + Controllers + DB
- Express + Pug + Routes + DB
- Next.js (Pages/App Router) + API routes/Server Actions + DB Pros: simple ops, zero CORS, shared code. Cons: scaling and team boundaries can blur as it grows.
B) Two servers
- SPA server (React/Angular/Vite/Next static export) + API server (Spring/Express/Nest).
- Next.js (BFF: UI + some APIs) + Core backend (Java/Node/Go) for heavy business logic. Pros: clearer separation, scale independently. Cons: CORS, more infra, API contracts to manage.
C) Three+ servers (service/microservice mesh)
- Next.js front/BFF + multiple domain services (auth, orders, inventory…). Pros: autonomy, scale per domain, resilience. Cons: ops overhead, distributed complexity, observability needed.
- “SSR = MVC only 1 server” → SSR can be MVC or not; and it can run on 1 or many servers. SSR is about where HTML is rendered, not team topology.
- “SSR + SPA needs 3 servers (Next + backend)” → Not necessarily. Next.js alone can do SSR + SPA-like interactivity + APIs in 1 server. You might add a separate backend (making 2) for scale or domain separation. You only get to 3+ when you split into multiple backend services.
Small/medium product or prototype
- Use Next.js monolith: UI (Server & Client Components) + API routes/Server Actions + DB.
- Benefits: great DX, SSR for SEO, SPA feel post-hydrate, fewer moving parts.
Growing app with heavier domain logic
- 2 servers: Next.js (BFF + SSR) + a backend (Spring Boot/NestJS) for complex workflows, scheduled jobs, and integrations.
- Keep auth/session and request shaping in BFF; domain services stay clean.
Large org, many domains
- Next.js front/BFF + multiple microservices (Java/Node/Go) + async messaging (Kafka/SQS), API gateway, observability stack.
-
“true SSR” examples
- Spring/Thymeleaf: Controller → Service/Repo → Thymeleaf template → HTML.
- Express/Pug: Route → Service → Pug template → HTML.
- Next.js: Route (server component) → fetch DB → return JSX → HTML, then hydrate.
-
Data fetching
- MVC: Controller queries DB and renders view.
- SPA: Browser calls API; API queries DB.
- Next.js: Server components/actions or API routes query DB; client calls these endpoints where needed.
“SSR + SPA—can’t we do it in 2?” Yes. Next.js gives you SSR + SPA feel in one app. Add a single backend service (if needed) and you’re at 2. Many teams run just Next.js (1 server) successfully for a long time.
“Can Express do what Next does?” Express can render templates (SSR) and serve APIs, but it doesn’t provide the React rendering pipeline (server components, routing conventions, SSG/ISR, automatic code-splitting, image/font optimizations, built-in bundling). You’d have to assemble those yourself.
“Is Next a BFF?” It can be. When you put API routes/Server Actions in Next and call downstream services/DB, it’s effectively a BFF.
- SEO + fast first paint + minimal ops: Next.js monolith.
- Strong React SPA + strict backend separation: SPA (Vite/CRA) + separate backend.
- Java-heavy org & server-rendered HTML: Spring Boot + Thymeleaf (or Express + Pug in Node world).
- Long-term React with SEO and flexibility: Next.js front + domain backend(s).
MVC = Model–View–Controller.
- Model = data & business logic (DB, queries, domain rules).
- View = templates or components that generate UI (HTML, JSON, etc.).
- Controller = glue: takes input (HTTP request), asks the Model for data, passes it to the View, and returns a response.
👉 In Spring Boot + Thymeleaf, MVC is formal:
- Model = JPA entities / service layer
- Controller = annotated
@Controllerclasses - View = Thymeleaf templates that render HTML
👉 In Express + Pug, MVC is more “loose” (hence MVC-ish):
- Controller = route handlers (
app.get('/todos', ...)) - View = Pug templates
- Model = sometimes you code it cleanly (separate DB layer), sometimes you just query the DB inline. So it’s not a strict pattern unless you enforce it → that’s why I said “MVC-ish.”
SSR (Server-Side Rendering) just means HTML is generated on the server and sent to the client.
-
SSR with MVC:
- Spring Boot Controller queries DB (Model) → Thymeleaf (View) generates HTML → browser displays it.
- Classic “MVC” web app.
-
SSR without MVC:
- Next.js Server Component fetches data directly and returns HTML.
- There’s no explicit “controller class” or “model layer” — just React code that happens to run on the server and output HTML.
- Still SSR, but not necessarily MVC.
So: SSR = rendering strategy; MVC = architectural pattern. They can overlap, but one does not imply the other.
Some people think “SSR = monolith.” Not true.
-
You can do SSR in a monolith: one server handles HTML + DB + business logic (e.g., Spring Boot + Thymeleaf).
-
Or in a multi-tier system:
- Next.js server renders HTML (SSR)
- Calls APIs from a backend service (Java/Spring, Node, etc.)
- Backend service fetches DB
- Next returns HTML to browser
👉 Both are SSR, regardless of server count. That’s why I wrote: SSR is about where HTML is rendered, not team topology (1 vs. many servers).
✅ Summary in one line:
-
MVC = how you structure your code (Model/Controller/View).
-
SSR = where HTML is generated (server vs. client).
-
You can have:
- SSR + MVC (Spring Boot + Thymeleaf)
- SSR without MVC (Next.js server components)
- MVC without SSR (React SPA with MVC-ish backend APIs).