Você está confundindo Injeção de Dependência com Inversão de Dependência? Vou te ajudar a entender!

Em nosso artigo anterior, refatoramos um sistema de registro de usuários utilizando o Princípio da Responsabilidade Única (SRP) . Também utilizamos Injeção de Dependência para passar nosso repositório e validador para o serviço.
Motivos pelos quais seu código Python é difícil de testar com o Princípio da Responsabilidade Única e como corrigir isso!
O Princípio da Responsabilidade Única (SRP, na sigla em inglês) é a base dos princípios SOLID. Ele afirma que uma classe deve ter…
No entanto, se você observar atentamente nosso código anterior, ainda existe um acoplamento oculto. Ele UserServicedepende diretamente da classe concreta UserRepository.
Isso nos leva ao Princípio da Inversão de Dependência (DIP) — o “D” em SOLID.
A Grande Confusão: Injeção vs. Inversão
Antes de refatorarmos, precisamos esclarecer um ponto de confusão muito comum: qual a diferença entre Injeção de Dependência e Inversão de Dependência?
1. Injeção de Dependência (O “Como”)
Injeção de Dependência é uma técnica . Significa simplesmente: “Não crie suas dependências dentro da sua classe; solicite-as no construtor.”
- Sem injeção de dependência:
UserServicecriaUserRepositoryinternamente. - Com DI:
UserServicerecebeUserRepositorycomo parâmetro.
2. Princípio da Inversão de Dependência (A “Regra”)
A inversão de dependência é um princípio arquitetural . Ela afirma: “Módulos de alto nível não devem depender de módulos de baixo nível. Ambos devem depender de abstrações.”
Em nosso código anterior, usamos Injeção, mas violamos a Inversão.
- A violação:
UserService(Nível Alto) importa e utilizaUserRepository(Nível Baixo). - A solução:
UserServicedeve depender de umUserRepositoryInterface. O tambémSQLUserRepositorydeve depender dessa interface.
❌ O “Antes”: Ligado aos detalhes
Vamos analisar o __init__método do nosso exemplo anterior de SRP.
class UserService :
# PROBLEMA: Estamos usando type hinting na classe CONCRETA.
# Estamos dizendo: "Eu preciso especificamente desta classe UserRepository exata."
def __init__ ( self , validator: UserValidator , repository: UserRepository ):
self.validator = validator
self.repository = repositorySe quisermos trocar UserRepository(o que salva no SQL↳SQL64 conteúdosSQL Server – Como evitar SQL Injection?Data · mai 2019Azure SQL DB Managed InstanceData · abr 2019SQL Server – Como evitar SQL Injection? Pare de utilizar Query Dinâmica como EXEC(@Query)Data · abr 2019Ver tudo em Data →) por um FakeUserRepository(para teste) ou um MongoUserRepository, teoricamente violamos o contrato de tipo definido aqui. O UserServicesabe demais sobre os detalhes de implementação de UserRepository.
✅ A Refatoração: Introduzindo Abstrações
Para aderir ao DIP em Python↳Python56 conteúdosVSCode + Python + Alexa: Desenvolva e teste skills para alexa localmente com pythonDev (Back & Front) · out 2025Dominando decoradores em Python: um guia completo com exemplosDev (Back & Front) · jan 2025Desenvolvimento de software: diferenças entre Python, JavaScript e JavaGestão Dev & TI · nov 2024Ver tudo em Dev (Back & Front) →, usamos Classes Base Abstratas (ABCs) para definir “Interfaces”.
Etapa 1: Definir as interfaces (O contrato)
Criamos classes abstratas que definem o que precisa ser feito, sem definir como .
from abc import ABC, abstractmethod# O contrato para armazenamentoclass UserRepositoryInterface ( ABC ):@abstractmethoddef exists ( self, email: str ) -> bool :pass@abstractmethoddef save ( self, user: 'User' ):pass# O contrato para validaçãoclass UserValidatorInterface ( ABC ):@abstractmethoddef validate ( self, user: 'User' ):pass
Etapa 2: Implementar as classes concretas
Agora, nossas classes de baixo nível implementam essas interfaces.
# Módulo de baixo nível que implementa a interface
class SQLUserRepository ( UserRepositoryInterface ):
def exists ( self, email: str ) -> bool :
# Simula verificação SQL
return email == "taken@example.com"
def save ( self, user: 'User' ):
# Simula salvamento SQL
print ( f"[SQL] INSERT INTO users VALUES (' {user.name} ', ' {user.email} ')" )
# Outro módulo de baixo nível (por exemplo, para testes ou banco de dados diferente)
class InMemoryUserRepository ( UserRepositoryInterface ):
def __init__ ( self ):
self .db = []
def exists ( self, email: str ) -> bool :
return any (u.email == email for u in self .db)
def save ( self, user: 'User' ):
self .db.append(user)
print ( f"[MEMORY] Salvo {user.name} na lista." )
class StandardUserValidator ( UserValidatorInterface ):
def validate ( self, user: 'User' ):
if len (user.name) < 3 :
raise ValueError( "Nome muito curto" )
if "@" not in user.email:
raise ValueError( "Email inválido" )
if len (user.password) < 6 :
raise ValueError( "Senha muito fraca" )Etapa 3: Refatorar o serviço (o módulo de alto nível)
Eis a mágica. O UserServicejá não sabe que aquilo SQLUserRepositoryexiste. Só sabe sobre aquilo outro UserRepositoryInterface.
class UserService :
# DIP APLICADO: Dependemos da Interface (Abstração), não da classe concreta.
def __init__ ( self, validator: UserValidatorInterface, repository: UserRepositoryInterface ):
self.validator = validator
self.repository = repository
def register ( self, name, email, password ):
user = User(name, email, password)
self.validator.validate (user)
if self.repository.exists (email):
raise ValueError( "Email já está em uso" )
self.repository.save (user)
print ( ">">> Usuário cadastrado com sucesso." )Colocando tudo em prática
Agora podemos trocar implementações facilmente sem alterar uma única linha de código em UserService.
# Cenário A: Produção (Usando SQL)sql_repo = SQLUserRepository()std_validator = StandardUserValidator()# O serviço funciona com SQLservice_prod = UserService(std_validator, sql_repo)service_prod.register( "Alice" , "alice@sql.com" , "secure123" )print ( "-" * 30 )# Cenário B: Teste (Usando Memória)# Não alteramos o código do UserService, mas mudamos completamente o comportamento.memory_repo = InMemoryUserRepository()# O MESMO serviço funciona com memóriaservice_test = UserService(std_validator, memory_repo)service_test.register( "Bob" , "bob@memory.com" , "secure123" )
Resumo
Ao aplicar o Princípio da Inversão de Dependência, invertemos o fluxo de controle:
- Antes:
UserService-> dependia de ->SQLUserRepository - Depois:
UserService-> depende de ->Interface<- implementado por <-SQLUserRepository
Por que isso importa?
- Acoplamento fraco: a lógica de alto nível (fluxo de registro) é protegida dos detalhes de baixo nível (SQL, MongoDB, sistema de arquivos).
- Flexibilidade: Você pode alternar entre provedores de banco de dados simplesmente passando uma instância de classe diferente.
- Testes: Você pode facilmente passar um
MockRepositoryobjeto que implementa a interface, tornando os testes unitários rápidos e confiáveis.
Mais de Cláudio Raposo
- Abstração de Infraestrutura e Microsserviços Portáteis: Implementando Dapr no Azure Container Apps
- Segregação de Responsabilidades de Comando e Consulta (CQRS): Desacoplamento Extremo com Azure Cosmos DB e Azure AI Search
- Federação de Service Mesh Multicloud: Orquestrando Tráfego Zero-Trust entre Nuvens via Istio e SPIFFE/SPIRE







