O que são portas e adaptadores?

O nome Ports and Adapters foi proposto por Alistair Cockburn para explicar como uma aplicação pode se comunicar com o mundo externo sem fazer seu modelo depender dos meios de comunicação. O núcleo contém regras e casos de uso. Uma porta descreve uma interação necessária; um adaptador traduz um mecanismo externo para essa interação.

Uma analogia: uma tomada define o formato esperado da conexão; adaptadores permitem ligar equipamentos com plugues diferentes. A porta da aplicação expressa a necessidade, enquanto o adaptador resolve o protocolo e a tecnologia.

Portas de entrada e de saída

Uma porta de entrada (driving/primary) permite que algo peça à aplicação para fazer um trabalho: endpoint HTTP, interface de linha de comando ou consumidor de mensagem. Uma porta de saída (driven/secondary) expressa uma necessidade do núcleo, como carregar um pedido, consultar o relógio ou publicar um evento.

A direção das dependências no código importa: o caso de uso declara ou consome o contrato da porta de saída; o adaptador concreto implementa esse contrato. O framework de composição conecta os dois ao iniciar a aplicação.

Um exemplo pequeno em C#

// Porta de entrada: ação oferecida pela aplicação.
public interface IConfirmarPedido
{
    Task ExecutarAsync(Guid pedidoId, CancellationToken cancellationToken);
}

// Porta de saída: capacidade de que o caso de uso precisa.
public interface IPedidos
{
    Task<Pedido?> ObterAsync(Guid id, CancellationToken cancellationToken);
    Task SalvarAsync(Pedido pedido, CancellationToken cancellationToken);
}

public sealed class ConfirmarPedido(IPedidos pedidos) : IConfirmarPedido
{
    public async Task ExecutarAsync(Guid id, CancellationToken ct)
    {
        var pedido = await pedidos.ObterAsync(id, ct)
            ?? throw new InvalidOperationException("Pedido não encontrado.");
        pedido.Confirmar();
        await pedidos.SalvarAsync(pedido, ct);
    }
}

Um controller pode adaptar HTTP para IConfirmarPedido; um repositório baseado em EF Core pode implementar IPedidos. Nem o caso de uso precisa conhecer ASP.NET Core, nem o domínio precisa conhecer o ORM.

Como se relaciona com Clean Architecture?

As ideias se sobrepõem: ambas protegem políticas internas de detalhes externos e exigem que dependências de código apontem para abstrações mais estáveis. Clean Architecture descreve camadas concêntricas e uma regra de dependências; Hexagonal destaca portas de interação e adaptadores. São modelos compatíveis, não necessariamente arquiteturas concorrentes nem sinônimos perfeitos.

Para entender camadas e Dependency Rule em mais profundidade, leia Clean Architecture. Para traduzir essas fronteiras em projetos e referências .NET, veja como organizar projetos .NET.

O que testar — e o que evitar

Teste regras do domínio sem infraestrutura; teste o caso de uso com adaptadores controlados; teste a integração real do adaptador com o banco, broker ou provedor externo. Não confunda substituir uma porta em teste com provar que a implementação concreta funciona.

O padrão não exige uma interface para cada classe, seis projetos ou uma fábrica de abstrações. Uma porta vale quando descreve uma fronteira que ajuda a preservar regras, trocar uma tecnologia ou testar um comportamento relevante. Em um CRUD pequeno e estável, camadas extras podem custar mais do que protegem.

Leituras relacionadas