L’architecture Agile : L’architecture Orientée Objet, ou l’architecture par Cas D’utilisation

Question : Que remarquez vous  en regardant à haut niveau l’architecture d’un système? Que recevez vous généralement dans “le fameux document contenant la vérité” d’un Architecte chevronné? Est ce que les mots “Service Web, MVC, Base de données MySQL, Third Party X ou Y” reviennent souvent dans ces architectures? Combien de fois aviez vous reçu ou élaboré un document, qui détaillait toutes ces “choses”? j’en ai produit moi aussi. Mais à vrai dire, je n’en étais pas satisfait. Il y’avait quelque chose qui me dérangeait,  à part le fait que ce soit un document que je devais produire (Mais ceci n’est pas notre sujet, nous y reviendrons dans un prochain Post ;-)). Ce qui me dérangeait c’est que je ne voyais pas dans le document ce que fait réellement le système! Mais plutôt une panoplie d’outils définissant le cadre logiciel avec lequel le développement et le déploiement doivent être faits.

Mes craintes ont été confirmés, mes compréhensions ont été appuyées, et ma soif sur le sujet a été bien assouvie suite à des lectures intensives et des révélations illuminantes des livres, parutions et séries de Bob C. Martin (Uncle Bob). Pour ceux qui n’ont pas le temps de lire, Voici ce que j’ai retenu pour ce sujet :

Qu’est ce qu’une architecture?

Qu’est ce que les termes MVC, Base de données, Langage de Programmation, Framework, Editeur, SOA, Service Layer, REST etc. disent pour vous? est ce que ces termes la définissent l’architecture?  Non! Ces termes représentent des Outils. De la même façon que sur le plan de construction d’une maison, vous ne voyez pas la pelle et le marque du béton qui va être utilisée, vous ne devez pas voir dans l’architecture d’un logiciel les termes ci dessous.

L’architecture d’un système est une affaire d’Utilisation. Elle doit exposer clairement l’intention du système, et non les outils avec lesquels ce dernier va être construit.

Imaginez l’architecture d’une librairie ou d’un centre culturel, vous regardez le plan et vous voyez des salles de lecture ou les lecteurs peuvent calmement lire et faire leur recherche, vous voyez des petits espaces éclairées avec des fauteuils pour prendre une petite pause, et des chambres pleines d’étagères de livres. Que voyez vous dans l’architecture d’une librairie? Vous voyez Une librairie!

Une bonne architecture doit Crier  les cas d’utilisations du système.

Par ailleurs, quand nous regardons de haut niveau l’architecture des applications, prenons en exemple les applications web, que voyons nous? nous voyons généralement la structure MVC. Nous y voyons clairement que c’est une application WEB. Par contre, les cas d’utilisation de cette application sont cachées par cette structure.

Qu’est ce qui est plus important de voir à haut niveau pour cette application? Supposons que c’est une application de gestion de commandes sur le web. Qu’aimerions nous voir “Gestion de commandes”, ou “Gestion Web” ? Clairement, nous voulons voir “Gestion de commandes”.

En fait, le MVC n’est rien d’autre qu’un mécanisme de livraison. Ni plus ni moins.  Le mécanisme de livraison ne doit en rien changer l’intention du système, et ne doit pas donc être prévalent dans l’architecture.

Nous voulons donc que nos cas d’utilisation soient le centre de notre système. Quand nous regardons l’architecture à haut niveau, nous voyons ces cas d’utilisation et nous voyons à quoi l’application sert.

Nous voulons Découpler nos cas d’utilisations des autres couches du système, de telle sorte que les autres couches soient considérées comme des “plugins” a notre couche la plus prévalante, celle qui expose les vrais cas d’utilisation du système.

Séparation de valeur

Vous pouvez imaginer que ceci ne s’applique pas pour les applications de CRUD simples. Ce qui généralement plus important serait la couche Interface Utilisateur, il n’y a pas de règles d’affaires très importants. Supposons que c’est le cas, ce que nous venons de stipuler ci-haut ne veut pas dire que l’Interface utilisateur n’est pas importante. Si  l’Interface Utilisateur est importante, disons plus importante que les cas d’utilisation, ceci veut dire qu’elle ne doit pas être affectée par les changements des autres couches.

