Omar del Valle Rodríguez

DDD: 2- Framework de IoC (Service Pack 1)

Después de la última sacudida recibida Sad smile por la encapsulación del Container que hicimos en el artículo anterior, sacamos este Service Pack 1 Smile y de paso cuento un poco cómo llegamos aquí.

Cuando me pasan estas cosas, siempre recuerdo el pasaje de una lectura que tengo por casa…

“Denis Cochin preparo un estudio sobre Química y lo presento a Pasteur. El trabajo comenzaba con las palabras. “Se sabe que...“

- ¿Que es lo que se sabe? - Interrumpió Pasteur al leerlo. - No se sabe nada.
- Pero señor - Contesto Cochin - lo que iba a citar es un trabajo de usted.

- No importa - replico Pasteur. - Yo podría haberme equivocado. Empiece Ud. de nuevo”

Tras los comentarios de Unai y Eduard más la referencia al artículo de Mark sobre el patrón o anti-patrón Service-Locator (Martin Fowler, 2004), había que profundizar más en el tema, así que sin pensarlo dos veces me compré el libro de “Dependency Injection in .NET” el cual recomiendo muchísimo por la claridad en el contenido, además de estar orientado directamente a NET.

En el libro, cuando Mark habla del Service Locator como un anti-patrón, dice: “Some people consider it a proper design pattern, whereas others (me included) consider it an anti-pattern.”

Tras esta definición no puedo evitar preguntarme ¿Y en qué bando me pongo yo? Es evidente que la opinión de Eduard, Unai, Mark y seguramente la de muchos otros, pesa muchísimo, así que lo más probable es que termine más rápido si busco alguna deficiencia en la implementación anterior que me lleve finalmente a verlo como ellos.

Tras no mucho tiempo… me imaginé la siguiente situación:

Tengo varios controladores (MVC) que usan inyección de dependencia con uno o varios servicios (Application services), tengo servicios que usan dependencias a uno o más repositorios, tengo varios repositorios que usan dependencia a una unidad de trabajo, tengo una unidad de trabajo que depende de una cadena de conexión. En esa situación, tendré en mis controladores llamadas al Resolve del container para crear los servicios, en los servicios llamadas al Resolve para crear instancias de los repositorios, tendré también llamadas al Resolve en los repositorios para recuperar la unidad de trabajo y así en toda mi arquitectura...

Salta a simple vista que mis controladores dependen de mis servicios y del Service Locator. Los servicios dependen de los repositorios y del Service Locator, los repositorios dependen de la unidad de trabajo y del Service Locator. ¿Qué pasa si quiero reutilizar los repositorios? ¿O si quiero reutilizar los servicios? ¿O si quiero reutilizar mi unidad de trabajo? Pues que en todo momento dependeré del Service Locator…

Sad smile No seguí buscando… mi objetivo era estar desacoplado del Framework de IoC y terminé atando toda la arquitectura.

¿Cómo soluciono esto entonces?

-Constructor Inyection
-Property Injection
-…

Si mis controladores recibieran los servicios que necesitan para trabajar mediante el constructor, no necesitaría una referencia al Service Locator… y lo mismo pasa en toda la cadena de inyección.

¿Cómo funciona esto? Imaginen que tengo…

Controller(IService srv) – Service(IRepository repo) – Repository(IUnitofWork uow) – UnitOfWork()

1- Un controlador necesita una instancia de un servicio, se intenta crear una instancia de ese servicio.
2- Para crear el servicio se necesita un repositorio, se intenta crear una instancia del repositorio.
3- Para crear el repositorio se necesita una unidad de trabajo, se intenta crear la unidad de trabajo.
4- Se crea la unidad de trabajo (no depende de nadie).
5- A partir de aquí, se inyecta la unidad de trabajo al repositorio, el repositorio al servicio y el servicio al controlador.

Este algoritmo me dice que todo empieza desde un punto único. A este punto Mark lo llama Composition Root. “A COMPOSITION ROOT is a (preferably) unique location in an application where modules are composed together.”

Un DI Container es quien me dice quién es el Container utilizado en mi aplicación y el encargado de componer todo el grafo de objetos. Este debe ser referenciado únicamente desde el Composition root (De aquí que Eduard y Unai no vean la necesidad de abstraer el Container) y se inicializa solo una vez en todo el ciclo de vida de la aplicación.

Conociendo un poco más que ayer, decidí hacer “refactoring” a todo lo visto ayer (Por llamarle de una forma menos dura al hecho de borrar todas las interfaces e implementaciones de mi Service Locator). Después de un rato, me quedó esto:

Mi DI Container. …

public static class UnityContainerFactory
{
    private static readonly IUnityContainer _container;

    static UnityContainerFactory()
    {
        _container = new UnityContainer();
        Configure();
    }

    private static void Configure()
    {
        var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
        section.Configure(_container);

        // Aditional Configuration
        // Container.RegisterType<IBaseType, ModuleAType>("moduleA")
        // Container.RegisterType<IBaseType, ModuleBType>("moduleB")
    }

    public static IUnityContainer GetContainer()
    {
        return _container;
    }

}

¿Quién sería mi Composition Root? Pues ya esto depende del tipo de aplicación que se vaya a crear, ya que cada aplicación puede tener una definición diferente para su “único punto de entrada”. Por ejemplo, para una aplicación MVC, Mark aconseja un IControllerFactory, aunque si es para MVC3, yo prefiero el IDependencyResolver.

Mi Composition Root para MVC3 sería:

public class UnityDependencyResolver : IDependencyResolver
{
        private readonly IUnityContainer _container;

        #region Implementation of IDependencyResolver

        public UnityDependencyResolver(IUnityContainer container)
        {
            _container = container;
        }

        public object GetService(Type serviceType)
        {
            return _container.IsRegistered(serviceType) ? _container.Resolve(serviceType) : null;
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return _container.ResolveAll<object>().Where(s => s.GetType() == serviceType);
        }
}

Aquí puedes encontrar un pequeño post de Steve explicando la implementación anterior.

Para el caso de un servicio WCF me gustó la forma en que lo implementaron en la guía de arquitectura N Layer donde se implementa la interfaz IInstanceProvider:

public class UnityDependencyProvider : IInstanceProvider
{
    private readonly IUnityContainer _container;
    private readonly Type _serviceType;

    public UnityDependencyProvider(Type serviceType)
    {
        if (serviceType == null) throw new ArgumentNullException("serviceType");

        _serviceType = serviceType;
        _container = UnityContainerFactory.GetContainer();          
    }

    #region Implementation of IInstanceProvider

    public object GetInstance(InstanceContext instanceContext)
    {
        return GetInstance(instanceContext, null);
    }

    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        return _container.Resolve(_serviceType);
    }

    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
        if (instance is IDisposable) ((IDisposable)instance).Dispose();
    }

    #endregion
}

Y luego creamos el atributo con el que marcaremos los servicios WCF que necesiten inyección de dependencias…

public class UnityDependencyProviderServiceBehavior : Attribute, IServiceBehavior
{
    #region Implementation of IServiceBehavior

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }

    public void AddBindingParameters(ServiceDescription serviceDescription,
                                                     ServiceHostBase serviceHostBase,
                                                     Collection<ServiceEndpoint> endpoints,
                                                     BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (var dispatcher in serviceHostBase.ChannelDispatchers.OfType<ChannelDispatcher>())
        {
            dispatcher.Endpoints.ToList().
                ForEach(endpoint =>
                {
                    endpoint.DispatchRuntime.InstanceProvider = new UnityDependencyProvider(serviceDescription.ServiceType);
                });
        }
    }

    #endregion
}

