Ports
O conceito de porta
Seção intitulada “O conceito de porta”Uma porta é um contrato — uma interface — que pertence ao interior do hexágono e é expresso inteiramente na linguagem do domínio. Ela descreve o quê acontece, nunca como. Nenhuma porta deve mencionar HTTP, SQL, JSON, Kafka ou qualquer detalhe de framework; se mencionar, ela vazou tecnologia para dentro do núcleo.
A regra prática: quem define a interface é o núcleo, quem a implementa (ou consome) é a periferia. Isso mantém o fluxo de dependência sempre apontando para dentro.
Os dois tipos de porta
Seção intitulada “Os dois tipos de porta”Driving ports (portas primárias / de entrada)
Seção intitulada “Driving ports (portas primárias / de entrada)”São a API do núcleo: representam os casos de uso que a aplicação oferece. São implementadas pelo núcleo (por um application service) e chamadas pelos adapters primários (um controller REST, uma CLI, um teste).
- Direção da chamada: adapter primário → driving port.
- Direção da dependência: adapter primário → núcleo (aponta para dentro).
- Exemplo:
AbrirContaUseCase,TransferirUseCase.
package com.exemplo.banco.application.port.in;
import com.exemplo.banco.domain.Conta;import java.math.BigDecimal;
public interface AbrirContaUseCase {
Conta abrir(String titular, BigDecimal depositoInicial);}Driven ports (portas secundárias / de saída, SPIs)
Seção intitulada “Driven ports (portas secundárias / de saída, SPIs)”São SPIs (Service Provider Interfaces): o que o núcleo precisa que alguém forneça. São definidas pelo núcleo mas implementadas pelos adapters secundários (JPA, cliente HTTP, produtor de fila). É aqui que a inversão de dependência fica mais evidente: o repositório é uma interface do domínio, e a persistência a implementa.
- Direção da chamada: núcleo → driven port.
- Direção da dependência: adapter secundário → núcleo (a implementação aponta para dentro).
- Exemplo:
ContaRepository,NotificacaoGateway.
package com.exemplo.banco.application.port.out;
import com.exemplo.banco.domain.Conta;import java.util.Optional;
public interface ContaRepository {
Conta salvar(Conta conta);
Optional<Conta> porNumero(String numero);}Portas e o DIP
Seção intitulada “Portas e o DIP”Ambos os tipos de porta são a materialização do DIP - Dependency Inversion Principle: módulos de alto nível não dependem de módulos de baixo nível; ambos dependem de abstrações. No lado driven a inversão é literal — sem a interface, o application service (alto nível) dependeria do JPA (baixo nível). Com a driven port, ambos dependem da abstração declarada no núcleo. As portas são, portanto, a espinha dorsal que faz o hexágono funcionar; sem elas, restaria acoplamento direto. Veja também SOLID.
Diagrama
Seção intitulada “Diagrama”classDiagram
class AbrirContaUseCase {
<<driving port>>
+abrir(titular, deposito) Conta
}
class ContaService {
<<application service>>
+abrir(titular, deposito) Conta
}
class ContaRepository {
<<driven port>>
+salvar(conta) Conta
+porNumero(numero) Optional~Conta~
}
class ContaRestController {
<<adapter primario>>
}
class ContaJpaAdapter {
<<adapter secundario>>
}
ContaRestController ..> AbrirContaUseCase : chama
ContaService ..|> AbrirContaUseCase : implementa
ContaService ..> ContaRepository : usa
ContaJpaAdapter ..|> ContaRepository : implementa
Anti-padrões e erros comuns
Seção intitulada “Anti-padrões e erros comuns”- Porta que expõe tipos de framework: métodos que recebem/retornam
EntityJPA,ResponseEntity,Pagedo Spring. A porta deve falar só a língua do domínio. - Driving port igual à assinatura do controller: copiar DTOs de request/response para dentro da porta; a porta usa comandos e tipos do domínio, não o payload HTTP.
- Repositório genérico onipotente: uma driven port que expõe
EntityManagerou métodos CRUD infinitos vaza a tecnologia e amarra o núcleo à persistência. - Interface por vaidade: criar uma porta trivial de um método só para “ter interface”, sem múltiplos consumidores nem intenção de troca — cerimônia sem valor.
- Confundir os lados: implementar a driving port em um adapter, ou fazer o núcleo implementar a driven port. A driving é implementada dentro; a driven, fora.
Relações
Seção intitulada “Relações”- Fundamento teórico: DIP e SOLID.
- No catálogo do Fowler, uma porta é uma Separated Interface de propriedade do domínio.
- As implementações das portas são os Adapters.
- Os papéis são detalhados em Driving e Driven.
- Driven ports realizam os Repositórios do Domain-Driven Design.
- Aplicação concreta em Exemplo Hexagonal com Spring.
- Volte ao índice Arquitetura Hexagonal.
Perguntas de revisão
Seção intitulada “Perguntas de revisão”1. Qual a diferença essencial entre uma driving port e uma driven port?
Resposta
A driving port é a API do núcleo (caso de uso), implementada dentro do hexágono e chamada de fora. A driven port é uma SPI, definida no núcleo mas implementada fora, e chamada de dentro para fora. Uma é “o que a aplicação faz”; a outra é “o que a aplicação precisa”.
2. Por que um repositório é uma porta e não um detalhe de infraestrutura?
Resposta
Porque a interface do repositório pertence ao núcleo e é expressa na linguagem do domínio (porNumero, salvar(Conta)). A tecnologia de persistência (JPA, Mongo) é apenas o adapter que a implementa. Assim, o domínio depende da abstração, não do banco — exatamente o DIP.
3. Como uma porta pode “vazar” tecnologia e por que isso é grave?
Resposta
Quando seus métodos usam tipos de framework (ResponseEntity, @Entity, Page) ou conceitos externos (SQL, tópicos Kafka). É grave porque reintroduz a dependência do domínio na tecnologia — o exato problema que a arquitetura veio resolver — impedindo troca de adapter e teste isolado.
4. Em que sentido as portas são a materialização do DIP?
Resposta
Elas são as abstrações das quais tanto o módulo de alto nível (application service) quanto o de baixo nível (JPA, HTTP) dependem. Sem a porta, o alto nível dependeria diretamente do baixo nível; com ela, a dependência da implementação aponta para dentro, para o contrato do domínio.