Ce découpage  s’applique à toutes les dépendances en général.  Il permet entre autres de déterminer les coûts : En compartimentant le système par “responsabilité”, nous permettons à la Business de prendre des décisions plus prudentes et moins risquées. Si l’Interface Utilisateur est réellement la grosse partie dans le système à développer, nous pourrons alors donner un estimé plus spécifique, ce qui pourrait faire changer la business d’avis, ou non. Si c’est toujours important pour la business, c’est donc clairement communiqué. D’autre part, nous pouvons nous focaliser afin que le développement de cette partie soit le plus autonome, et que cette couche soit  la plus indépendante que possible. C’est ce que nous appelons la Séparation de valeur.

Dans tous les cas, nous voulons que les décisions sur Le UI, la base de données, le serveur web, les Services etc. soient découplées de nos cas d’utilisation. Aussi, nous voulons que les Cas d’utilisation ne connaissent rien des mécanismes de livraison.

Ces décisions peuvent (et doivent) être prises plus tard, n’est ce pas l’objectif de l’architecture de reporter ces décisions?  Un bon architecte sait comment laisser les options ouvertes le plus longtemps que possible. En fait, une bonne architecture maximise le nombre de décisions qui ne sont PAS prises.

Que gagnons nous en agissant Ainsi? Nous pouvons changer d’avis entre temps, et plusieurs fois peut être, sans affecter les cas d’utilisation du système.

Mais Comment?

Comment reporter ces décisions alors que nous devons récupérer des données de l’utilisateur et fournir des résultats. En effet, nous pouvons reporter ces décisions en structurant et en compartimentant le système de sorte de découpler les cas d’utilisation du mécanisme de livraison, et ainsi que rendre ce dernier moins relevant. Nous pouvons reporter ces décisions en concentrant nos efforts sur les cas d’utilisations et non sur l’environnement logiciel.

Exemple de mécanisme de livraison : Le WEB / MVC :

Dans une application WEB MVC, et si nous utilisons un framework,  nous remarquons généralement que les contrôleurs, vues et modèles sont étroitement reliées. Dans une “page web”,  la vue affiche une multitude d’informations et est fortement liée à la structure du modèle. D’autre part, la logique d’affaire navigue entre le modèle et le contrôleur.

Une autre mauvaise compréhension est que le modèle contient les règles d’affaires. Cette compréhension implique généralement que la couche MVC – qui n’est rien que le mécanisme de livraison – devienne la couche prévalante.

Je me répète : Dans l’architecture d’un système de gestion de commande web,  qu’aimerions nous voir “Gestion de commandes”, ou “Gestion Web” ? Clairement, nous voulons voir “Gestion de commande”!

Nous devons être capable de changer complément le mécanisme de livraison sans toucher aux cas d’utilisations.

Un vieux problème :

Nous pouvons qualifier ce problème de vieux problème, puisque ce dernier a été étudié  en 1992 par Ivar Jacobson. Dans son livre “Object-Oriented Software Engineering“, Jacobson indique que nous devons voir les systèmes sous forme d’interactions avec l’utilisateur. Nous devons décrire ces interactions avec des concepts qui n’impliquent pas un mécanisme de livraison. En d’autres termes, sans utiliser les termes spécifiques de “clic”, “page”, “bouton” etc.

Jacobson appelle ces interactions “des cas d’utilisation” Jacobson indique aussi que le développement d’applications doit être piloté par ces cas d’utilisation. Ces C.U. représentent le Centre Organisationnel et les abstractions autour des quels le système est construit.

Qu’est ce que cela nous donne? Quand nous regardons un système pareil, nous voyons l’intention du système!

Qu’est ce qu’un Cas d’utilisation?

Un cas d’utilisation est la façon avec laquelle un utilisateur interagit avec le système dans le but d’atteindre un certain objectif. Par exemple dans notre système de gestion de commande :

Créer une commande
 Données :
id-client,info-contact-client,destination-livraison,méthode-livraison,info-payement
Cas normal :
l'utilisateur issue un nouvel ordre avec les données ci-dessous
le système valide toutes les données
le système créer la commande et détermine le code de la commande
le système délivre le code de la commande à l'utilisateur
Cas Exceptionnel (ou cas étendu) : Erreur de validation
le système délivre un message d'erreur

 

