A Regra da Dependência
O princípio
Seção intitulada “O princípio”Uncle Bob resume assim: source code dependencies must point only inward, toward higher-level policies. Quanto mais interno o círculo, mais alto o nível da política e mais estável ela deve ser. As Entities são o núcleo estável; Frameworks & Drivers são os detalhes voláteis.
A regra tem uma consequência absoluta: nada declarado em um anel externo pode ser mencionado por código de um anel interno — nem um nome de classe, nem uma função, nem uma variável, nem um formato de dado. Se o núcleo importa org.springframework.* ou jakarta.persistence.*, a regra já foi violada.
Por que “para dentro”?
Seção intitulada “Por que “para dentro”?”Porque as coisas que mudam por motivos diferentes e em ritmos diferentes devem ser separadas, e as políticas de negócio (que mudam devagar, por razões de negócio) não devem ser reféns de detalhes técnicos (que mudam rápido, por razões técnicas). Ao apontar as dependências para dentro, o banco de dados, o framework web e a UI tornam-se plugins substituíveis do núcleo.
Como se consegue isso: inversão
Seção intitulada “Como se consegue isso: inversão”O desafio prático: em runtime, o fluxo de controle frequentemente vai de fora para dentro (o controller chama o use case) e também de dentro para fora (o use case precisa persistir, chamar um gateway). O segundo caso pareceria forçar uma dependência de dentro para fora.
A solução é o DIP: o círculo interno define a interface que precisa, e o círculo externo a implementa. Assim a dependência de código-fonte se inverte em relação ao fluxo de controle.
classDiagram
class GatewayPort {
<<interface>>
+salvar(pedido) void
}
class Interactor {
-gateway: GatewayPort
}
class JpaGatewayAdapter {
+salvar(pedido) void
}
Interactor ..> GatewayPort : usa (interno)
JpaGatewayAdapter ..|> GatewayPort : implementa (externo)
O Interactor (interno) depende só da abstração GatewayPort, também interna. O JpaGatewayAdapter (externo) depende do núcleo, apontando para dentro. O fluxo de controle vai do interactor para a JPA, mas a dependência de código aponta ao contrário.
Travessia de fronteira (boundary crossing)
Seção intitulada “Travessia de fronteira (boundary crossing)”Cada vez que o controle cruza um dos círculos, ele passa por uma interface (port). Duas direções:
- Input port (fronteira de entrada): a interface do caso de uso que o mundo externo invoca. O controller depende dela; o interactor a implementa.
- Output port (fronteira de saída): a interface que o caso de uso invoca para entregar resultado ou pedir um serviço externo. O interactor depende dela; um presenter ou gateway a implementa.
Ver Use Cases para os detalhes dos interactors e presenters, e Ports para a origem hexagonal do conceito.
DTOs cruzando fronteiras
Seção intitulada “DTOs cruzando fronteiras”Objetos que atravessam a fronteira devem ser estruturas de dados simples e isoladas — request models e response models — nunca as Entities de domínio nem objetos do framework.
Regras práticas:
- O que entra no use case é um request model (DTO de entrada), não o
@RequestBodydo controller diretamente. - O que sai é um response model (DTO de saída), não a Entity.
- O DTO deve estar na forma mais conveniente ao círculo interno, jamais numa forma imposta pelo círculo externo (ex.: uma linha de
ResultSetou um objeto JPA não podem vazar para dentro). - Se você passa a Entity para fora, o mundo externo passa a depender do núcleo de forma acoplada — e mudanças no domínio quebram a UI. O DTO desacopla.
public record CriarPedidoRequest(String clienteId, List<ItemRequest> itens) {}
public record ItemRequest(String produtoId, int quantidade, BigDecimal precoUnitario) {}
public record PedidoResponse(String pedidoId, String status, BigDecimal total) {}Esses records vivem no anel de Use Cases e são o contrato pelo qual controllers e presenters conversam com o núcleo sem tocar nas Entities.
Anti-padrões e erros comuns
Seção intitulada “Anti-padrões e erros comuns”- Entity vazando para o controller: expor a Entity de domínio como resposta HTTP acopla a API ao domínio e viola a fronteira.
- Import proibido no núcleo: qualquer
import org.springframeworkoujakarta.persistencedentro de Entities/Use Cases quebra a regra. - Interface no lugar errado: declarar o
GatewayPortno pacote de infraestrutura em vez do núcleo. A interface pertence a quem a usa (o interactor), não a quem a implementa. - DTO anêmico que é a própria Entity: reaproveitar a Entity como DTO por preguiça, criando acoplamento reverso.
- Dependência transitiva escondida: o núcleo não importa Spring diretamente, mas depende de uma lib que arrasta o framework junto.
Relações
Seção intitulada “Relações”- Mecanismo de inversão: DIP e OCP (o núcleo fica fechado a mudança, aberto a extensão via plugins). Contexto: SOLID.
- Estrutura em anéis: Camadas da Clean Architecture e o índice Clean Architecture.
- Fronteiras e casos de uso: Use Cases, Ports, Adapters, Driving e Driven.
- Origem do conceito de fronteira: Arquitetura Hexagonal.
- Aplicação prática: Exemplo Clean com Spring.
- Domínio no centro: Entidades, Value Objects, Repositórios, Domain-Driven Design.
- Comparação: Comparação - Hexagonal vs Clean vs Camadas.
Perguntas de revisão
Seção intitulada “Perguntas de revisão”1. Um Interactor pode importar uma classe do pacote de controllers?
Resposta
Não. O interactor está num círculo mais interno que os controllers (Interface Adapters). A dependência apontaria para fora, violando a regra. A comunicação ocorre via input/output ports definidos no núcleo.
2. Como a dependência de código pode apontar para dentro se o use case precisa chamar o banco?
Resposta
Via DIP: o use case depende de uma interface (output port/gateway) definida no próprio núcleo; a implementação JPA, na camada externa, implementa essa interface. O fluxo de controle vai para fora, mas a dependência de código-fonte aponta para dentro.
3. Por que não passar a Entity diretamente pela fronteira?
Resposta
Porque isso acopla o círculo externo ao formato interno. Qualquer mudança na Entity quebraria a UI e a API. Usa-se DTOs (request/response models) simples, na forma mais conveniente ao círculo interno, para desacoplar.
4. Onde deve ser declarada a interface do gateway?
Resposta
No círculo que a consome (o de Use Cases / núcleo), não no de infraestrutura. Quem usa a abstração é o dono dela; quem a implementa apenas a satisfaz — essa é a essência da inversão.