Como estruturar projetos Next.js escaláveis com App Router
A estrutura de um projeto Next.js com App Router precisa favorecer dois pilares fundamentais: clareza e previsibilidade para o time e capacidade de evolução sustentável.
O modelo de App Router é orientado a rotas, layouts compartilhados e componentes de servidor por padrão (React Server Components). Compreender como desenhar limites de arquitetura limpos é o que separa uma aplicação rápida e fácil de manter de um projeto com acoplamento e re-renderizações desnecessárias.
Estrutura base recomendada
Adotar uma estrutura limpa na raiz da aplicação facilita a navegação e a separação de responsabilidades:
src/
app/
(site)/
layout.tsx
page.tsx
blog/
page.tsx
[slug]/
page.tsx
api/
components/
ui/
common/
features/ # ou módulos de domínio (ex: auth, blog, checkout)
blog/
actions/
components/
hooks/
schemas/
lib/
db/
services/
utils/
Diretrizes práticas de arquitetura
- Rotas em
app/apenas para composição: Evite colocar regras de negócio pesadas dentro dos arquivospage.tsx. Use as páginas para coordenar data fetching e injetar props nos componentes. - Server Components por padrão: Não adicione
"use client"a menos que precise de hooks de estado local (useState,useReducer), efeitos (useEffect), ouvintes de eventos do DOM ou bibliotecas que dependem do navegador. - Mantenha os limites de cliente nas folhas da árvore: Em vez de marcar uma página inteira como Client Component, isole apenas o botão ou widget interativo.
- Centralize conexões de infraestrutura em
lib/: Clientes de banco (como Prisma ou Drizzle), serviços de email e APIs de terceiros devem residir fora do escopo de rotas.
Server Actions e Mutações de Dados
Com Server Actions, mutações de dados acontecem diretamente no servidor, eliminando a necessidade de criar endpoints de API avulsos apenas para receber formulários. No entanto, segurança e validação são inegociáveis:
"use server";
import { z } from "zod";
import { revalidatePath } from "next/cache";
const CreateCommentSchema = z.object({
postId: z.string().uuid(),
author: z.string().min(2).max(50),
content: z.string().min(5).max(1000),
});
export async function addCommentAction(formData: FormData) {
const parsed = CreateCommentSchema.safeParse({
postId: formData.get("postId"),
author: formData.get("author"),
content: formData.get("content"),
});
if (!parsed.success) {
return { success: false, errors: parsed.error.flatten().fieldErrors };
}
// Execução no banco de dados com segurança
await db.comment.create({ data: parsed.data });
revalidatePath(`/blog/${parsed.data.postId}`);
return { success: true };
}
Checklist para manter o projeto saudável
- Consistência de nomenclatura: Mantenha convenções bem documentadas para pastas e arquivos.
- Evite vazar segredos: Utilize pacotes como
server-onlypara garantir que módulos com chaves de API nunca sejam importados por componentes de cliente acidentalmente. - Tipagem estrita: Compartilhe schemas (Zod) entre o backend/actions e os formulários no cliente.
Uma boa arquitetura não é a que usa o maior número de abstrações, mas a que permite que qualquer desenvolvedor encontre o que precisa e entregue código confiável com facilidade.