Notez qu’il n’y a aucune notion de “page”,bouton etc. Tout ce que le Cas d’utilisation expose sont comment l’interaction se manifeste. Le cas d’utilisation est essentiellement un algorithme qui interprète des données en entrée et génère des données de sortie. Ce qui implique que nous pouvons créer un objet qui implémente ces opérations.

Attention, un cas d’utilisation est diffèrent d’une User Story en Agile. Le cas d’utilisation est beaucoup plus “complet” qu’une “user story”. Un ensemble de user stories évoluent et livrent en général un cas d’utilisation.

Partitionnement

Les cas d’utilisation contiennent les règles d’affaires. De plus, le plus qu’on crée des cas d’utilisation, le plus nous découvrons d’objets et le plus de logique d’affaire (algorithmes). Ou devons nous implémenter ces règles? quels sont les types d’objets dans lesquelles ces règles doivent résider? Quelle est l’architecture d’un tel système dont les cas d’utilisation sont le centre? 

C’est une erreur de les implémenter dans votre MVC. Les objets dont on parle sont dans un niveau architectural plus haut. Nous allons oublier le MVC qui est plus tôt une couche bas niveau. Nous allons adapter le MVC à ces Objets un peu plus tard.

Jacobson partitionne l’architecture en trois niveau :

  1. Les Objets d’affaire, qu’il appelle “Entités”
  2. Les Objets d’Interface Utilisateur, qu’il appelle “Boundaries”
  3. Les Objets de cas d’utilisation, qu’il appelle “Contrôles”, et que nous allons appeler “Interactors” pour ne pas nous mélanger avec les objets Contrôleurs du MVC.

Les Objets “Entités” sont des dépôts (Repository) contenant des règles d’affaires indépendant de l’application. Les méthodes de ces objets effectuent des opérations qui gèrent leur donnée indépendamment de l’application qui les utilise.

Par exemple, un  Produit peut être utilisé dans plusieurs Systèmes, comme un système d’ajout de produits, un système d’inventaire, ou encore un système de catalogue en ligne. Les méthodes de cet Objet sont utiles pour ces systèmes. Cet objet n’a aucune méthode qui est spécifique à l’une ou l’autre de ces applications.

Les méthodes spécifiques aux applications se trouvent plutôt dans les Objets “Interactor”. Les cas d’utilisation sont aussi spécifiques aux applications, et sont implémentés aussi par les Objets “Interactor”. Les Objets Interactor sont donc specifiques aux applications. Par exemple, dans un système de gestion de commande, l’objet “Create Odrer” ou l’objet “Order Item” appartiennent au système d’ajout de commande.  Ils sont donc implementés au niveau de la couche Interactor.

L’objet Interactor appelle les méthodes sur les objets de type Entité (donc des méthodes qui sont indépendants de l’application) afin de remplir sa logique spécifique et d’atteindre l’objectif du cas d’utilisation

Diagram1

L’une des responsabilités du Cas d’utilisation est d’accepter des données d’entrée et de retourner des données à l’utilisateur. Ceci est le troisième type d’objets : L’objet de type “Boundary”.

Les objets Boundary  isolent le mécanisme de livraison du Cas d’utilisation, et offrent un chemin de communication entre ces deux couches.

Tous les classes de la couche MVC, ou d’un système par Console sont des mécanismes de livraison. Le Cas d’utilisation ne “connait” pas ces couches.

Comment ça se passe

Diagram2

Le mécanisme de livraison

  1. recueille les donnes de l’utilisateur
  2. Il les transforme en une donnée canonique appelée modèle de la requête
  3. Les fournit à l’Interacteur via le Boundary

L’interacteur

  1. invoque sa logique d’affaire spécifique à l’application
  2. manipule les objet Entité et leur règles agnostique de l’application
  3. recueille les données de résultat
  4. les emballe dans un modèle résultat canonique ou modèle de la réponse
  5. les fournit au mécanisme de livraison via le boundary.

Tout ça est beau, mais ça ne dit pas encore Comment devons nous concevoir notre architecture pour Isoler les cas d’utilisation des Use Cases.

Imaginons que le mécanisme de livraison de notre système de gestion de commande soit le web. Le webserver, les classes de routage, le HTML, les Contrôleurs MVC, les Vues etc. doivent vivre dans ce mécanisme. Le modèle Aussi! Mais que doit contenir le modèle?

