Inversão de Controle (IoC) ajuda a evitar que seu código vire uma bagunça cheia de amarrações. Com IoC, você divide responsabilidades, evita deixar tudo preso e facilita testar sua aplicação.

O que é IoC?

IoC significa "Inversão de Controle". Em vez de você fazer tudo, como criar dependências, você delega isso para algo externo. Isso pode ser feito através de injeção de dependências, programação orientada a aspectos, ou usando um framework de IoC.

Lembre-se: Injeção de Dependência (DI) é só uma das maneiras de aplicar IoC. Outro jeito é a Programação Orientada a Aspectos (AOP), que adiciona comportamentos na hora de compilar. Com IoC, você separa o que fazer de como fazer, deixando seu código mais limpo e adaptável.

Por que utilizar?

Dentro das boas práticas do SOLID, o Princípio da Inversão de Dependência (DIP) é vital para reduzir dependências e anda de mãos dadas com IoC.

Ao usar IoC, seu projeto ganha:

  • Menos dependências amarradas
  • Mais fácil de testar
  • Mais fácil de manter

Hoje em dia, qualquer framework bom já suporta IoC. Se o seu não tem, é hora de reconsiderar.

Um exemplo prático

Imagine que você precisa salvar um pedido.

dependencia

O domínio só precisa saber que o pedido será salvo, não onde. Pode ser num banco, num arquivo, etc. Vamos aplicar IoC:

dependencia-invertendo

O domínio conversa com uma interface (IPedidoRepository), sem se preocupar com o que tá por trás.

Os benefícios dessa abordagem

  • O domínio não sabe se o repositório é um banco SQL ou NoSQL.
  • Mudou a implementação? Tudo bem, a interface é a mesma e o sistema continua funcionando.
  • Dá para testar o domínio, já que você pode simular essa dependência.

Talk is cheap, show me the code

Um exemplo de código do jeito errado para o certo.

1. Código acoplado

public class PedidosService
{
    public void AnalisarPedido(Pedido pedido)
    {
        if (pedido.Total > 10m)
        {
            pedido.Suspeito();
            this.EnviarEmailAlerta(pedido);
        }

        new PedidoRepositorySql().SalvarPedido(pedido);
    }

    private void EnviarEmailAlerta(Pedido pedido)
    {
        // Lógica de envio de e-mail
    }
}

2. Refatorando com IoC

public class PedidosService
{
    private readonly IPedidoRepository _pedidoRepository;
    private readonly IEmailService _emailService;

    public PedidosService(IPedidoRepository pedidoRepository, IEmailService emailService)
    {
        _pedidoRepository = pedidoRepository;
        _emailService = emailService;
    }

    public void AnalisarPedido(Pedido pedido)
    {
        if (pedido.Total > 10m)
        {
            pedido.Suspeito();
            _emailService.EnviarEmailAlerta(pedido);
        }

        _pedidoRepository.SalvarPedido(pedido);
    }
}

Interfaces

public interface IPedidoRepository
{
    void SalvarPedido(Pedido pedido);
}

public interface IEmailService
{
    void EnviarEmailAlerta(Pedido pedido);
}

public class PedidoRepositorySql : IPedidoRepository
{
    public void SalvarPedido(Pedido pedido) { }
}

public class EmailGoogleService : IEmailService
{
    public void EnviarEmailAlerta(Pedido pedido) { }
}

Com essa abordagem, o PedidosService fica flexível e limpo.

Você poderia implementar salvando Pedidos num MongoDB:

var pedidoService = new PedidosService(
    new PedidoRepositoryMongoDb(),
    new MailChimpService()
);

E facilmente mudar para outro banco:

var pedidoService = new PedidosService(
    new PedidoRepositoryPostgres(),
    new AwsEmailService()
);

Não precisou mudar nada no domínio.

Injeção de dependência

Na prática, usamos um container de IoC para configurar:

services.AddScoped<IPedidoRepository, PedidoRepositorySql>();
services.AddScoped<IEmailService, EmailGoogleService>();

Assim, o framework injeta o que você precisa e você só pede.

[Route("pedidos")]
public class PedidosController : Controller
{
    private readonly IPedidoService _pedidoService;

    public PedidosController(IPedidoService pedidoService) 
    {
        _pedidoService = pedidoService;
    }
	
    public IActionResult Post(Pedido pedido)
    {
        _pedidoService.AnalisarPedido(pedido);
    }
}

Conclusão

Inversão de Controle não é só uma técnica de desenvolvimento. É a diferença entre um código fácil de manter e um problemático. Se ainda escreve new em tudo, pense nas suas práticas. Controlar é bom, mas delegar é essencial. Seus testes e colegas de equipe vão agradecer.