Da Bagunça ao Nirvana: Como Parei de Sofrer com Estrutura de Projetos Next.js
Fala, galera do TabNews! 👋
Lembram quando eu postei aqui sobre a melhor forma de estruturar projetos React com Vite? Pois é... aquele post já tem uns bons anos nas costas, e adivinha? Eu evoluí (espero que vocês também 😅).
Hoje venho compartilhar minha nova solução para o problema eterno de todo dev: como organizar um projeto fullstack sem virar um completo caos.
A Jornada do Sofrimento (aka: Minha Evolução)
Quando comecei com React, era aquela bagunça clássica:
- Componentes misturados com lógica de negócio
- Pasta
utilsvirando um depósito de tudo - Imports quilométricos tipo
../../../components/form/input/TheOneInput.tsx - Aquela sensação de "onde diabos eu coloco esse arquivo?"
Daí veio o post sobre React + Vite, onde eu organizei tudo bonitinho. Funcionou bem... até eu começar a fazer projetos fullstack de verdade.
Enter Next.js: O Plot Twist
O Next.js mudou o jogo (e quebrou minha estrutura antiga 💀). De repente eu tinha:
- Server Components e Client Components
- Server Actions
- Rotas que são pastas
- Middleware para autenticação
- Um monte de conceitos novos
E minha estrutura antiga? Não fazia mais sentido.
Então, depois de muito café, refatorações dolorosas e discussões acaloradas com o duck de borracha na minha mesa, eu criei algo que finalmente funciona.
O Template que Mudou Minha Vida (dramático, eu sei)
Criei um template fullstack Next.js que uso em TODOS os meus projetos agora. E o melhor? É open source!
👉 github.com/igorroc/fullstack-next-template
O Que Tem Nessa Belezura?
Stack Completa:
- ✅ Next.js 16 (o mais novo!)
- ✅ TypeScript (porque somos adultos responsáveis)
- ✅ Prisma + PostgreSQL (banco de dados de verdade)
- ✅ NextUI (componentes bonitos sem esforço)
- ✅ Docker (pra subir o banco sem choro)
- ✅ Autenticação completa (com JWT e tudo mais)
Mas o Real MVP é a Arquitetura:
src/
├── app/ # Next.js App Router
│ ├── auth/ # Rotas de autenticação
│ └── profile/ # Páginas protegidas
│
├── components/ # UI Components (Client)
│ ├── auth/ # Formulários, etc
│ └── profile/ # Componentes de perfil
│
├── features/ # Business Logic (Server Actions)
│ ├── auth/ # login.ts, register.ts
│ └── users/ # get-all-users.ts
│
└── lib/ # Utilitários compartilhados
├── utils/ # validators.ts
├── auth.ts # Funções de autenticação
└── db.ts # Prisma client
Por Que Essa Estrutura é Diferente?
1. Separação Clara de Responsabilidades
Chega de ficar pensando "onde eu coloco isso?":
- Lógica de negócio? →
features/ - Componentes visuais? →
components/ - Utilitários? →
lib/ - Páginas? →
app/
É tipo aqueles organizadores de gaveta: cada coisa tem seu lugar.
2. Organização por Feature (não por tipo)
Em vez de ter:
❌ components/Button.tsx
❌ components/Form.tsx
❌ actions/login.ts
❌ actions/register.ts
Você tem:
✅ features/auth/
├── login.ts
├── register.ts
└── index.ts
✅ components/auth/
├── login-form.tsx
├── register-form.tsx
└── index.ts
Tudo relacionado à autenticação fica junto. Quando você precisa mexer em auth, sabe exatamente onde ir.
3. Imports Limpos (graças aos Barrel Exports)
Sabe aqueles imports horríveis?
❌ import { loginAction } from "@/actions/auth/login"
❌ import { registerAction } from "@/actions/auth/register"
❌ import { logoutAction } from "@/actions/auth/logout"
Agora é assim:
✅ import { loginAction, registerAction, logoutAction } from "@/features/auth"
Cada pasta tem um index.ts que exporta tudo. É tipo um porteiro que facilita sua vida.
4. kebab-case em TUDO
Sim, eu escolhi uma side nesse eterno debate:
login-form.tsx✅LoginForm.tsx❌
Por quê? Consistência. URLs são kebab-case, então por que não os arquivos? Menos coisa pra pensar, mais coisa pra programar.
Exemplo Prático: Adicionando uma Nova Feature
Digamos que você quer adicionar um sistema de produtos. Aqui vai o passo a passo:
1. Cria a Server Action:
// src/features/products/get-products.ts
"use server"
import db from "@/lib/db"
export async function getProducts() {
return await db.product.findMany()
}
2. Exporta:
// src/features/products/index.ts
export { getProducts } from "./get-products"
3. Cria o Componente:
// src/components/products/product-list.tsx
"use client"
import { Card } from "@nextui-org/react"
export function ProductList({ products }) {
return (
<div className="grid gap-4">
{products.map(p => (
<Card key={p.id}>{p.name}</Card>
))}
</div>
)
}
4. Usa na Página:
// src/app/products/page.tsx
import { getProducts } from "@/features/products"
import { ProductList } from "@/components/products"
export default async function ProductsPage() {
const products = await getProducts()
return <ProductList products={products} />
}
Pronto! Tudo organizado, fácil de encontrar, fácil de manter.
O Que Já Vem Pronto?
Para não começar do zero (porque a vida é curta), o template já vem com:
- 🔐 Sistema de autenticação completo (register, login, logout)
- 🛡️ Middleware de proteção de rotas
- 👤 Página de perfil com lista de usuários
- 🗄️ PostgreSQL no Docker (basta um
npm run compose:up) - 🎨 NextUI configurado (dark mode e tudo)
- 📝 TypeScript super configurado
- 🔄 Migrations automáticas com Prisma
É literalmente dar um git clone e começar a programar.
As Lições Que Aprendi (e Você Não Precisa Aprender na Dor)
1. Clean Architecture Não é Overengineering
Já ouvi muito: "isso é muito complexo para um projeto simples!"
Spoiler: não é. É justamente o contrário. Quando você tem uma estrutura clara desde o início, ADICIONAR coisas fica trivial. É tipo ter uma casa com os cômodos definidos - você sabe onde colocar cada móvel.
2. Separar Client e Server Components é CRUCIAL
Next.js 13+ tem essa distinção importante. Manter os Server Components nas páginas (app/) e Client Components separados (components/) te salva de muita dor de cabeça.
3. Barrel Exports São Seus Amigos
Aquele index.ts em cada pasta pode parecer boilerplate inútil, mas quando você tem 50 imports em um arquivo e pode resumir tudo em 3 linhas, você agradece.
4. Nomenclatura Consistente > Nomenclatura Perfeita
Não importa se você escolhe kebab-case ou PascalCase. Importa que você escolha um e siga até o fim. A consistência reduz a carga cognitiva.
Performance e Produção
Uma dúvida comum: "isso não adiciona muita complexidade?"
Resposta curta: não.
Resposta longa: essa estrutura é como ter um armário organizado. No início você gasta 5 minutos a mais organizando, mas depois economiza horas procurando as coisas.
E em produção? O Next.js otimiza tudo igual. A estrutura é só para você, dev, não sofrer.
Como Usar (TL;DR)
- Clone o repo:
git clone https://github.com/igorroc/fullstack-next-template.git meu-projeto
cd meu-projeto
bun install
- Configure o
.env:
DATABASE_DB="meu_db"
DATABASE_PASSWORD="senha_super_secreta"
POSTGRES_PRISMA_URL="postgresql://postgres:senha_super_secreta@localhost:5432/meu_db"
AUTHENTICATION_SECRET_KEY="seu_jwt_secret_aqui"
- Sobe o banco:
bun compose:up
bun migrate
- Roda:
bun dev
Pronto! Você tem um fullstack app rodando com autenticação, banco de dados e tudo mais.
Bun vs npm: Por Que Eu Escolhi a Velocidade
Uma coisa que você deve ter notado nos exemplos: eu uso tanto npm quanto bun nos comandos. Mas vou ser sincero: eu prefiro Bun. E vou te contar por quê.
O Que Diabos é Bun?
Para quem não conhece, o Bun é um runtime JavaScript que veio para dar um chega pra lá no Node.js. E quando eu digo "veio com força", não é exagero. O negócio é ABSURDAMENTE rápido.
A Diferença na Prática
Vou te mostrar números reais do meu próprio setup:
Instalando dependências deste template:
- npm: ~45 segundos ⏰
- bun: ~8 segundos ⚡
Rodando o TypeScript check:
- npm: ~3.2 segundos
- bun: ~1.8 segundos
É tipo comparar uma Ferrari com um Fusca. Ambos te levam até o destino, mas um é MUITO mais divertido.
Por Que Eu Uso Bun?
1. Velocidade = Produtividade
Aqueles segundos economizados se acumulam. Já fiz as contas: em um dia de trabalho, economizo uns 15-20 minutos só de tempo de build e install. Isso é um café a mais, um episódio de podcast, ou simplesmente menos tempo olhando pra tela preta esperando.
2. TypeScript Nativo
O Bun entende TypeScript out of the box. Sem configuração, sem babel, sem transpilar. É tipo mágica, mas é só engenharia bem feita.
3. APIs Modernas
As APIs do Bun são limpas e intuitivas. Se você já sofreu com fs.readFileSync vs fs.promises.readFile, vai entender o alívio.
4. Drop-in Replacement
O mais legal? Você não precisa mudar NADA no seu código. É literalmente trocar npm por bun nos comandos:
# Com npm
npm install
npm run dev
npm run build
# Com Bun (mesma coisa, mais rápido)
bun install
bun run dev
bun run build
"Mas E a Compatibilidade?"
Boa pergunta! Aqui vai minha experiência REAL:
Funciona perfeitamente:
- Next.js (obviamente, é o que usamos aqui)
- Prisma
- NextUI / Tailwind
- React
- A maioria dos pacotes npm
Pode ter problemas:
- Alguns pacotes nativos antigos
- Ferramentas específicas de Node.js
- Alguns builders e bundlers específicos
Minha estratégia: Uso Bun em desenvolvimento (porque velocidade) e tenho npm/node na CI/CD como fallback (porque empresas gostam de estabilidade).
"Devo Migrar Pro Bun?"
Depende do seu cenário:
Use Bun se:
- ✅ Você está começando um projeto novo
- ✅ Quer velocidade de desenvolvimento
- ✅ Gosta de ferramentas modernas
- ✅ Tem um projeto pessoal ou startup
- ✅ Não tem restrições corporativas
Fique com npm se:
- ✅ Está em produção crítica há anos
- ✅ Tem dependências muito específicas/antigas
- ✅ Trabalha numa empresa que só aceita "battle-tested"
- ✅ Não quer lidar com possíveis edge cases
Minha Recomendação Honesta
Experimente o Bun! Sério. Baixa, testa nesse template, vê se funciona pro seu caso. Se funcionar (e provavelmente vai), você nunca mais vai querer voltar pro npm install lento.
E se não funcionar? npm tá ali, fiel como sempre, esperando você de braços abertos.
Como Instalar o Bun
Simples demais:
# No macOS/Linux
curl -fsSL https://bun.sh/install | bash
# No Windows (PowerShell)
irm bun.sh/install.ps1 | iex
Pronto! Agora é só usar bun no lugar de npm.
Conclusão (com Reflexão Filosófica)
Organizar código é tipo organizar a vida: todo mundo tem um jeito, mas alguns jeitos definitivamente funcionam melhor que outros.
Esse template é o resultado de anos tentando diferentes abordagens, lendo sobre arquitetura, quebrando a cara e refatorando. É o que eu gostaria de ter quando comecei.
E o melhor? É só um ponto de partida. Pegue, use, modifique, melhore. Contribua com issues e PRs. Vamos construir isso juntos!
Links úteis:
- 📦 Repositório no GitHub
- 📚 Documentação da Arquitetura
- 🚀 Template Next.js oficial (pra entender os conceitos)
E você, como organiza seus projetos Next.js? Usa uma estrutura parecida? Diferente? Conta aí nos comentários!
Ah, e se usar o template, manda um salve! Adoro saber que tá ajudando alguém. 🚀
PS: Sim, eu sei que existem outras formas de organizar projetos. Sim, todas são válidas. Não, não precisa começar uma guerra nos comentários. 😅
PPS: Se você ainda usa o Vite + React e tá feliz, continua! Nem todo projeto precisa ser fullstack. Use a ferramenta certa pro trabalho certo.
PPPS: Sobre a guerra Bun vs npm/Node: eu escolhi meu lado (Team Bun 🚀), mas você não precisa escolher o mesmo. O importante é que o código rode, não com o que ele roda. Paz e amor (mas velocidade também). ✌️