Dans les MVCs Web de nos jours, les vues affichent plus que les informations d’un Objet mais plutôt le résultat de collaboration de plusieurs objets. Dans le MVC, Le modèle doit contenir justement le résultat de la collaboration de ces objets qui implementent le Use Case. Un modèle est un objet unique, mais ce n’est pas un objet d’affaire. Un Objet Modèle n’est rien de plus qu’une structure de données qui peut traverser les “Boundaries” entre le mécanisme de livraison et le composant de Use Cases.

Diagram3

Voici ce qui se passe dans Le MVC  :

  1. Le serveur web reçoit la requête HTTP
  2. Il exécute cette requête via son mécanisme de routage afin de déterminer le contrôleur relatif
  3. Le contrôleur analyse la requête et extrait la donnée de requête utilisateur, en l’emballant dans une structure de données. cette structure de données ne contient tous les meta données de la requête HTTP mais uniquement les données relevant de la requête utilisateur. C’est l’objet “modèle de la requête”. Si vous regardez cet Objet indépendamment du contexte, vous remarquerez qu’il est bien agnostique du mécanisme de livraison.

Ce modèle est livré à l’interacteur via le boundary. L’interacteur Orchestre la magie de conversion du modèle de la requête en modèle de réponse. Il implémente le cas d’utilisation et applique la logique d’affaire en utilisant les objets Entités. il recueille les données de résultat et les place dans une structure de donnée qui est le modèle de la réponse.  Ici encore, la donne n’a rien a voir avec le web. Cette structure est passee a l’objet “Presenter” qui adapte la donnée dans un format plus apaté a l’affichage et la passe a la vue qui l’affiche en HTML (J’expliquerai le Pattern Model-View-Presenter dans un prochain post).

Analyse

Diagram4Mais que sont ces Objets Boundary ?

Remarquez deux ensembles d’interfaces dans notre diagramme :

  • Le premier ensemble (Boundary1) est utilisé par le contrôleur et implementé par l’interacteur. Il accepte une structure Modèle de requête.
  • Le deuxième ensemble (Boundary2) est utilisé par l’interacteur et implementé par le Presenter. il accepte une structure Modèle de Réponse.

Ce sont ces interfaces qui representent le Boundary!  Ils appartiennent à l’application, et  font partie de l’architecture de l’application. Le mécanisme de livraison en dépend et les implemente même.

Ce que nous venons surtout de faire via cette architecture, c’est l’utilisation du Principe d’Inversion de Controle qui fait que :

Il n’y a aucune dépendance qui traverse la limite de l’architecture des cas d’utilisation en pointant vers le mécanisme de livraison.  Le niveau architectural plus bas (le MVC) pointe vers le niveau plus haut, en l’occurrence notre composant de Cas d’utilisation.

C’est ce principe qui permet en fait le découplage entre les composants de l’application. Toute la beauté du Paradigme Orienté Objet est en grande partie dans les subtilités de Ce principe. Je reviendrai sur la gestion des dépendances en orienté objet dans un prochain Post.

Conclusion

Tous les objets que nous venons de voir sont des objets simples (Old Plain Objects). Ce qui veut dire que vous pouvez les développer, les tester et les déployer sans avoir besoin d’un framework, ni même d’un serveur web pour les rouler. Vous pouvez avoir l’entièreté de votre application qui marche avant de prendre une décision sur le serveur web. Vous pouvez reporter la décision pour longtemps. vous pouvez changer d’avis de ne plus utiliser le web comme mécanisme de livraison, et vous ne perdez rien de la logique de votre application.

Nous venons aussi de découpler nos composants ce qui nous permettra de remplacer nos “plugins” sans impact sur nos Cas d’utilisation.

N’est-ce pas ce qu’est une bonne architecture doit permettre?

Mais attendez, comment devons nous faire pour la base de données? est ce que les entités doivent être persistés dans la Base? quel serveur de base de données allons nous utiliser? Quelle décision devons nous prendre maintenant?

Ceci est une histoire pour un Prochain Post!

Anis Berejeb

