Desmistificando a injeção de dependência – o que é e por que é importante

Desmistificando a injeção de dependência – o que é e por que é importante


Desmistificando a injeção de dependência – o que é e por que é importante

19.01.2026 |

5 Leitura de minuto

M
A maioria dos projetos usa injeção de dependência de alguma forma. No entanto, muitos desenvolvedores consideram isso um dado adquirido e realmente não questionam o que é ou por que o estão usando. Este artigo pretende responder a essas perguntas, embora raramente sejam feitas.

Este artigo faz parte de uma série que se aprofunda na Injeção de Dependência (DI). Nesta série, aprendemos o que realmente é DI e por que gostaríamos de usá-lo. Depois de começar a se aprofundar nisso, você encontrará alguns termos relacionados, que serão explicados. Existem também diversas maneiras de injetar dependências, com ainda mais nomes e variações menores. Abordaremos isso e também exploraremos as diferentes maneiras pelas quais as dependências podem ser conectadas e quais estruturas podem nos ajudar a fazer isso.

Problema

Vamos primeiro colocar todos na mesma página, explicando os conceitos básicos: O que é uma dependência?

Problema

Podemos explicar isso facilmente usando a imagem acima. O que vemos é uma classe A que usa métodos de uma classe chamada B. Isso significa B é uma dependência de A.
Simples assim. Mas estamos também a introduzir aqui um problema, porque neste caso A cria uma instância de B.

Se expressássemos isso como código, ficaria assim:

class UserService {
    UserRepository userRepo = new UserRepository(...);

    List<User> getActiveUsers() {
        return userRepo.findAll().stream()
        	.filter(User::isActive)
            .toList();
    }
}

Aqui vemos algum código Java. A nomenclatura se parece um pouco com Spring, então você já deve ter uma ideia aproximada das responsabilidades das classes.
O UserService (anteriormente classe A) cria um UserRepository (anteriormente classe B). O repositório pode acessar um banco de dados ou algo semelhante.

Isto acaba por ser problemático, porque viola o Princípio da Responsabilidade Única. O UserRepository não é apenas uma dependência, o UserService também o cria.
Ao criá-lo, o serviço passa a ser responsável pelo seu ciclo de vida. E se o repositório abrir uma conexão com um banco de dados ou algo semelhante, o serviço também é responsável por garantir que a conexão seja fechada em algum momento. Se você acha que esta é uma responsabilidade estranha para um serviço que deveria lidar com usuários, você está absolutamente certo.

Mas este não é o único problema. Também introduzimos um acoplamento forte entre essas duas classes.
Não é possível testar o getActiveUsers método independentemente do UserRepositorymesmo que o contrato do repositório findAll método é claro. Ele retorna uma lista, mas não estou conseguindo testar getActiveUsers sem ele.

Devido a esse acoplamento, uma mudança na UserRepository também pode causar testes para UserService falhar. Por exemplo, uma alteração na ordem da lista retornada, mesmo que a ordem não seja relevante para o teste. Em geral, não queremos que os testes falhem quando os detalhes que não estamos testando mudam.

Solução

Introduzimos vários problemas ao conectar nossas dependências dessa maneira, mas existe uma solução que resolve todos eles. E como você deve ter adivinhado pelo título, essa solução é Injeção de Dependência.

Em vez de criar a dependência onde ela é necessária, nós a injetamos de fora. Abaixo está um exemplo de injeção de uma dependência por meio do construtor:

class UserService {
    UserRepository userRepo;

	UserSerivce (UserRepository userRepo) {
    	this.userRepo = userRepo;
    }

    List<User> getActiveUsers() {
        return userRepo.findAll().stream()
        	.filter(User::isActive)
            .toList();
    }
}

O ciclo de vida do UserRepository agora está fora do UserService. Onde ele é criado e quem o gerencia será discutido em outro artigo desta série (sobre fiação).

Esta abordagem também resolve o problema do acoplamento forte. Testes independentes agora são possíveis. É fácil criar uma simulação simples de UserRepository que retorna uma lista estática de usuários e a passa para o serviço. Podemos simplesmente usar o construtor para injetar a simulação em vez da implementação real.

Como alteramos o código, o diagrama também muda.

Solução

Aula A ainda usa métodos de classe Bmas esta é agora a única seta nessa direção. Há outro componente, Xque lida com a criação e injeção de dependências. Para usar a terminologia adequada daqui para frente:
X é o injetor, A é o cliente e B é a dependência.

Conclusão

Como vimos, a injeção de dependência impõe a separação de interesses. A criação e o uso de uma dependência agora são separados pela injeção de dependências externas.
Isso nos permite testar unidades isoladamente.

Existem várias maneiras de injetar dependências, que serão abordadas em outra parte desta série.
A DI também pode ser implementada de diferentes maneiras, manualmente ou com um framework. Frameworks podem injetar dependências em tempo de compilação ou em tempo de execução. Esses tópicos também serão explorados em artigos futuros.

« Documentação como código com AsciiDoctor, GitLab CI e GitLab Pages



Source link

Postagens Similares

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *