Padrões de DI: Composition Root

E ai, pessoal?

Estou de volta com a série “Padrões de DI”. Na primeira parte, vimos uma introdução sobre o conceito de Dependency Injection e seus três aspectos: composição, ciclo de vida e interception.

Nesta parte, veremos o conceito chamado de Composition Root e como aplicá-lo.

Então vamos nessa!

1. O QUE É

Composition Root é o local único na aplicação onde é feita a composição dos objetos, ou seja, onde todas as dependências são registradas. Em se tratando de implementação, ele pode ser uma única classe ou um conjunto de classes localizadas no mesmo módulo (assembly).

O Composition Root, como o próprio nome sugere, deve estar localizado no ponto de entrada da aplicação, de forma que o registro das dependências seja feito uma única vez durante a execução da mesma.

O ponto de entrada varia de acordo com o tipo de aplicação. Por exemplo, em uma aplicação do tipo console, a entrada é o método Main enquanto que em uma aplicação ASP.NET, a entrada é o event handler Application_Start no arquivo global.asax.

2. DI CONTAINERS

Normalmente utilizamos no Composition Root um DI Container, que nada mais é do que uma biblioteca com uma série de facilidades no que diz respeito a DI, dando suporte, na maioria das vezes, aos três aspectos de DI vistos no post anterior. Há uma infinidade de containers disponíveis e até benchmarks <como este> (para .Net) para auxiliar na escolha de um.

Não entrarei em detalhes sobre algum container em específico para não fugir do foco deste post, mas veremos o uso básico de um no exemplo a seguir.

3. IMPLEMENTANDO UM COMPOSITION ROOT NO ASP.NET MVC

Como dito anteriormente, o ponto de entrada de uma aplicação ASP.NET é o Application_Start. Tudo o que precisamos fazer é instanciar o container, registrar as dependências e permitir que o container resolva e libere as dependências após seu uso.

Vamos ao exemplo (utilizando o DI Container Ninject). Começamos pelo global.asax:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        var container = new StandardKernel(new NinjectWebAppModule());
        ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory(container));
    }
}

No start da aplicação, instanciamos o container (new StandardKernel()), passando o objeto que possui o registro das dependências (new NinjectWebAppModule()).

Em seguida, para resolvermos as dependências, precisamos utilizar o seam fornecido pelo ASP.NET MVC: a interface IControllerFactory. Para isso, implementamos essa interface e configuramos a implementação NinjectControllerFactory como a nova factory de controllers, usando o método SetControllerFactory().

Esta é a implementação do módulo NinjectWebAppModule:

public class NinjectWebAppModule : NinjectModule
{
    public override void Load()
    {
        Kernel.Bind<IConversor>().To<ConversorPdf>();
    }
}

No caso do Ninject, tudo que é preciso fazer para implementar um módulo é herdar de “NinjectModule” e sobrescrever o método Load(). O método Bind() do container é responsável por registrar a dependência. No exemplo, o container é “ensinado”a resolver a dependência “IConversor” com o tipo concreto “ConversorPdf”.

[NOTA]
A implementação de um módulo, assim como o próprio nome “módulo”, varia de um container para outro. O Windsor, por exemplo, chama um “módulo” de “installer”. No entanto, a ideia é a mesma.

Outros, como o SimpleInjector, sequer possuem esse conceito e chamamos os métodos para registrar direto pelo objeto container:

//.....
var container = new Container();
container.Register(typeof(IConversor), typeof(ConversorPdf));
//.....

[/NOTA]

Por fim, segue a implementação do NinjectControllerFactory:

public class NinjectControllerFactory : DefaultControllerFactory
{
    private readonly StandardKernel _container;

    public NinjectControllerFactory(StandardKernel container)
    {
        if (container == null) throw new ArgumentNullException("container");
         _container = container;
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
         return _container.Get(controllerType) as IController;
    }

    public override void ReleaseController(IController controller)
    {
        _container.Release(controller);
    }
}

Por simplicidade, ao invés de implementar IControllerFactory, a factory herda de DefaultControllerFactory. Feito isso, basta sobrescrevermos os métodos GetControllerInstance() e ReleaseController().

No método GetControllerInstance(), pedimos ao container para nos entregar o controller requerido, resolvendo todas as dependências exigidas por ele. No Ninject, o método Get() é o responsável por resolver dependências embora o nome mais comum nos containers (e que faz mais sentido) é Resolve().

No método ReleaseController(), pedimos ao container para liberar o controller e todas as suas dependências, por meio do método Release(). Isso, em geral, é feito pelo Garbage Collector do .Net, mas é uma boa ideia chamar o Release() pois podem haver dependências não-gerenciadas, ou seja, que precisam ter seus métodos Dispose() chamados o mais rápido possível.

É isso. Implementamos um composition root com apenas duas classes novas (NinjectWebAppModule e NinjectControllerFactory) e ligamos tudo no Application_Start() da aplicação.

4. DI EM AÇÃO

Para ver a mágica em funcionamento, basta criar as classes IConversor e ConversorPdf e injetar o IConversor em um controller qualquer:

public class HomeController : Controller
{
    private readonly IConversor _conversor;
    public HomeController(IConversor conversor)
    {
        _conversor = conversor;
    }
}

Ao solicitar o controller acima, o método GetControllerInstance() da NinjectControllerFactory será chamado e pedirá ao container para entregar o controller desejado resolvendo todas as dependências existentes (no caso, apenas IConversor).

Você deverá notar, então, que no construtor de HomeController será injetado um objeto do tipo “ConversorPdf”.

5. ENCERRANDO

Nesta segunda parte da série “Padrões de DI”, vimos o conceito conhecido por Composition Root e uma forma de implementá-lo usando um DI container em uma aplicação ASP.NET MVC.

Este padrão nos diz ONDE compor os objetos: no start da aplicação.

Ao longo do post, vimos indiretamente dois outros padrões – Register-Resolve-Release e Constructor Injection – que discutiremos nos posts seguintes.

[]s e até lá!

Anúncios

6 comentários em “Padrões de DI: Composition Root

  1. Dúvida, no Asp.net contamos com o seam para auxiliar na DI porem em uma Console Application não temos esse recurso. Como posso fazer?

    1. Ola, David

      Nesse caso é bem simples: o ponto de entrada é o método main(). Nele você chama o seu “bootstrap” pra registrar as dependências ANTES do seu código:


      void Main()
      {
      // aqui cria, registra as dependências e retorna o container configurado
      var container = DIComposer.Compose();

      // seu codigo
      var dependencia = container.Resolve();
      // .....

      // no final, chama o release do container ou tudo pode estar no bloco using
      container.Release();
      }

      Mais ou menos como acima. Não sei o que sua app console faz, mas o miolo que usa o container poderia ficar bem encapsulado em outras classes para o Main() não ficar gigantesco…

      []s

      1. Obrigado Robson suas respostas estão ajudando muito. O projeto o qual estou trabalhando é um importador de CSV onde nele constam atributos (cor, tamanho, gênero, faixa etária) de um produto os quais preciso validar cada uma das informações encapsuladas em um transação que no final realizo o Commit ou disparo e-mail com a relação de erros.

        Outro ponto é que quero deixar a Main enxuta com pouco código então criei uma classe Atributos onde tenho os métodos (CriarCor, CriarTamanho, CriarGenero…) .

      2. Legal, David.
        Dá uma estudada no Command (Design pattern) e nos CommandProcessors (a forma que implemento os serviços de aplicação num dos posts que te passei) que podem ser uteis caso voce tenha varias operacoes para executar.
        []s

Participe! Vamos trocar uma ideia sobre desenvolvimento de software!

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s