Anis est avant tout un passioné de l'agilité et du développement. Avec plus de 15 ans dans le domaine du développement web, son expertise combine des connaissances accrues dans l'ensemble des notions partant du développement logiciel jusqu'à l'organisation des équipes dans les environnements agiles à grande échelle.

You may also like...

12 Responses

  1. Merci pour cet excellent résumé du Clean Architecture.

    Voici quelques commentaires pour discussions:

    1) Les Entités ne sont pas forcément des dépôts (Repositories). C’est seulement le cas dans un modèle anémique (typiquement avec Active Record). Les Entités sont les objets d’affaires (représentant les concepts du domaine) avec les comportements d’affaires, mais pas la logique d’entreposage.

        Je préfère habituellement présenter les Repositories (~ Gateway en CleanArchitecture) selon Fowler. Le Repository est un entrepôt distinct qui a pour but l’entreposage des Entités. Ainsi, c’est le Repository qui a les méthodes “findByXYZ, persist(entite), remove(entite), …” qui permettent de déshydrater / réhydrater  les entités. Le Repository est une interface dans le couche Domaine et ses implémentations sont dans la couche de données / infrastructure.

        Petite illustration: http://owl.li/zQScr

       C’est ce que l’on a dans un domaine riche. Si on veut faire un domaine avec des ActiveRecords, alors l’objet possède les deux rôles (Entité et Repository), mais ça reste deux rôles distincts.

    2) Dans la figure avec l’Interactor, l’Entité (le Repository en fait ici) sert aussi de “Factory”. Normalement, un Repository ne devrait pas à la fois créer et entreposer (SRP), car les mécanismes de création et d’entreposages peuvent être distincts et évoluer séparément. On peut avoir des stratégies de création qui n’ont pas de liens avec l’entreposage.

         Je trouve l’exemple du « Create Order » un peu confondant en ce sens. J’utiliserais, personnellement, plutôt un exemple dont la méthode appelée n’est pas du CRUD, mais un traitement d’affaires pour mieux illustrer ton point. Exemple: payOrder . 

    3) +100 sur le fait que le MVC est un mécanisme de livraison seulement.

    4) Dans la vision de Bob Martin (Clean Architecture), il y a une certaine quantité de logique qui sera dans l’Interactor. Comme tu le dis, dans sa vision, la séparation se fait en fonction du niveau d’indépendance à l’application. 

         La communauté DDD (Eric Evans) partage en gros la même philosophie que Clean Architecture, mais n’a pas exactement la même vision concernant cette séparation. Dans leur cas l’Application Service (~ Interactors) auront moins de logique et ne feront que de l’orchestration au profit du domaine (les Entités, Repositories et Factories). Juste une question de focus car DDD est plus centré sur le Domaine alors que Clean Architecture plus sur les cas d’utilisation. C’est un peu simpliste mon résumé de DDD vs CleanArchitecture, mais c’est quand même intéressant à considérer.

  2. admin dit :

    Merci Pour tes eclaircissements Félix-Antoine!

    Par rapport à “Juste une question de focus car DDD est plus centré sur le Domaine alors que Clean Architecture plus sur les cas d’utilisation.”, Selon toi justement, quelle est la différence entre le DDD et ce que Uncle Bob appelle les Cas d’utilisation? La ligne est vraiment mince entre les deux!

  3. Manuel Guilbault dit :

    Excellent article Anis, et commentaire très pertinent!

    La distinction entre les deux approches ne pourrait-elle pas être une question de contexte? J’ai l’impression que si on a pour ambition de modéliser un domaine de façon plutôt intégrale, par exemple pour pouvoir l’utiliser dans le cadre de plusieurs applications, ou dans un contexte plutôt large, sans que les cas d’utilisations soient clairement définis au préallable, il serait plus avantageux de suivre le DDD. Si le contexte est plutôt reserré, dans un contexte où on a un ensemble de cas d’utilisations bien définis, avec une application unique, ça me semblerait plus simple de suivre l’approche Clean Architecture, car ce serait au final plus facile à maintenir, le design étant plus simple et plus cohérent vis-à-vis besoins concrets, c’est-à-dire les cas d’utilisation.

  4. admin dit :

    Humm! en fait il y’a une jolie réponse ici : http://stackoverflow.com/questions/3173070/design-methodology-use-case-driven-vs-domain-driven (reponse de Vijay Patel). Que nous conseille tu Félix-Antoine?

  5. Jni dit :

    Bonjour!

    Merci pour ce bon résumé de clean architecture! J’ai bien aimé le rappel que “[tous] ces termes représentent des outils”. C’est exactement ce qu’ils sont, des outils! Clean architecture aussi est un outil, de même que MySQL, AWS, ASP.NET etc. Aucun ne répond à tous les besoin et la pire erreur à faire est d’utiliser tel outil juste parce qu’on l’a. J’ai une perceuse à béton chez moi, mais je ne m’en sert pas pour accrocher un cadre!

    Je dis souvent que le langage utilisé par les Product Owner influence directement le code, même s’ils écrivent des User Stories de haut niveau (c’est la loi de Conway). Dans l’exemple de BDD, on y retrouve “id-client, info-contact-client, etc”. Je suggère d’utiliser des termes plus “business” dans ce cas-ci. Id-client fait très base de données et sera sûrement juste un integer dans la BD. Mais par contre, “identifiant du client” – qu’est-ce qui identifie le client? Un numéro aléatoire? Une certaine information, son courriel ou NAS par exemple? Le langage de la User Story change tout, et dans ce cas-ci c’était une belle opportunité de discussion qui a probablement été ratée!

    Parlant de User Stories, j’aimerais approfondir d’avantage la différence entre celles-ci et les Use Cases. Comme vous dites, ce n’est définitivement pas la même chose, mais quelles sont les différences? Est-ce possible d’avoir U.S. > U.C.? Et U.S. == U.C.? Ou encore U.S. < U.C.? Et dans quels cas? Ce sujet ferait un autre très bon article je crois!

    J'ai aussi personnellement un problème avec la nomenclature de "cas exceptionnel" ou "cas étendu". Selon moi, ces mots apportent un sens de "bof, on verra tantôt. C'est pas supposé arriver…", alors que c'est tout le contraire! Ces cas sont souvent tout aussi important que le "happy path" et tout aussi "normaux". Un client qui a oublié de remplir un champ, c'est pas si exceptionnel! Une BD qui plante non plus en fait. "Plan for failure"!

    Pour ce qui est de Use case driven VS DDD, personnellement je trouve que c'est en grande partie une question de préférence (où mettre le focus). Effectivement, Clean Architecture est peut-être un peu mieux pour la manipulation de données et DDD semble plus approprié pour des domaines complexes où des parties d'algorithmes sont réutilisées à plein. Dans tous les cas, le message important est que le mécanisme de livraison n'est pas important!! La logique d'affaires ne devrait jamais en dépendre.

    En terminant, je crois qu'on ne dira jamais assez que le M de MVC ne représente pas le modèle d'affaires de l'application!

    Merci encore pour cet article!

  6. admin dit :

    JNI Merci pour ton commentaire, concernant la différence entre les user stories et les cas d’utilisation, le meilleur livre pour répondre a tes questions c’est User Stories Applied. (http://www.amazon.com/User-Stories-Applied-Software-Development/dp/0321205685). Mike Cohn y aborde ce sujet!
    Sinon pour les cas etendus, effectivement, ce ne sont pas des “nice to have” mais bien des cas importants dans le système. le mot “extends” en UML ne veut pas dire du tout que c’est optionnel, mais plutôt que la fonctionnalité est une fonctionnalité qui étend une autre.

  7. Comme l’explication est plutôt longue, je vais écrire un bille de blogue sur le sujet et le publier sur developpementagile.com .

  8. admin dit :

    Merci Felix-Antoine! Très bonne explication! Merci d’expliquer la différence sur l’accent surtout, c’est cette partie que j’essayais de voir

  1. 31 juillet 2014

    […] nous avons vu dans mon dernier Post, nous avons utilisé ce principe pour découpler le mécanisme de livraison du Module Principal […]

  2. 5 août 2014

    […] et d’autre de ces limites est bien différent. Des exemples de limites sont par exemple la limite entre le module definissant les objets du domaine et le module du mecanisme de livraison, ou encore la limite entre les vues et les modèles dans un module […]

  3. 11 août 2014

    […] notre dernier Post sur l’Architecture basée sur les cas d’utilisation, nous nous sommes intéressés a la séparation du mécanisme de livraison et  nous avons montré […]

Laisser un commentaire