Arquitetura Hexagonal (Ports & Adapters): Desacoplando seu negócio da tecnologia

Você já viveu isso: seu sistema está cheio de regras de negócio importantes, mas elas estão emaranhadas com detalhes de banco de dados, chamadas de API externas e filas. Quando precisa trocar o banco, você mexe nas regras de negócio. Quando a API de pagamento muda, você altera a lógica central. Um pesadelo.

A Arquitetura Hexagonal (também chamada de Ports & Adapters) foi criada exatamente para resolver esse problema. Ela coloca o domínio – aquilo que torna seu negócio único – no centro, isolado de tecnologias externas. O resultado: sistemas mais testáveis, mais fáceis de evoluir e que vivem por muitos anos sem virarem legado indigesto.

Neste post, vamos entender os conceitos, ver exemplos práticos e descobrir por que essa arquitetura é uma aliada poderosa – mesmo sem usar frameworks ou linguagens específicas.

O problema da arquitetura tradicional em camadas

A clássica arquitetura em três camadas (apresentação → negócio → dados) tem uma falha sutil: a camada de negócio geralmente depende diretamente da camada de dados. Na prática, você acoplou suas regras de negócio ao banco de dados.

Se o banco muda, o serviço quebra. Se você adiciona uma API REST e também um CLI, precisa duplicar lógica. E testar o serviço exige um banco real – lento e frágil.

A proposta hexagonal: negócio no centro

A arquitetura hexagonal inverte a dependência. O domínio (regras de negócio) fica no centro, sem saber de nada externo. Tudo que entra ou sai do domínio passa por portas (interfaces). Cada tecnologia externa – banco, API, fila, UI – é um adaptador que implementa a porta adequada.

Analogia: O hexágono é o “núcleo do negócio”. As portas são tomadas (interfaces). Adaptadores são plugues que conectam o núcleo ao mundo externo.

Componentes essenciais

ComponentePapelExemplo
Domínio (Entity)Regras de negócio puras, sem dependênciasPedido, Cliente, Pagamento
Casos de uso (Use Case)Orquestração de entidades para realizar uma tarefaCriarPedidoUseCase
Portas de entradaInterfaces que o mundo externo usa para chamar o núcleoPedidoInputPort (método criarPedido)
Portas de saídaInterfaces que o núcleo usa para se comunicar com o exteriorPedidoRepositoryPort, EmailSenderPort
AdaptadoresImplementações concretas das portasPostgresPedidoRepository, SmtpEmailSender

Exemplo prático: processamento de pedidos

Vamos imaginar um sistema de e-commerce. O caso de uso: criar um pedido, calcular o total, verificar estoque (mock) e salvar.

1. Domínio puro (sem dependências externas)

// Java, mas vale para qualquer linguagem
public class Pedido {
    private String id;
    private List<Item> itens;
    private Status status;

    public double calcularTotal() {
        return itens.stream().mapToDouble(Item::getPreco).sum();
    }

    public void aprovar() {
        this.status = Status.APROVADO;
    }
}

2. Porta de saída (interface que o núcleo espera)

public interface PedidoRepository {
    void salvar(Pedido pedido);
    Pedido buscarPorId(String id);
}

3. Caso de uso (orquestração, ainda sem tecnologia)

public class CriarPedidoUseCase {
    private final PedidoRepository repository;
    private final EstoqueServicePort estoque; // outra porta de saída

    public CriarPedidoUseCase(PedidoRepository repository, EstoqueServicePort estoque) {
        this.repository = repository;
        this.estoque = estoque;
    }

    public void executar(Pedido pedido) {
        // regra: verifica estoque antes de salvar
        for (Item item : pedido.getItens()) {
            if (!estoque.temDisponibilidade(item.getId(), item.getQuantidade())) {
                throw new EstoqueInsuficienteException();
            }
        }
        pedido.aprovar();
        repository.salvar(pedido);
    }
}

4. Adaptador concreto para banco PostgreSQL

public class PostgresPedidoRepository implements PedidoRepository {
    private final DataSource dataSource;

    @Override
    public void salvar(Pedido pedido) {
        // JDBC ou JPA – detalhe tecnológico
        dataSource.getConnection().prepareStatement(...);
    }
}

5. Montagem da aplicação (injeção de dependência)

// No "main" ou framework de injeção
PedidoRepository repo = new PostgresPedidoRepository();
EstoqueServicePort estoque = new EstoqueServiceAdapter(); // chama API externa
CriarPedidoUseCase useCase = new CriarPedidoUseCase(repo, estoque);

// Adaptador de entrada (ex: REST)
@RestController
public class PedidoController {
    @PostMapping("/pedidos")
    public void criar(@RequestBody PedidoDto dto) {
        useCase.executar(dto.toPedido());
    }
}

Vantagens reais:

BenefícioExplicação
TestabilidadeTeste o caso de uso com mocks, sem banco ou API. Teste rápido e confiável.
Troca de tecnologiaMigrar de PostgreSQL para MongoDB? Crie um novo adaptador, o núcleo não muda.
Isolamento de domínioRegras de negócio não vazam para camadas externas.
Múltiplos adaptadoresA mesma porta pode ter adaptadores REST, CLI, gRPC, etc.
Evolução independenteEquipes podem evoluir adaptadores sem mexer no núcleo.

Quando NÃO usar arquitetura hexagonal?

  • Projetos muito pequenos (CRUD simples) – o overhead não compensa.
  • Times sem maturidade – exige disciplina de injeção de dependência e interfaces.
  • POCs e protótipos – a velocidade de entrega inicial será menor.

Comparação com Clean Architecture e Onion

Hexagonal é muito semelhante à Clean Architecture (Uncle Bob) e Onion. Todas compartilham a ideia de inversão de dependência. A diferença principal é que Hexagonal foca nas “portas” (interfaces) e “adaptadores”, sendo visualmente mais simples de explicar.

Como começar com hexagonal

  1. Identifique seu domínio: quais são as entidades e regras de negócio que independem de tecnologia?
  2. Defina portas de saída: quais serviços externos seu domínio precisa (repositórios, APIs, filas)?
  3. Escreva casos de uso: implemente a orquestração usando apenas interfaces.
  4. Crie adaptadores: implemente as interfaces com tecnologias reais (banco, HTTP, etc.).
  5. Monte no entry point: o main ou framework injeta adaptadores concretos nos casos de uso.

Hexagonal em linguagens dinâmicas vs estáticas

  • Java/C#/Go: use interfaces (ou structs com interfaces). Testes com mocks são diretos.
  • Python/Ruby/JavaScript: use protocolos (duck typing) ou classes abstratas. A inversão de dependência acontece igualmente.

Conclusão: isole o que importa

A Arquitetura Hexagonal não é uma bala de prata, mas é uma ferramenta valiosa para sistemas que precisam durar. Ao colocar o negócio no centro e os detalhes tecnológicos nas bordas, você ganha flexibilidade, testabilidade e longevidade.

Na Jacobus Software, aplicamos esse padrão em modernizações de sistemas críticos. O resultado: clientes que conseguem trocar banco, fornecedor de pagamento ou infraestrutura de nuvem sem reescrever a alma do negócio.

Rolar para cima