mardi 24 juillet 2012

RemiDDD Partie II : Aggregate


Lors de la conception d'un projet en utilisant une méthode DDD, on va faire appel à la notion de Aggregate.

Un aggregate est un entité métier de base. Cette entité peut être composée d'autres entités appelées "value object" qui n'ont aucune signification sans leur entité maitre et dont la durée de vie est la même.
Exemple : Un client à une adresse, dans le cadre d'un CRM l'adresse ne veut rien dire sans le client, quand on créera/supprimera un client on créera/supprimera son adresse.

Les Aggregate/value object doivent répondre à ces exigences :
  • Seuls les Aggregates ont un Id
  • Seuls les Aggregates sont filtrables / requêtables (on ne peut obtenir l'Adresse d'un Client sans avoir son Id).
  • Les value object ne peuvent pas référencer d'autres value object, que des Aggregate (l'adresse d'un client pourra référencer un Pays).

Il faut faire attention à ne pas faire des Aggregate sur-dimensionnés embarquants la moitié de la base de donnée.
Prenons l'exemple d'une Commande, on pourrait très bien penser que c'est un value object de Client vu qu'une Commande ne veux rien dire sans le Client. Mais le fait est que dans de nombreuses interfaces on va vouloir éditer les informations de la commande, les filtrer, appliquer une offre spéciale, l'annuler ... On va donc créér un Aggregate "Commande" qui va avoir une référence vers l'Aggregate "Client", les cas d'utilisation ont forcé cette promotion.


Le design des Aggregate se base sur ces cas d'utilisations (d’où leur utilisation en DDD). Lors de leur conception il faut se demander "Est ce que l'on aura besoin de le référencer tout seul quelque part ?" , "Est ce qu'une interface permettra de le mettre à jour ?" ...

Si vous voulez en savoir plus je vous incite à lire les documents écris par Vaughn Vernon : http://dddcommunity.org/library/vernon_2011

RemiDDD, part I

Aujourd'hui je lance mon "framework" de développement qui s'appelle RemiDDD (original).

J'ai récemment lu pas mal d'articles sur le Domain Driven Design (aka DDD), le Command-Query Responsibility Segregation (aka CQRS). Et ça a bien modifié ma manière de voir le développement d'applications métier .

Je vais donc essayer de décrire ces deux notions (ou plutôt d'en donner ma description qui n'est certainement pas la meilleure).

DDD est une manière de concevoir son application guidé par le métier (Domain).

Petit exemple d'un projet dans une démarche classique :
- Le client va demander "Lors du click sur ce bouton l'internaute va passer sa commande", le technicien créer la fonctionnalité "créer une commmande".
- Le client veut recevoir un mail lorsque le client passe une commande. Le développeur va donc envoyer un mail dès qu'on crée une commande.
- Ensuite le client veut pouvoir ajouter dans la base une commande faite par téléphone. Mais le problème est ce du coup le client va recevoir le mail de commande alors qu'il vient de la rentrer lui même. Donc le développeur va mettre un "if(OrderNotFromWeb)". 2 ans plus tard e développeur referra entièrement la fonctionnalité CreateOrder car il a 200 if a gérer et il en oubliera certainement 30 en réécrivant son code.

La complexité d'un modèle (qui est le reflet du domaine dans le logiciel) est souvent du au mélange des rôles. Par exemple ici la fonction "CreateOrder" sera implicitement responsable de "PassOrder" parce que au début cela semblait évident mais lorsque l'on sépare ces deux notions, c'est la catastrophe.
Le soucis ici est que les développeur et le client ne parle pas le même langage. DDD consiste a établir un langage commun (le "ubiquitous language") sur lequel va se baser leur travail. Ce langage commun est utilisé dans tout les projets (DDD ou non ) mais dans un projet DDD il va être le centre de toutes les attentions et si c'est correctement mis en place, un client pourra dire exactement ce que fait la fonction "CreateOrder" : créer une commande, pas réduire le stock, pas mailer les commerçants, pas vérifier le stock.

Pour permettre la création d'un modèle solide , il va falloir se baser sur plusieurs notions :
  • Aggregate Roots, Value Object qui sont le cœur du modèle, ils ne vont pas être uniquement des DTO mais aussi avoir un vrai comportement (la loi de Demeter est un bon point de départ pour savoir si l'ont doit créer une méthode dans un objet ou le manipuler directement dans la méthode courante)  :
    • Aggregate : un objet identifié, c'est donc sur lui que l'on va faire des actions du type mise à jour, suppression, insertion (exemple : Order)
    • ValueObject : une valeur non identifié qui compose un aggregate, il peut référencer un autre aggregate, mais il ne peut pas référencer un autre valueobject.(exemple : OrderLine)
  • Comand - Query Separation : Deux modèles un modèle en écriture et un modèle en lecture. 
    • Command : modifie l'état du modèle, ne retourne rien (void)
    • Query : retourne une partie de l'état du modèle
  • Comande/Query Handler qui executeront les command/query. Cette séparation ne me semble pas essentielle mais permettra de regrouper dans un même handler des commandes/query proche. Les Command/Query ne servent en fait que a décrire l'action à faire ou la requête à envoyer, les handler feront le travail a proprement dit (comunnication BDD, appel méthodes aggregates ...)
  • Les évènements/observateurs : Ils permettront d'appliquer les demandes du client commençant par "quand vous faites ça faites ...", tout en n'empiétant pas sur les process déjà existant (et debuggé). Par exemple sur le cas du mail client on aura
    • PassOrderCommand qui fera le processus de passer une commande
    • OrderPassedEvent qui sera levé a la fin du process de la commande avec les informations de la commande (prix, coord client ...)
    • SendClientMail qui observera le OrderPassedEvent qui enverra le mail au client pour confirmer sa commande (et qui peut etre appelé directement pour réexpédier le mail).
    • SendSellerMail qui observera le OrderPassedEventqui enverra le mail au vendeur pour lui signifier la commande (on peut imaginer un CrmConnection qui communiquera la vente au CRM du client ...)
  • Views : qui seront l'interface entre l'utilisateur et le client. Qui déclenche des actions sur le controller.
  • Controller qui traite les demande de l'utilisateur, les valides, et crée en conséquence les commande/query nécessaires.

 Ces classes nécessitent un peu d'infrastructure (par exemple pour lever un évènement, gérer une commande ...)  :
  • Pour les Vue / Controller des framework existent et sont très bien fait (dans les exemples j'utiliserai principalement Asp.Net MVC 3)
  • Pour les évènements / observateur , command/commandhandler, query/queryhandler, je développerai moi-même l'infrastructure en me basant sur la Reflection et sur Ninject, les quelques framework que j'ai croisé me semblent trop complexes alors que le dev me semble minime (la meilleure raison pour faire quelque chose soit même)
  • Idem pour les Aggregate/ Value Object. La persistance sera géré via RavenDB Embedded car assez mur, performant, simple d'utilisation et requétable via du Linq (je déteste les ORM qui remplace l'apprentissage du SQL par l'apprentissage d'un autre language type HQL).
L'exemple sera certainement un jeux de rôles le plus simple possible au niveau des règles mais le plus large possible au niveaux fonctionnalités.

GitHub : https://github.com/RemiBou/RemiDDD