.NET, Architecture, ALM, Cloud

Publicité

Chaînes de Responsabilités & Unity Application Block

Aussi bien pour nos composants techniques (logger, configuration, cache...) que applicatifs (DAL, BAL...), il convient d'adopter une démarche une démarche de couplage faible (ou lâche) pour les interactions entre nos différents composants techniques et applicatifs.

 

Certains développeurs ou architectes nous diront que ce type d'approche ne s'applique pas forcément à tous les projets notamment les 3 tiers classiques car cela rajoute de la complexité et du temps de développement. Ce n'est pas mon avis. Pourquoi ? Car en se dotant des bon outils à côté (framework IoC pour éviter à avoir à gérer des factory dans tous les sens, outils tels que Resharper pour faciliter le fameux "Go to Definition" entre ses interfaces et implémentations), ce type d'approche ne rajoute pas de complexité et au contraire va rendre votre solution plus flexible et plus modulable sans avoir à refactoriser votre code en cas de remise en cause de tel ou tel composant.

 

Un exemple, votre solution utilise un Framework de cache spécifique et vous décidez de l'implémenter en mode couplage fort, c'est-à-dire que votre Framework de cache sera référencé directement sur tous vos projets nécessitant  le moteur de cache.  Problème un an plus tard, vous devez changer votre moteur de cache et cela aura un impact sur toutes vos couches applicatives.

 

En adoptant un couplage léger entre votre service de cache et vos couches applicatives (à l'aide par exemple d'une interface ICacheService qui sera le seul lien entre vos couches applicatives, le changement de Framework de cache se fera simplement en définissant une nouvelle implémentation de ICacheService qui sera linké ensuite via votre framework IoC par configuration ou code).

 

Pour sortir à présent de la théorie pour aller dans la pratique, nous allons limiter notre exemple aux couches DataAccessLayer et BusinessLayer avec le framework IoC fournit par Microsoft Unity Application Block.

Pour ceux qui ne connaissent pas Unity Application Block, je vous renvoi vers mon ancien billet de présentation d'Untity (basé sur la version 1).

 

Pour un composant Business simpliste de recrutement de personnel, nous aurons :

 

public class RecruitmentBusiness : IRecruitmentBusiness

    {

        [Dependency]

        public ILoggerService LoggerService { get; set; }

 

        [Dependency]

        public IJobSeekerDal JobSeekerDal { get; set; }

 

        [Dependency]

        public IJobDal JobDal { get; set; }

 

        ....

 

    }

 

L'attribut "Dependency" est fournit par le Framework Unity et va donc nous permettre d'injecter les dépendances avec l'implémentation qui a été définit  en configuration.

 

Le principal inconvénient  qu'on pourrait  faire à cette approche est que comme l'attribut "Dependency" est fournit par Unity Application Block, vous allez devoir  vous trimballer avec Unity en référence dans tous vos projets qui va donc en pleine contradiction avec notre volonté de vouloir avoir un couplage faible entre nos composants techniques et couches applicatives.

 

La solution va consister donc pour palier à ce problème de définir votre propre attribut d'injection de dépendance indépendamment du Framework IoC choisit (attribut à définir soit dans votre Framework d'entreprise ou assembly utilitaire de votre solution) :

 

public class MyDependencyAttribute : Attribute

    {

    }

 

Comme vous pouvez le voir, notre attribut n'a aucune relation avec notre framework IoC. Notre objet Business aura donc à présent la forme suivante :


public class RecruitmentBusiness : IRecruitmentBusiness

    {

        [MyDependencyAttribute]

        public ILoggerService LoggerService { get; set; }

 

        [MyDependencyAttribute]

        public IJobSeekerDal JobSeekerDal { get; set; }

 

        [MyDependencyAttribute]

        public IJobDal JobDal { get; set; }

        ...

 

    }

 

Il va falloir indiquer à présent à Unity comment gérer notre nouvel attribut. Pour cela nous allons tout d'abord définir notre stratégie de build Unity lorsque l'instance de RecruitmentBusiness sera initié par Unity pour gérer notre attribut "MyDependency" :


 

