Pular para o conteúdo

Comparação - Hexagonal vs Clean vs Camadas

As três buscam responder à mesma pergunta: como impedir que detalhes de tecnologia (banco, framework web, mensageria) contaminem as regras de negócio? A diferença está em como organizam o código e para onde apontam as dependências.

O ponto de virada é a Inversão de Dependência. Sem ela, “camadas” acaba com o domínio dependendo do banco. Com ela, o domínio define interfaces e a infraestrutura as implementa — que é exatamente o que Hexagonal e Clean formalizam.

graph TD
    UI["Apresentação<br/>(Controllers)"] --> BLL["Regras de Negócio<br/>(Services)"]
    BLL --> DAL["Acesso a Dados<br/>(Repositories/DAO)"]
    DAL --> DB[("Banco de Dados")]

A dependência flui de cima para baixo até o banco. O problema clássico: a camada de negócio depende da camada de dados, e frequentemente dos tipos do ORM/JPA. O domínio passa a “conhecer” a tecnologia. Tende ao modelo anêmico e a regras espalhadas nos services.

graph LR
    REST["Adapter REST"] --> DP["Driving Port"]
    DP --> CORE(("Núcleo<br/>Domínio + Aplicação"))
    CORE --> DVP["Driven Port"]
    DVP -.implementada por.-> JPA["Adapter JPA"]

Organiza o mundo em dentro (núcleo com o domínio) e fora (adapters de tecnologia), conectados por portas. Ver Driving e Driven. Não impõe número de camadas internas; a fronteira é o hexágono.

graph TD
    subgraph Externo
        FW["Frameworks & Drivers<br/>(Spring, JPA, Web)"]
        IA["Interface Adapters<br/>(Controllers, Gateways, Presenters)"]
    end
    subgraph Interno
        UC["Use Cases"]
        ENT["Entities"]
    end
    FW --> IA --> UC --> ENT

Detalha o dentro em círculos concêntricos com a Regra da Dependência: o código-fonte só aponta para dentro. É mais prescritiva sobre as camadas internas (Entities, Use Cases, Interface Adapters, Frameworks). Ver Camadas da Clean Architecture.

Aspecto Camadas tradicional Hexagonal Clean
Autor difuso (anos 90) Alistair Cockburn (2005) Robert C. Martin (2012)
Metáfora pilha de camadas hexágono com portas círculos concêntricos
Direção da dependência de cima para baixo (até o DB) de fora para o núcleo de fora para dentro
Isolamento do domínio fraco (vaza tecnologia) forte (portas) forte (regra da dependência)
Prescrição de camadas internas services + DAOs livre (só define a fronteira) rígida (Entities/Use Cases/…)
Inversão de dependência opcional (e por isso rara) central central
Vocabulário camada, service, DAO porta, adapter, driving/driven entity, use case, boundary, gateway

O mapeamento entre os vocabulários deixa claro que Hexagonal e Clean descrevem quase a mesma estrutura:

Hexagonal Clean Architecture
Núcleo (domínio) Entities
Serviço de aplicação / driving port Use Case + Input Boundary
Driven port (ex: repositório) Output Boundary / Gateway (interface)
Adapter primário (controller) Interface Adapter (Controller)
Adapter secundário (gateway JPA, client HTTP) Interface Adapter (gateway); o driver/ORM em si é Frameworks & Drivers

Ver o desenvolvimento completo em Como Tudo se Conecta.

A equivalência fica visível colocando a mesma transferência bancária nas duas estruturas — os artefatos são um a um os mesmos, só mudam de crachá:

Artefato (transferir dinheiro) No projeto Hexagonal No projeto Clean
Regra de negócio (Conta, Dinheiro) domain/ domain/ (Entities)
Contrato do caso de uso port/in/TransferirUseCase port/in/TransferirInputPort
Implementação do caso de uso service/TransferenciaService usecase/TransferirInteractor
Contrato de persistência port/out/ContaRepository port/out/ContaGateway
Tradução HTTP rest/TransferenciaRestController web/TransferenciaController
Persistência concreta persistence/ContaJpaAdapter persistence/ContaGatewayJpaAdapter

O diff real entre Exemplo Hexagonal com Spring e Exemplo Clean com Spring se resume a um ponto: a Clean canônica acrescenta o output port + presenter para desacoplar também a apresentação, enquanto o Hexagonal pragmático devolve o resultado pelo próprio caso de uso.

Nenhuma delas é gratuita: portas, boundaries e mapeamentos entre modelos custam código. Em um CRUD anêmico isso é cerimônia sem retorno. O gatilho para adotá-las é domínio com regras que valem a pena proteger — normalmente onde DDD também faz sentido.

  • “Hexagonal com camadas por dentro”: criar service que chama repository concreto e chamar isso de hexagonal. Sem porta invertida, é só N-tier renomeada.
  • Clean cerimonial em CRUD: input/output boundaries, presenters e request models para um cadastro trivial. Custo alto, benefício nulo.
  • Mapear tudo em todo lugar: exageros de DTOs entre camadas viram boilerplate. Mapeie nas fronteiras que realmente isolam.
  • Confundir camada física (pacote) com camada lógica: a regra da dependência é sobre dependência de código, não sobre pastas.
  1. Por que a arquitetura em camadas tradicional costuma falhar em isolar o domínio, mesmo tendo uma camada de negócio separada?
Resposta

Porque a dependência aponta para baixo, até o banco. A camada de negócio depende da de dados (e dos tipos do ORM), então a tecnologia vaza para dentro do domínio. Falta a inversão de dependência que Hexagonal e Clean tornam central.

  1. Qual conceito de Clean corresponde a uma driven port da Hexagonal?
Resposta

A output boundary / gateway (uma interface definida por dentro e implementada na camada externa), por exemplo o repositório.

  1. Quando adotar Clean em vez de Hexagonal?
Resposta

Quando você quer disciplina explícita e prescritiva sobre as camadas internas e os boundaries (Entities, Use Cases, Interface Adapters), tipicamente em sistemas/times maiores. Hexagonal dá a mesma proteção com menos prescrição interna.

  1. Qual princípio SOLID é o mecanismo comum que faz as três “seções cruzadas” funcionarem?
Resposta

O DIP: módulos de alto nível e detalhes dependem de abstrações; o detalhe (infra) implementa a interface definida pelo núcleo.