Problema
Times crescem, rotas multiplicam-se e o App Router facilita colocar tudo em app/ sem critério. O resultado comum é: lógica de negócio misturada a UI, fetch duplicado, componentes client gigantes e imports circulares. Em produção isso vira débito técnico rápido.
Solução
Tratar app/ como camada de composição (rotas, layouts, loading e error boundaries) e extrair regras para módulos estáveis: features/, lib/, server/, components/. Definir regras explícitas: o que é Server Component por padrão, onde use client é permitido e como acessar dados (Server Actions, route handlers, serviços).
Arquitetura
app/
(marketing)/...
(dashboard)/...
api/...
features/
billing/
ui/
actions.ts
service.ts
lib/
db.ts
env.ts
components/
ui/ # primitivos reutilizáveis
- Rotas agrupadas por contexto
(auth),(app)reduzem ruído em URLs. - Co-location de
loading.tsxeerror.tsxpor segmento melhora UX e observabilidade. - Features isolam caso de uso;
service.tsconversa com DB/API;actions.tsvalida input e chama o serviço.
Código
// features/orders/service.ts — sem "use client"
import { db } from "@/lib/db";
export async function listOrdersForUser(userId: string) {
return db.order.findMany({ where: { userId }, take: 50 });
}// app/(app)/orders/page.tsx — Server Component
import { listOrdersForUser } from "@/features/orders/service";
import { OrderTable } from "@/features/orders/ui/order-table";
export default async function OrdersPage() {
const userId = "..."; // sessão / claims
const orders = await listOrdersForUser(userId);
return <OrderTable initial={orders} />;
}Performance
Server Components por padrão reduzem JS no bundle. Segmentar dados por rota evita waterfalls: cada layout busca só o necessário. Use React.cache para deduplicar leituras no mesmo request tree quando vários componentes precisam do mesmo recurso.
Melhorias futuras
Camada de contratos (Zod/OpenAPI) compartilhada entre actions e clients; feature flags por ambiente; testes de integração em rotas críticas com Playwright.
Conclusão
Escalabilidade no App Router é menos sobre pastas “bonitas” e mais sobre fronteiras claras entre roteamento, UI e domínio. Quem revisa seu repositório precisa enxergar decisões — não só arquivos.