Padrões de DI – Parte 2: Composition Root

Na primeira parte da série “Padrões de DI”, 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.

1. O QUE É

Composition Root é o componente da aplicação onde é feita a composição dos objetos.

Este componente fica no ponto de entrada da aplicação, que varia de acordo com a tecnologia. Por exemplo, em uma aplicação do tipo console, a entrada é o famoso método “main” enquanto que em uma aplicação ASP.NET MVC, a entrada é um IControllerFactory.

Ainda dependendo da tecnologia e se você estiver usando um container de DI, é normal que o código do Composition Root seja “splitado” em duas partes: uma que registra todas as dependências (que é chamada uma única vez, quando a aplicação sobe) e outra que efetivamente resolve/libera as dependências (logo mais, um exemplo de implementação).

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 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

Neste exemplo, usaremos um container de DI. Tudo o que precisamos fazer é instanciá-lo, registrar as dependências e permitir que o container resolva e libere as dependências após seu uso.

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

public class MvcApplication : 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. CONCLUSÃO

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.

» Continuar para [Parte 3 – Register Resolve Release]

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

Adicione o seu

  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?

    Curtir

    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

      Curtir

      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…) .

        Curtir

      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

        Curtir

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 )

Foto do Google

Você está comentando utilizando sua conta Google. 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 )

Conectando a %s

Blog no WordPress.com.

Acima ↑

%d blogueiros gostam disto: