DIP - Dependency Inversion Principle
Definição formal
Seção intitulada “Definição formal”Robert C. Martin enuncia o DIP em duas cláusulas:
- “High-level modules should not depend on low-level modules. Both should depend on abstractions.”
- “Abstractions should not depend on details. Details should depend on abstractions.”
O termo “inversão” refere-se à inversão do sentido convencional da dependência. No fluxo ingênuo, a regra de negócio (alto nível) chama diretamente o código de infraestrutura (baixo nível: banco, HTTP, e-mail) — logo, o de alto nível depende do de baixo nível, e a política fica refém dos detalhes. O DIP inverte isso: a abstração é definida pelo/junto ao módulo de alto nível (é ele quem declara o contrato de que precisa), e o módulo de baixo nível a implementa. A seta de dependência de código passa a apontar do detalhe para a política.
O problema que resolve
Seção intitulada “O problema que resolve”Sem DIP, a lógica de negócio importa diretamente classes de infraestrutura. Consequências:
- A política de negócio não pode ser testada sem o banco/serviço real (sem mocks fáceis).
- Trocar tecnologia (JDBC → JPA, e-mail → SMS) obriga a mexer na regra de negócio.
- O núcleo do sistema fica acoplado a frameworks voláteis.
O DIP quebra essa dependência introduzindo uma interface que pertence à camada de negócio. A infraestrutura passa a implementar essa interface. A regra de negócio nunca conhece a implementação concreta.
Detalhe crucial: a quem pertence a abstração
Seção intitulada “Detalhe crucial: a quem pertence a abstração”A inversão é literal e verificável: trata-se do sentido da dependência de código-fonte — na prática, de quem precisa dar import em quem. Sem DIP, o domínio importa a classe de infraestrutura e a seta aponta para fora. Com DIP, a interface mora no pacote do domínio e é a infraestrutura que a importa para implementá-la; a seta passa a apontar para dentro. O teste é mecânico: apague o módulo de infraestrutura e veja se o domínio ainda compila. Note que o fluxo de execução continua indo do domínio para o banco — o que inverte é a dependência de compilação, e é daí que vem o nome do princípio.
Satisfeita a direção da seta, resta uma segunda armadilha, mais sutil: o vocabulário da interface. Se você extrai a interface a partir da classe de banco (mesmos métodos, mesma linguagem de persistência), o compilador atesta a inversão, mas a política continua refém do detalhe — trocar Postgres por uma API externa muda a interface, e a mudança sangra para dentro do domínio. A abstração deve expressar o que a política precisa, não o que a infraestrutura oferece: aguardandoPagamento() é um conceito de negócio; selectByStatus("PENDING") é persistência disfarçada.
As duas coisas são independentes, e vale não confundi-las: a direção da seta é o DIP. O vocabulário é o que separa uma inversão real de uma que apenas cumpre a letra do princípio — uma interface com nomes de negócio morando no pacote de infraestrutura não é DIP nenhum, é só uma interface bem nomeada.
A mecânica de colocar a interface num pacote separado do implementador tem nome próprio no catálogo de Fowler: Separated Interface.
Na Arquitetura Hexagonal, essas interfaces de propriedade do domínio são os Ports; as implementações concretas de infraestrutura são os Adapters. É exatamente o DIP operando em escala arquitetural, e é o que sustenta A Regra da Dependência da Clean Architecture: o código-fonte só pode apontar para dentro, em direção às políticas de mais alto nível.
Como identificar violações
Seção intitulada “Como identificar violações”Trade-offs e quando não aplicar dogmaticamente
Seção intitulada “Trade-offs e quando não aplicar dogmaticamente”Toda abstração tem custo: mais tipos, mais indireção, navegação de código mais difícil. Aplicar DIP a tudo produz “interface para cada classe” sem ganho real.
Exemplo em Java + Spring Boot 3
Seção intitulada “Exemplo em Java + Spring Boot 3”Antes — violando DIP
Seção intitulada “Antes — violando DIP”A regra de negócio depende diretamente da implementação concreta de infraestrutura.
@Servicepublic class CadastroUsuario {
private final MySqlUsuarioDao dao = new MySqlUsuarioDao(); private final SmtpEmailSender emailSender = new SmtpEmailSender();
public void cadastrar(String nome, String email) { dao.inserir(nome, email); emailSender.enviar(email, "Bem-vindo, " + nome); }}
public class MySqlUsuarioDao { public void inserir(String nome, String email) { System.out.println("INSERT no MySQL: " + nome); }}
public class SmtpEmailSender { public void enviar(String destino, String corpo) { System.out.println("SMTP para " + destino); }}CadastroUsuario (alto nível) instancia e depende de detalhes concretos. Trocar MySQL ou testar sem banco é inviável.
Depois — dependência invertida sobre abstrações do domínio
Seção intitulada “Depois — dependência invertida sobre abstrações do domínio”As interfaces pertencem ao domínio e falam a linguagem do negócio. A infraestrutura implementa e é injetada pelo Spring.
public record Usuario(String nome, String email) {}
public interface UsuarioRepository { void registrar(Usuario usuario);}
public interface NotificadorBoasVindas { void enviarBoasVindas(Usuario usuario);}@Servicepublic class CadastroUsuario {
private final UsuarioRepository repository; private final NotificadorBoasVindas notificador;
public CadastroUsuario(UsuarioRepository repository, NotificadorBoasVindas notificador) { this.repository = repository; this.notificador = notificador; }
public void cadastrar(String nome, String email) { Usuario usuario = new Usuario(nome, email); repository.registrar(usuario); notificador.enviarBoasVindas(usuario); }}@Repositorypublic class JdbcUsuarioRepository implements UsuarioRepository {
private final JdbcTemplate jdbcTemplate;
public JdbcUsuarioRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; }
@Override public void registrar(Usuario usuario) { jdbcTemplate.update("INSERT INTO usuario (nome, email) VALUES (?, ?)", usuario.nome(), usuario.email()); }}@Componentpublic class EmailNotificadorBoasVindas implements NotificadorBoasVindas {
private final JavaMailSender mailSender;
public EmailNotificadorBoasVindas(JavaMailSender mailSender) { this.mailSender = mailSender; }
@Override public void enviarBoasVindas(Usuario usuario) { SimpleMailMessage msg = new SimpleMailMessage(); msg.setTo(usuario.email()); msg.setSubject("Bem-vindo"); msg.setText("Bem-vindo, " + usuario.nome()); mailSender.send(msg); }}CadastroUsuario não conhece MySQL nem SMTP. Depende só de UsuarioRepository e NotificadorBoasVindas, interfaces do domínio. Em teste, injeta-se um dublê; em produção, o Spring injeta os adaptadores concretos.
Diagrama
Seção intitulada “Diagrama”classDiagram
class CadastroUsuario {
+cadastrar(String, String)
}
class UsuarioRepository {
<<interface>>
+registrar(Usuario)
}
class NotificadorBoasVindas {
<<interface>>
+enviarBoasVindas(Usuario)
}
class JdbcUsuarioRepository {
+registrar(Usuario)
}
class EmailNotificadorBoasVindas {
+enviarBoasVindas(Usuario)
}
CadastroUsuario --> UsuarioRepository
CadastroUsuario --> NotificadorBoasVindas
UsuarioRepository <|.. JdbcUsuarioRepository
NotificadorBoasVindas <|.. EmailNotificadorBoasVindas
As setas de implementação (<|..) apontam do detalhe para a abstração: é a inversão. A infraestrutura depende do contrato do domínio, nunca o contrário.
DIP e arquitetura
Seção intitulada “DIP e arquitetura”Anti-padrões e erros comuns
Seção intitulada “Anti-padrões e erros comuns”Relações
Seção intitulada “Relações”- Faz parte de: SOLID
- Habilita: OCP - Open-Closed Principle (depender de abstrações permite estender por novas implementações).
- Apoia-se em: ISP - Interface Segregation Principle (abstrações enxutas) e LSP - Liskov Substitution Principle (implementações substituíveis).
- Realizado no nível de pacotes por: Separated Interface (onde a interface mora é o que produz a inversão).
- Base teórica de: Arquitetura Hexagonal, Clean Architecture, A Regra da Dependência, Ports, Adapters, Domain-Driven Design.
- Índice: Home
Perguntas de revisão
Seção intitulada “Perguntas de revisão”- Qual é a “inversão” no Princípio da Inversão de Dependência?
Resposta
A inversão do sentido convencional da dependência: em vez de o alto nível depender do baixo nível, ambos dependem de uma abstração definida junto ao alto nível. A seta de dependência de código passa a apontar do detalhe (infra) para a política (domínio).
- Qual a diferença entre DIP e injeção de dependência (o que o Spring faz)?
Resposta
DIP é o princípio de depender de abstrações. Injeção de dependência é um mecanismo (IoC) que fornece implementações de fora. É possível usar injeção e mesmo assim violar o DIP, ao injetar uma classe concreta em vez de uma abstração de propriedade do domínio.
- Por que “extrair a interface a partir da classe de banco” não satisfaz o DIP?
Resposta
Porque a abstração continua falando o vocabulário da tecnologia e reflete o que a infraestrutura oferece, não o que a política precisa. Não houve inversão — apenas uma camada de indireção. A interface deve ser de propriedade do domínio e expressar suas necessidades.
- Como o DIP se relaciona com a Arquitetura Hexagonal e a Clean Architecture?