Dependency Injection com StructureMap e Asp.Net MVC

Ok, então você começou a usar o Dependency Injection e viu que dava muito trabalho. A boa notícia é que existem vários frameworks por aí que ajudam a diminuir o trabalho e ainda a ter um código muito mais limpo.

Particularmente, eu gosto muito do StructureMap, principalmente porque ele nos permite utilizar o “Convention over Configuration”. Vamos ver jajá como isso funciona.

Bom, dando sequência no post anterior, vamos modificar aquele código para utilizar o StructureMap. A primeira coisa a fazer obviamente é baixar o framework e adicionar uma referência para o assembly StructureMap.dll.

O próximo passo é configurar o StructureMap, para isso vamos criar uma classe:

public class StructureMapConfiguration
{
    public static void Configure()
    {
        ObjectFactory.Initialize(InitializeStructureMap);
    }

    private static void InitializeStructureMap(IInitializationExpression x)
    {
        x.Scan(y =>
        {
            y.Assembly("MvcApplication1");
            y.With();
        });
    }
}

O que fazemos nesse código e dizer para o ScrutureMap buscar no Assembly MvcApplication1 usando a convenção padrão. E o que é a convenção padrão? Geralmente quando criamos uma interface, como no nosso caso criamos a interface IClienteService criamos uma classe com a implementação padrão da interface chamada ClienteService. Essa é a convenção padrão, dessa forma, toda vez que precisarmos de um objeto que implemente a interface IClienteService o StructureMap buscará nos assemblies informados para ele, a classe de mesmo nome só que sem o I. Prático não? Nada de configurar XMLs e afins (embora o StructureMap permita isso).

Outra coisa que precisamos fazer é criar uma classe que será responsável pela criação dos nosso controllers. Isso é necessário porque por padrão o Asp.Net MVC cria os controllers apenas com o construtor padrão, sem parâmetros. Nossa classe fica assim:

public class StructureMapControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(Type controllerType)
    {
        if (controllerType == null) return null;
        try
        {
            return ObjectFactory.GetInstance(controllerType) as Controller;

        }
        catch (StructureMapException)
        {

            System.Diagnostics.Debug.WriteLine(ObjectFactory.WhatDoIHave());
            throw;
        }
    }
}

Pronto, agora vamos alterar a inicialização da aplicação no Global.asax, que deve ficar assim:

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);
    StructureMapConfiguration.Configure();
    ControllerBuilder.Current.SetControllerFactory(new
        StructureMapControllerFactory());
}

Uma última coisa, no nosso controller ClienteController tínhamos dois construtores, agora vamos deixar apenas um com o parâmetro necessário para funcionar:

public IClienteService _service;
public ClienteController(IClienteService service)
{
    _service = service;
}

Pronto, isso é tudo o que é necessário. Mas se você quiser utilizar um XML para configurar suas classes, é possível. Dentro da classe de configuração, logo abaixo do método Scan, adicione:

x.AddConfigurationFromXmlFile("structuremap.config.xml");

Isso fará que com que o StructureMap procure na raiz do site por um arquivo chamado structuremap.config.xml. Segue um exemplo de como deve ser esse arquivo:


  
    PluginType="MinhaLibrary.Business.IClasseNegocio, MinhaLibrary.Business"
    PluggedType="MinhaLibrary.Business.ImplementacaoClasseNegocio, MinhaLibrary.Business"
    Scope="Singleton" />
  

É isso! E viva o Convention over Configuration!!!!

Asp.Net MVC e o padrão Dependency Injection

O padrão Dependency Injection (DI) é  utilizado para diminuir o acoplamento entre as partes de um sistema.

Vou demonstrar em duas partes como fazer Dependency Injection, na primeira parte vou fazer a injeção de forma manual e depois vou utilizar o framework Structuremap para isso.

Existem algumas razões para se fazer isso, mas na minha opinião as principais são: facilitar a criação de testes unitários e a possibilidade de se estender uma aplicação sem comprometer as partes que dependem do funcionamento daquela aplicação.

Imagine o seguinte cenário:

Cenário de Dependência

No caso acima, temos uma classe ClienteController (já pensando no MVC), que usa/depende uma classe ClienteService, que é a classe responsável por todas as regras de negócio relativas a Cliente mas que usa/depende da classe ClienteRepository, que é a classe responsável por todas as atividades no banco de dados relacionadas a Cliente.

Uma implementação das classes ClienteController e ClienteService poderia ser feita assim:

public class ClienteController : Controller
{
    public ActionResult Novo(Cliente cliente)
    {
        var service = new ClienteService();
        service.Salvar(cliente);
        return View();
    }
}

public class ClienteService
{
    public void Salvar(Cliente c)
    {
        if(ValidarCliente(c))
        {
            var rep = new ClienteRepository();
            rep.Salvar(c);
        }
    }

    private bool ValidarCliente(Cliente cliente)
    {
        return true;
    }
}

Veja na linha 5 que a classe ClienteController faz uma referência direta à classe ClienteService e na linha 17 a classe ClienteService faz uma referência direta à classe ClienteRepository. Isso não seria um problema se a) você não estiver considerando fazer testes unitários e b) se o seu sistema for um sistema pequeno com poucas classes e que não vá lhe gerar muitos problemas de manutenção.

