Arquitetura Limpa em JavaScript: Dominando o Hexagonal na Prática

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Seu código é uma fortaleza: por que a Arquitetura Hexagonal salvará seu projeto JavaScript

\n\n

Você já se deparou com uma situação em que trocar de banco de dados ou substituir uma API externa se transformou em um inferno de reescrita de metade do projeto? Ou, talvez, você escrevia testes unitários que mais pareciam testes de integração, porque a lógica de negócios estava fortemente acoplada ao Express ou MongoDB? Se sim — bem-vindo ao mundo da Arquitetura Hexagonal (Ports and Adapters).

\n\n

Este não é apenas um padrão da moda dos livros de Alistair Cockburn. É uma forma prática de tornar seu código JavaScript robusto, testável e independente de frameworks. Neste artigo, não vamos dar palestras chatas. Vamos pegar um problema real (um sistema de gerenciamento de pedidos) e construí-lo passo a passo com os princípios da arquitetura hexagonal em JavaScript. Você verá como a lógica de negócios permanece imaculadamente pura, enquanto todo o "ruído" (bancos de dados, requisições HTTP, filas) fica do lado de fora.

\n\n

Pronto? Vamos lá!

\n\n

O que é Arquitetura Hexagonal (e o que portos e adaptadores têm a ver com isso)?

\n\n

Imagine um gateway de uma nave espacial. Dentro — o painel de controle (sua lógica de negócios). Fora — uma infinidade de planetas diferentes (bancos de dados, frameworks web, serviços externos). O gateway é o porto (interface). E o traje espacial que permite respirar em um planeta específico é o adaptador.

\n\n

A ideia principal: o núcleo da aplicação (core) não deve saber quem ou como está batendo à sua porta. Ele declara apenas "portos" (interfaces). E as implementações concretas (adaptadores) são conectadas externamente. Isso oferece três superpoderes:

\n\n
    \n
  • Isolamento da lógica de negócios — você troca o banco de dados sem tocar no código de cálculo de descontos.
  • \n
  • Testabilidade — você testa a lógica sem precisar subir Docker ou um banco de dados real.
  • \n
  • Independência de framework — você pode migrar do Express para o Fastify apenas escrevendo um novo adaptador.
  • \n
\n\n

Vamos para o código.

\n\n

Configuração do projeto e estrutura de pastas

\n\n

Primeiro, vamos criar uma estrutura simples. Escreveremos em TypeScript puro (ou JavaScript com JSDoc — como preferir). Nosso objetivo é um sistema de gerenciamento de pedidos.

\n\n
src/\n├── core/\n│   ├── domain/          # Entidades e Value Objects\n│   ├── ports/           # Interfaces (portos)\n│   └── services/        # Lógica de negócios (casos de uso)\n├── adapters/\n│   ├── primary/         # Adaptadores que chamam nosso núcleo (HTTP, CLI)\n│   └── secondary/       # Adaptadores que o núcleo chama (BD, APIs externas)\n└── app.js               # Ponto de entrada (montagem de tudo)\n
\n\n

Passo 1. Núcleo: Entidades de domínio e portos

\n\n

Começaremos pelo mais importante — aquilo que não depende de nada. Vamos criar a entidade Order (pedido).

\n\n

core/domain/Order.js

\n\n
export class Order {\n  constructor({ id, userId, items, status = 'pending' }) {\n    this.id = id;\n    this.userId = userId;\n    this.items = items; // Array { productId, quantity, price }\n    this.status = status;\n    this.total = this.calculateTotal();\n  }\n\n  calculateTotal() {\n    return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0);\n  }\n\n  approve() {\n    if (this.status !== 'pending') {\n      throw new Error('Only pending orders can be approved');\n    }\n    this.status = 'approved';\n  }\n}\n
\n\n

Agora vamos definir os portos — as interfaces que nosso núcleo espera do mundo externo.

\n\n

core/ports/OrderRepository.js

\n\n
// Este é um porto (interface). Não o implementamos aqui.\nexport class OrderRepository {\n  async save(order) { throw new Error('Not implemented'); }\n  async findById(id) { throw new Error('Not implemented'); }\n}\n
\n\n

core/ports/EmailService.js

\n\n
export class EmailService {\n  async sendOrderConfirmation(order) { throw new Err                

Blogs

Book Recommendations