"As you can see, it took me a couple of years of intense use to realize the shortcomings of SERVICE LOCATOR and that better alternatives existed. For this reason, I find it easy to understand why so many developers find it attractive…” (Gracias Mark) Smile

Los test…

El problema de los test está en que no tenemos un evento al cual nos podamos subscribir ni interfaz que implementar… Entonces, ¿Cómo creo los test sobre el IoC Container? by Scott…

Basados en las interfaces IA, IB y las clases A y B que escribimos en el artículo anterior, tendríamos los siguientes test:

[TestClass]
public class IocTest
{
    private readonly IUnityContainer _ioc;

    /// <summary>
    /// En los test, este es mi punto de entrada (Composition root)
    /// </summary>
    public IocTest()
    {
        _ioc = UnityContainerFactory.GetContainer();
    }

    [TestMethod]
    public void TestCreateObjetWithoutConstructorParameters()
    {
        var a = _ioc.Resolve<IA>();

        Assert.IsNotNull(a);
        Assert.AreEqual(1, a.ObjectId);
    }

    [TestMethod]
    public void TestCreateObjetWithConstructorParameters()
    {
        //var a = new A();  Ya no necesitamos hacer esto...
        //el framework de IoC se encarga de construir IA

        var b = _ioc.Resolve<IB>();

        Assert.IsNotNull(b);
        Assert.AreEqual(1, b.ParamInjector.ObjectId);
    }
}

Los elementos de configuración, ya podemos inyectarlos tal y como nos pedía Juanma:

[TestClass]
public class ConfigTest
{
    private readonly IUnityContainer _ioc;

    public ConfigTest()
    {
        _ioc = UnityContainerFactory.GetContainer();
    }

    [TestMethod]
    public void TestConfiguration()
    {
        var global = new Global(new AppSettingsHelper());
        var settings = global.Settings;

        Assert.AreEqual("My Project DDD", settings.Name);
        Assert.AreEqual("es-ES", settings.LanguageDefault);
        Assert.AreEqual("dd-MM-yyyy HH:mm", settings.DateTimeFormat);
        Assert.AreEqual(1, settings.TimeZoneOffset);

        Assert.IsInstanceOfType(global.SettingsHelper.GetBoolean("bool"), typeof(bool));
    }

    [TestMethod]
    public void TestIocConfiguration()
    {
        var global = _ioc.Resolve<IGlobalSettings>();

        Assert.IsNotNull(global);
        Assert.IsNotNull(global.Settings);
        Assert.IsNotNull(global.SettingsHelper);

        Assert.IsInstanceOfType(global.Settings, typeof(AppConfigurationElement));
        Assert.IsInstanceOfType(global.SettingsHelper, typeof(AppSettingsHelper));
    }
}

test-config

Gracias a Unai y a Eduard por hacer posible esta mejora…

PD: Aún no he probado los módulos implementados para MVC3 o para WCF… ya os contaré Winking smile

Salu2

Posted: 22/11/2011 23:53 por Omar del Valle Rodríguez | con 7 comment(s)
Archivado en: ,,,,
Comparte este post:

Comentarios

Unai ha opinado:

Si me lo permites te voy a hacer otro comentario... Me imagino que en el libro de Mark habrás visto otro patrón RRR ( Register-Resolve-Release). El problema de IDependencyResolver es que no tiene Release ( aunque es cierto que hay una llamada a Dispose si el controlador lo implementa) y por lo tanto a muchos les/nos chilla bastante... por lo que por ahora se sigue haciendo un ControllerFactory en vez de esta implementación...

saludos

Unai

# November 23, 2011 12:17 AM

Luis Ruiz Pavón ha opinado:

Que gran libro Unai!!! Tengo pendiente hacer una review en el blog.

Por cierto (Autobombo) hace un tiempo que escribí un pequeño artículo sobre IDependencyResolver:

