Next.js - ¿Cómo estructurar un proyecto SaaS?

En muchos tutoriales sobre Next.js se explica cómo crear páginas, consumir APIs o montar blogs. Sin embargo, pocos abordan un aspecto clave: cómo estructurar un producto real preparado para crecer.

Cuando desarrollamos un SaaS (Software as a Service), la complejidad aumenta significativamente. Ya no se trata solo de mostrar contenido, sino de diseñar una arquitectura capaz de escalar, separar responsabilidades, gestionar la autenticación y manejar datos de forma eficiente.

En este artículo te presento una forma estructurada de cómo organizar un proyecto con Next.js orientado a aplicaciones SaaS.

Una breve introducción a Next.js

Next.js es un framework de React de código abierto para el desarrollo web full-stack creado por la empresa Vercel. Permite construir aplicaciones SaaS completas combinando frontend y backend en un mismo proyecto.

Estas son algunas de las características más importantes a tener en cuenta cuando utilizamos esta tecnología:

  • Renderizado Híbrido: Soporta renderizado del lado del servidor (SSR) para datos dinámicos y generación de sitios estáticos (SSG) para alto rendimiento.
  • Enrutamiento automático
  • Optimización automática: Optimización de imágenes, fuentes y scripts para mejorar la velocidad.
  • API Routes: permite la creación de endpoints de API (en el backend) dentro del mismo proyecto.
  • Soporte de TypeScript

Separación clave: Marketing vs Aplicación

Cuando pensamos en la arquitectura de la aplicación que queremos desarrollar, es recomendable separar el producto en dos partes:

  • Zona pública (marketing): Orientada a la captación de clientes, mediante una landing page, pricing, el blog y el SEO.
  • Zona privada (aplicación): La lógica de negocio, donde el usuario interactúa con el producto (dashboard, datos, funcionalidades).

Esta separación permite optimizar cada parte de forma totalmente independiente, es decir, podemos aplicar SEO y rendimiento en marketing y lógica y experiencia de usuario en aplicación.

Estructura de proyecto recomendada

Al diseñar la arquitectura de un proyecto debemos tener en cuenta desde el inicio una estructura orientada a la escalabilidad, con este enfoque, reducimos costes directos en mantenibilidad y rendimiento a posteriori. 

Organizamos el proyecto en tres capas:

Separar “Public Area” de la “Aplicación” no es únicamente una decisión organizativa, sino una base clave para escalar un SaaS correctamente. Es por ello que, organizar el proyecto por responsabilidades, en vez de por tipo de archivo, facilita la escalabilidad del código.

Ejemplo de estructura

my-saas/
├── app/
│ ├── (marketing)/
│ │ ├── page.tsx # Landing page
│ │ ├── pricing/
│ │ └── blog/
│ ├── (app)/
│ │ ├── dashboard/
│ │ ├── settings/
│ │ └── layout.tsx # Layout con auth guard
│ └── api/
│ └── [...route]/
├── components/
│ ├── ui/ # Componentes genéricos
│ └── dashboard/ # Componentes específicos de app
├── lib/
│ ├── db.ts
│ └── auth.ts
├── hooks/
└── types/

Flujo de datos en un SaaS 

En una arquitectura típica, el flujo de datos sigue el siguiente patrón:

UI → Route Handler → Service Layer → Database

Next.js permite la integración de este flujo mediante:

  • Route Handlers (backend dentro del propio proyecto)
  • Server Components (fetch de datos en servidor)

Esto reduce directamente la necesidad de tener que separar el backend y el frontend en proyectos pequeños o medianos.

Server vs Client Components

Uno de los cambios más importantes introducidos en Next.js es la distinción entre Server Components y Client Components. Entender bien cuándo usar cada uno es fundamental para construir aplicaciones eficientes y escalables.

Los Server Components se ejecutan de lado del servidor y se envían ya renderizados al cliente. Esto implica varias ventajas:

  • Permite la obtención de datos directamente desde el servidor (base de datos, APIs internas, etc)
  • Reduce la cantidad de código JavaScript que se envía al navegador.
  • Mejora el tiempo de carga inicial y reduce el tiempo hasta que la página es interactiva.
  • Se utiliza cuando queremos mostrar contenidos que no requieren de interacción inmediata.

Estas ventajas en un entorno SaaS suelen aplicarse en:

  • Dashboard (carga de datos iniciales)
  • Listados (proyectos, usuarios, métricas)
  • Landing pages orientadas a SEO

La utilización de este tipo de componentes permite mantener la lógica sensible fuera del cliente, lo cual beneficia a la aplicación en términos de seguridad.

Ejemplo de carga de datos sin JavaScript en cliente

// app/(app)/dashboard/page.tsx // No hace falta 'use client' → es Server Component por defecto
async function DashboardPage() { const data = await db.query("SELECT * FROM projects");
return ; }

Los Client Components se ejecutan directamente en el navegador y permiten añadir interactividad a la aplicación. Son utilizados para:

  • Gestionar el estado local (useState, useReducer…)
  • Responder a eventos del usuario (clicks, inputs, animaciones)
  • Implementar lógica reactiva en tiempo real

Su uso es imprescindible en elementos como:

  • Formularios
  • Modales
  • Filtros dinámicos
  • Componentes interactivos en el dashboard

Pero, hay que tener en cuenta que abusar de este tipo de componentes puede afectar directamente al rendimiento de la aplicación, ya que estos, aumentan el tamaño del bundle de JavaScript.

Ejemplo de barra de filtrado en cliente

// components/ui/FilterBar.tsx
"use client";

import { useState } from "react";

export function FilterBar({ onFilter }) {
  const [query, setQuery] = useState("");

return (
    <input
      value={query}
      onChange={(e) => {
        setQuery(e.target.value);
        onFilter(e.target.value);
      }}
      placeholder="Buscar..."
    />
  );
}

Y con esto presente, ¿cómo conviene combinarlos?

Cuando diseñamos una aplicación, no se trata de elegir entre uno u otro, sino de combinarlos correctamente. Por ejemplo:

  • Landing page → principalmente Server Components
    • Contenido estático, SEO, carga rápida
  • Dashboard → combinación de ambos
    • Server components para cargar datos
    • Client components para interacción
  • Formularios y UI Interactiva → Client Components

Si se tiene en cuenta este patrón, permite aprovechar lo mejor de cada modelo, nos aporta rendimiento y eficiencia en el servidor e interactividad y dinamismo en el cliente. Esto implica un cambio de mentalidad, pasar de “todo en el cliente” a “server-first” es uno de los factores que diferencian una aplicación bien estructurada y bien optimizada de una que no lo está.

¿Cómo gestionar la escalabilidad?

La escalabilidad es la capacidad de una aplicación para crecer en usuarios, datos y funcionalidades sin degradar el rendimiento ni la mantenibilidad. Por ello, es fundamental tenerla en cuenta desde el diseño inicial de la estructura del proyecto. Una buena organización permite que la aplicación evolucione de forma eficiente, facilitando la incorporación de nuevas funcionalidades sin comprometer su estabilidad a largo plazo. Para abordar estos retos, Next.js nos ofrece varias herramientas nativas:

  • Caché y revalidación: mediante fetch en Server Components se puede controlar cuánto tiempo se cachea una respuesta y cuándo se regenera.
  • ISR (Incremental Static Regeneration): Páginas estáticas que se regeneran automáticamente cada X segundos, son ideales en Landing Pages o blogs con datos que cambian poco.

Conclusión

Daniel D. Full-Stack Developer | Next.js, React, Node

Next.js no es solo un framework, sino una base para construir productos reales. La diferencia entre una aplicación funcional y un producto real no está en la tecnología utilizada, sino en cómo se estructura desde el principio.

Adoptar una arquitectura clara, separar responsabilidades y entender el flujo de datos son aspectos clave para desarrollar un SaaS escalable, mantenible y preparado para crecer.

No es sorpresa que Next.js se haya posicionado como uno de los frameworks orientados al desarrollo web full-stack con mayor integración dentro de equipos profesionales.

Referencias