No caso do teste unitário isso é um problema porque assim que for criado um teste para o método Salvar na classe ClienteController esse teste irá testar toda a sequência de atividades, inclusive as classes de Service e Repository.

Outro problema é o seguinte, imagine que esse é um sistema que você distribui para os seus clientes e que você tem uma outra classe ContasPagarService, a regra de negócio de contas a pagar varia de acordo com cada cliente que você tem. Se o código estivesse com o nível de dependência acima, talvez a única forma (ou a mais comum) de se resolver essa diferença seria implementando uma série de ifs dentro do código da classe de Service.

Como resolver esse problema então? 1o temos que começar quebrando as dependências criando interfaces.  Assim temos:

// IClienteService.cs
public interface IClienteService
{
    void Salvar(Cliente c);
}

Depois disso, precisamos alterar as nossas classes para implementar as interfaces:

public class ClienteRepository : IClienteRepository
public class ClienteService : IClienteService

Agora já estamos prontos pra fazer um pouco de Dependency Injection.

Vamos começar reconhecendo as dependências em nossas classes, sabemos que a classe Controller depende da classe Service que por sua vez depende da classe Repository. Vamos trocar essas dependências para as interfaces que criamos e teremos a classe ClienteController assim:

public class ClienteController : Controller
{
    private IClienteService _service;
    public ClienteController(IClienteService service)
    {
        _service = service;
    }

    public ActionResult Novo(Cliente cliente)
    {
        _service.Salvar(cliente);
        return View();
    }
}

Veja que agora a classe ClienteController está esperando qualquer objeto que implemente a interface IClienteService e não temos mais uma dependência direta para a classe ClienteService.

Agora veja como ficou a classe ClienteService:

public class ClienteService : IClienteService
{
    private IClienteRepository _repository;
    public  ClienteService(IClienteRepository repository)
    {
        _repository = repository;
    }

    public void Salvar(Cliente c)
    {
        if(ValidarCliente(c))
        {
            _repository.Salvar(c);
        }
    }

    private bool ValidarCliente(Cliente cliente)
    {
        return true;
    }
}

A classe ClienteService ficou muito parecida com a classe ClienteController. Agora ela espera um objeto que implemente a interface IClienteRepository no construtor e depois usa esse objeto nas chamadas do repository.

Agora só falta ligar os pontos. Como dizer para as classes que ClienteController e ClienteService qual objeto elas realmente vão usar.

Para isso vamos utilizar um outro padrão chamado Abstract Factory para centralizar todas as nossas dependências em uma única classe. A nossa classe de abstract factory ficou assim:

// MeuProjetoFactory.cs
public class MeuProjetoFactory
{
    public static IClienteService CriaClienteService()
    {
        return  new ClienteService(CriaClienteRepository());
    }
    public  static IClienteRepository CriaClienteRepository()
    {
        return  new ClienteRepository();
    }
}

Como vemos, nessa classe sabemos como criar um objeto de cada interface que precisamos. Qual a vantagem disso? Todas as dependências estão centralizadas em único local, muito mais fácil para dar manutenção, não é?

Agora na nossa classe de Controller, precisamos fazer mais uma modificação:

public class ClienteController : Controller
{
    private IClienteService _service;

    public ClienteController(): this(MeuProjetoFactory.CriaClienteService())
    {

    }

    public ClienteController(IClienteService service)
    {
        _service = service;
    }

    public ActionResult Novo(Cliente cliente)
    {
        _service.Salvar(cliente);
        return View();
    }
}

Pronto, agora toda vez que uma classe ClienteController for criada, se no momento da criação não for informado um IClienteService e for utilizado o construtor padrão, automaticamente iremos buscar da nossa classe de factory.

Isso deve ser feito dessa forma porque o Asp.Net MVC por padrão não consegue criar controllers sem ser pelo construtor padrão.

Pronto, agora já temos um sistema com as nossas camadas bem separadas e utilizando o padrão Dependency Injection.

Agora se quisermos testar a nossa classe ClienteController é só passar uma classe de mock que implemente a interface IClienteService para o construtor.

E no caso que falei sobre as regras de negócio diferentes no contas a pagar? Bom, uma das soluções para isso nesse caso seria ter várias classes que implementassem IContasPagarService por exemplo, ContasPagarCliente1Service, ContasPagarClienteXptoService e assim por diante. Então, dentro do abstract factory, poderíamos ter um método assim:

public static IContasPagarService CriaContasPagarService()
{
    switch (ConfigurationManager.AppSettings["Cliente"])
    {
       case "Cliente1":
            return new ContasPagarCliente1Service();
       case "ClienteXpto":
            return new ContasPagarClienteXptoService();
       default:
            return new ContasPagarService();
    }
}

De novo, estamos centralizando tudo na classe de abstract factory e utilizando o web.config para configurarmos qual classe de negócio utilizaremos. Um código bem mais simples e limpo. E ainda, a cada vez que tivermos que mudar uma regra de negócio de um cliente específico, não precisamos nos preocupar se afetamos ou não a regra de negócio de outro.

No próximo post eu vou falar sobre como fazer Dependency Injection de uma forma ainda mais fácil que essa. Espero que nesse ponto pelo menos a teoria esteja clara.