A arquitetura hexagonal (portas e adaptadores) mantém suas regras de negócio no centro e empurra frameworks, bancos de dados e filas para as bordas. Em Node.js, isso significa que rotas Express e clientes Prisma nunca vazam para o código de domínio.
Controladores gordos fingindo ser adaptadores - Rotas com 80 linhas de lógica de negócio não são hexagonais. Correção: Extraia um caso de uso; deixe a rota com 5-10 linhas.
Portas que espelham formas de ORM - save(prismaOrder) acopla o domínio ao Prisma. Correção: Mapeie entre entidades de domínio e DTOs de persistência apenas dentro do adaptador.
Uma raiz de composição gigante - main.ts se torna 400 linhas de conexão. Correção: Fábricas registerBillingModule() por módulo que retornam roteadores e serviços.
Pular adaptadores em memória - Equipes escrevem apenas adaptadores Postgres e pulam testes rápidos. Correção: Entregue InMemoryInvoiceRepository ao lado do real.
Cerimônia hexagonal em uma API de 3 rotas - Três endpoints e um desenvolvedor não precisam de quatro pastas. Correção: Comece com domínio + rotas; extraia portas quando um segundo adaptador aparecer.
Uma porta é uma interface que sua aplicação define (NotificationPort). Um adaptador é a implementação concreta que fala com o mundo real (SendGridNotificationAdapter, InMemoryNotificationAdapter).
A arquitetura hexagonal requer uma classe por caso de uso?
Não. Uma função chargeInvoice(deps, input) funciona se as dependências forem passadas explicitamente. Classes ajudam quando casos de uso carregam estado ou você usa um contêiner de DI.
Onde a validação Zod pertence?
Na fronteira do adaptador HTTP. Analise req.body em um DTO tipado, então passe objetos simples para o caso de uso. A validação de domínio cobre invariantes que o Zod não pode expressar (por exemplo, "a fatura não deve ser cobrada duas vezes").
Posso usar o layout hexagonal com NestJS?
Sim. Provedores Nest implementam portas; controladores são adaptadores de condução. Mantenha as pastas de domínio livres de @Injectable() se você quiser testes unitários sem framework.
Como compartilhar portas entre módulos?
Prefira portas locais do módulo. Se dois módulos precisarem da mesma abstração, mova a porta para shared/ports/ apenas quando um segundo consumidor existir - evite kernels compartilhados prematuros.
Trabalhadores de fila devem ser adaptadores?
Sim. Um consumidor BullMQ é um adaptador de condução que desserializa uma carga de trabalho e chama o mesmo caso de uso que sua rota HTTP.
Quantas portas por módulo são demais?
Se cada chamada externa tiver sua própria porta, você pode estar super-abstraindo. Comece com repositórios e gateways que você espera trocar ou simular em testes.
Isso funciona com Prisma?
Envolva o Prisma em um adaptador de repositório. Nunca exporte PrismaClient das camadas de domínio ou aplicação.
Como migrar incrementalmente um arquivo "god-service"?
Extraia um caso de uso e uma porta por vez. Deixe as rotas legadas chamando o código antigo até que o novo caminho seja testado, então delete o bloco antigo.
Hexagonal é o mesmo que DDD?
Relacionado, mas não idêntico. Hexagonal é sobre direção de dependência; DDD adiciona contextos delimitados, agregados e linguagem ubíqua. Você pode usar hexagonal sem DDD completo.