public class MyDependencyBuilderStrategy : BuilderStrategy

    {

        public override void PostBuildUp(IBuilderContext context)

        {

            //Récupération du type instancé par Unity

            Type instanceType = context.Existing.GetType();

 

            //Parcours des propriétés du type

            foreach (PropertyInfo property in GetProperties (instanceType))

            {

                //On vérifie si le propriété a un attribut "MyDependency de définit

                MyDependencyAttribute attr =

                    (MyDependencyAttribute)

                    Attribute.GetCustomAttribute(property, typeof(MyDependencyAttribute));

                if (attr != null

                    && property.GetValue(context.Existing, null) == null)

                {

                    //Si c'est le cas on injecte la dépendance via notre container

                    property.SetValue(context.Existing, ContainerService.Instance.GetInstance(property.PropertyType), null);

                }

            }

 

            //On passe à la stratégie de PostBuild suivante...

            base.PostBuildUp(context);

        }

 

        public override void PreTearDown(IBuilderContext context)

        {

            if(context.Existing == null)

                base.PreTearDown(context);

 

            //On récupère le type de l'instance à libérer

            Type instanceType = context.Existing.GetType();

 

            //Parcours des propriétés du type

            foreach (PropertyInfo property in GetProperties (instanceType))

            {

                //On vérifie si le propriété a un attribut "MyDependency de définit

                MyDependencyAttribute attr =

                    (MyDependencyAttribute)

                    Attribute.GetCustomAttribute(property, typeof(MyDependencyAttribute));

                if (attr != null)

                {

                    //Si c'est le cas on dispose notre composant

                   Object dependencyInstance = property.GetValue(context.Existing, null);

                    if(dependencyInstance != null)

                        ContainerService.Instance.DisposeInstance(dependencyInstance);

                }

            }

 

            //On passe à la stratégie de PreTearDown suivante...

            base.PreTearDown(context);

        }

 

        private PropertyInfo[] GetProperties(Type type)

        {

            return type.GetProperties(

                BindingFlags.GetProperty

                | BindingFlags.Instance

                | BindingFlags.NonPublic

                | BindingFlags.Public);

        }

    }

 

Une fois vos différentes stratégies de build définies, il va vous falloir créer une extension Unity pour ajouter votre chaîne de build customisée :

 

public class MyBuilderStrategiesExtension : UnityContainerExtension

    {

        protected override void Initialize()

        {

            //L'ordre a son importante !

            //Pour le build : de haut en bas

            //Pour le teardown/dispose : de bas en haut

            this.Context.Strategies.AddNew<MyDependencyBuilderStrategy>(UnityBuildStage.PreCreation);
           //Ajout de vos autres stratégies de build...

        }

    }

 

Cette extension pourra être définit en code :

 

UnityContainer container = new UnityContainer();

container.AddExtension(new MyBuilderStrategiesExtension());

 

Ou en configuration :

 

<extensions>

          <add type="... MyBuilderStrategiesExtensionUsingSystem.Framework.Container " />

</extensions>

 

Nous verrons dans un prochain billet comment optimiser ce mécanisme notamment pour régler le dernier inconvénient où avec ce mécanisme lors de l'instanciation de notre objet Business par Unity, toutes les dépendances seront chargés même si pas utilisées. La solution consistera à mettre en place un mécanisme de Lazy loading de nos dépendances.

Publicité
Retour à l'accueil
Partager cet article
Repost0
Pour être informé des derniers articles, inscrivez vous :
Commenter cet article
M
<br /> <br /> J'avoue ne pas être fan des injections par contructeur car l'objet ne pourra pas être instancié simplement sans passer aucun paramètre<br /> <br /> <br /> Par contre l'article a but pour aussi de montrer le mécanisme de stratégie de build d'unity qui vous permet d'étendre le container comme bon il vous semble en prenant pour exemple les attribut<br /> pour injecter les dépendances.<br /> <br /> <br /> Mais si vous voulez avoir zéro couplage, unity permet aussi l'injection de dépendance par configuration en définissant simplement la propriété en configuration ( )<br /> <br /> <br /> <br /> <br /> <br />
Répondre
M
<br /> <br /> Article interessant, mais meme s'il s'agit d'un attribut custom pour l'injection de dependances ça reste un attribut et le couplate est la, meme indirect, c'est un fonctionnement particulier de<br /> unity, de mon point de vue il vaut mieux injecter les dependances a partir du constructeur, il n'y a dans ce cas aucun couplage et tous les IOC sont compatibles.<br /> <br /> <br /> <br />
Répondre