geeks.ms/.../asp-net-mvc-3-por-qu-233-idependencyresolver-no-cumple-con-la-filosof-237-a-de-los-ioc.aspx

Un saludo.

# November 23, 2011 12:27 AM

Omar del Valle Rodríguez ha opinado:

@Unai, claro que se permiten comentarios, gracias a eso estamos en este punto :)

Sobre RRR, sí que lo vi... y en la implementación para el caso de servicios WCF se ve claramente em el método ReleaseInstance.

El tema de MVC3 pensé resolverlo usando alguna implementación PerRequest del LifetimeManager de Unity. De todas maneras me lo miro un poco más...Gracias de nuevo.. ;)

@Luis.. gracias por el autobombo.. :P

Salu2 a los dos...

# November 23, 2011 12:52 AM

Juanma ha opinado:

Buenos reflejos, Omar, ahora esto va teniendo una pinta mucho mejor :-)

Viendo este ejemplo de dependencias:

Controller(IService srv) – Service(IRepository repo) – Repository(IUnitofWork uow) – UnitOfWork()

tengo curiosidad por ver cómo vas a montar el UoW.

# November 23, 2011 2:35 PM

Omar del Valle Rodríguez ha opinado:

Juanma, si te soy sincero, no tengo ni idea... :)

Tengo claro cómo lo hacía antes, y que deseo que esto termine siendo algo parecido a un template que me ayude a tener todo listo cuando inicie un proyecto con este tipo de arquitectura. Por eso, trataré en todo momento de no depender de Framework específicos (NH, Linq2Sql o EF).

Está claro que con IoC ya vimos que no vale la pena. Con el resto de la arquitectura, ya iremos viendo con la ayuda del resto de geeks si vamos o no por buen camino.. :)

Salu2...

# November 24, 2011 9:32 AM

Eduard Tomàs i Avellana ha opinado:

Buenas,

Hay un debate abierto sobre si es necesario abstraer IUnitOfWork y tus propios repositorios, o pasar de esas abstracciones y usar directamente el ORM seleccionado (ya sea EF, NH u otro ya que cualquier ORM actual proporciona repositorios y unit of work de serie). Hay posturas en ambos sentidos y personalmente es una decisión que creo que hay valorar en cada caso.

Hace algún tiempo Unai escribió un post relacionado con esto (geeks.ms/.../idbset-iobjectset-como-repositorios-o-tus-propias-abstracciones.aspx).

Alguien que está en contra de dichas abstracciones es Oren Eini (Ayende) como se puede leer en su blog (ayende.com/.../repository-is-the-new-singleton y ayende.com/.../architecting-in-the-pit-of-doom-the-evils-of-the-repository-abstraction-layer).

Udi Dahan escribió hace tiempo sobre el tema y el prefiere usar Query Objects (www.udidahan.com/.../query-objects-vs-methods-on-a-repository)

Como se puede ver es un tema que da para mucho...

Hay que valorar siempre que ganamos con esas abstracciones y si su uso nos acerca o no a la solución o solo nos añade más complejidad. Unai lo resumió perfectamente en su post cuando dijo "es la responsabilidad de cada uno".

Un saludo!

# November 24, 2011 12:36 PM

Omar del Valle Rodríguez ha opinado:

Gracias por la recopilación Eduard..... ;) yo creo q deberíamos abrir un post solo para eso :)

El tema esta en q hoy en día siempre trabajas con mas de un origen de datos. El principal sigue siendo la base de datos, pero también interactuamos con Google (docs, calendars, contacts), twitter, Facebook, etc. y cada día aparecerán mas...

Si trabajamos con los repositorios y la unidad de trabajo del ORM, estamos cerrandonos a esa fuente de datos, tu arquitectura tendría q variar cuando te pidan recuperar los usuarios de Facebook... :( no?

No seria mas cómodo olvidarnos si los contactos salen de Google, Facebook o nuestra base de datos?

Salu2;

# November 24, 2011 5:32 PM