S.O.L.I.D. DIP (Dependency Inversion Principle)

  • Rafael Miceli
  • 11 Abr 2016

Neste post vou explicar um pouco qual a ideia por trás de um princípio extremamente importante, o principio da Inversão de Dependência (Dependency Inversion Principle).

Para explicar melhor, nada como um bom exemplo.

Divisão em camadas

Imagine a seguinte situação.

Digamos que temos uma aplicação e que a mesma sabemos que vai se tornar um sistema grande. No geral usamos o conceito de divisão de 3 camadas para ficar mais simples organizarmos as responsabilidades de nossa aplicação.

A ideia é que nosso sistema é dividido em 3 camadas:

Lembrando que essa é uma representação moderada. Para arquiteturas de sistemas maiores existem outras abordagens para a divisão das camadas.

1 - Presentation (Apresentação)

2 - Domain ou Business Logic (Domínio)

3 - Data (Dados)

As responsabilidades de cada uma são respectivamente:

Presentation - Possui todo seu código de apresentação, para fácil entendimento, em um projeto usando .Net C# essa seria nossa aplicação MVC

Domain - É uma class library aonde fica a responsabilidade de toda lógica de regra de negócios do nosso sistema.

Data - Nesta camada, que também é uma class library, implementamos qualquer lógica para acesso a dados que persistimos.

Dependências entre camadas

É quase natural que se fossemos imaginar a distribuição de dependência entre as camadas teríamos esse desenho:

fluxo-data

O problema com está imagem é que neste cenário nossa camada de regra de negócios (Domain) depende da camada de dados. Mas o coração de todo software se encontra na sua regra de negócio, pois como o nome da camada ja diz, a mesma define o business do nosso sistema.

Este de longe é cenário mais comum aonde é aplicado o princípio da Inversão de Dependência

DIP (Dependency Inversion Principle)

Ao aplicarmos o DIP no cenário acima a imagem muda para a seguinte:

fluxo-data

Mas na prática como fazemos isso?

Eis um pedaço (vertical) de nossa aplicação usando DIP, lembrando que este código pode ser encontrado no meu Github:

MateriaController.cs

public class MateriaController : ApiController
{
    private IMateriaService _materiaService;

    public MateriaController()
    {
    }

    // POST api/values
    public HttpResponseMessage Post([FromBody]Materia materia)
    {
        try
        {
            _materiaService.CriarNovaMateria(materia);

            return Request.CreateResponse(HttpStatusCode.Created);
        }
        catch (Exception ex)
        {
            return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex.Message);
        }
        
    }
    
}

MateriaService.cs

public class MateriaService : IMateriaService
{
    private IMateriaRepo _materiaRepo;

    public MateriaService(IMateriaRepo materiaRepo)
    {
        _materiaRepo = materiaRepo;
    }

    public Materia BuscarMateriaPorNome(string nomeMateria)
    {
        return _materiaRepo.BuscarMateriaPorNome(nomeMateria);
    }

    public IEnumerable<Materia> BuscarMateriasSelecionadas(IEnumerable<int> materiasSelecionadasId)
    {
        return _materiaRepo.BuscarMateriasSelecionadas(materiasSelecionadasId);
    }

    public void CriarNovaMateria(Materia materia)
    {
        if (BuscarMateriaPorNome(materia.Nome) != null)
            throw new Exception("Matéria com esse nome já existe");

        _materiaRepo.CadastrarMateria(materia);
    }
}

MateriaRepository

public class MateriaRepository : IMateriaRepo
{
    public Materia BuscarMateriaPorNome(string nomeMateria)
    {
        //Buscar materia no banco de dados
        return null;
    }

    public IEnumerable<Materia> BuscarMateriasSelecionadas(IEnumerable<int> materiasSelecionadasId)
    {
        //Buscar materias no banco de dados
        return null;
    }

    public void CadastrarMateria(Materia materia)
    {
        //Cadastrar materia no banco de dados
    }
}

Percebam que, a nossa classe MateriaRepository da nossa camada de dados depende da implementação de nossa interface IMateriaRepo que se encontra em nossa regra de negócios, ou seja, nós adicionamos uma referência do nosso projeto Domain em nosso Data e não o oposto.

Outra coisa que muda é que nossa camada de regra de negócios não mais instância nossa camada de dados (como resolver as instâncias veremos em um próximo post)

Domain

Porque aplicamos o DIP no cenário anterior?

Porque queremos que o centro de nossa aplicação seja sempre nosso Domain, uma vez que ele possui o conjunto de regras necessárias para o funcionamento correto de nosso sistema. Podemos ver uma excelente referência sobre Domain no livro Domain Driven Design do Eric Evans.

Em um próximo post vamos ver como iniciar as instâncias de nossas classes após termos feito a inversão das dependências.

comentarios com Disqus Disqus