DDD et internal

Julien on nov 21st 2008

Je poursuis mes réflexions sur l'impact que le domain driven design a sur ma façon de développer... :-)

Jusqu'à très récemment, le nombre de fois ou j'ai utilisé le mot clef internal devait se compter sur les doigts des 2 mains. La faute au fait que je n'ai que rarement développé des API consommés par l'extérieur? Ou peut être tout simplement un gros défaut dans mon style de développement :-)!
Quoiqu'il en soit, j'ai remarqué que j'avais fortement évolué depuis que je m'intéresse au domain driven design. Je vais reprendre l'exemple qui me tient à cœur en ce moment, a savoir le cas d'un panier sur un site de e-commerce !

J'ai donc 3 classes. ShoppingCart, Product et ShoppingCartItem :

  1.  
  2. public class ShoppingCart
  3. {
  4. private int _id;
  5. private readonly IList<ShoppingCartItem> _items;
  6.  
  7. public ShoppingCart()
  8. {
  9. _items = new List<ShoppingCartItem>();
  10. }
  11.  
  12. // + les getters
  13. }
  14.  
  15. public class Product
  16. {
  17. private int _id;
  18. private readonly string _name;
  19.  
  20. public Product(string name)
  21. {
  22. _name = name;
  23. }
  24.  
  25. // + les getters
  26. }
  27.  
  28. public class ShoppingCartItem
  29. {
  30. private readonly Product _product
  31. private readonly ShoppingCart _cart;
  32. private int _quantity;
  33.  
  34. public ShoppingCartItem(ShoppingCart cart, Product product)
  35. {
  36. _cart = cart;
  37. _product = product
  38. }
  39.  
  40. // + les getters
  41. }
  42.  

La modélisation est simple et à priori valide. Si je veux ajouter un Product à mon ShoppingCart, j'écrirai le code suivant :

  1.  
  2. var myCart = new ShoppingCart();
  3. var myProduct = new Product();
  4.  
  5. myCart.Items.Add(new ShoppingCartItem(myCart, myProduct));
  6.  

SSSSSTTTTOOOOOOPPPPPPP !!
Certes, ce code est valide mais je viole plusieurs principes (par exemple la loi de Déméter) et myCart.Items.Add(new ShoppingCartItem(myCart, myProduct)); n'a pas beaucoup de sens au niveau du modèle. En effet, si on se met à la place d'un client d'Amazon, on va ajouter un produit à son panier (un dvd ou autre) et non pas autre chose. Il serait donc beaucoup plus logique d'avoir le code suivant :

  1.  
  2. var myCart = new ShoppingCart();
  3. var myProduct = new Product();
  4.  
  5. myCart.AddProduct(myProduct);
  6.  

Une implémentation de AddProduct pourrait ressembler à cela :

  1.  
  2. public ShoppingCartItem AddProduct(Product product)
  3. {
  4. ShoppingCartItem item = _items.SingleOrDefault(x => x.Product == product);
  5.  
  6. if(item == null) {
  7. item = new ShoppingCartItem(this, product);
  8.  
  9. _items.Add(item);
  10. } else {
  11. item.Quantity += 1;
  12. }
  13.  
  14. return item;
  15. }
  16.  

Note : la collection Items deviendrait évidemment une ReadOnlyCollection !

Pour revenir au sujet de cet article, quel est le rapport avec le mot clef internal?

Dans ce contexte précis (et c'est évidemment très dépendant de votre domaine), la classe ShoppingCartItem ne devrait jamais être instancié directement en dehors du modèle, cela n'a pas de sens. J'irai même plus loin : seules la classe ShoppingCart et la couche de persistance ne devrait pouvoir l'instancier. A défaut de mieux, on devra donc modifier l'accessibilité du constructeur de ShoppingCartItem à internal afin d'éviter que les consommateurs du domaine n'en fasse mauvais usage.

Filed in Domain Driven Design | 2 responses so far

2 Responses to “DDD et internal”

  1. Gauthier Segay déc 1st 2008 at 08:53 1

    juste pour info,

    si tu n’as pas pour objectif de tester unitairement la classe ShoppingCartItem (car c’est un détail d’implémentation sur lequel il n’est peut-être pas nécéssaire d’exercer de test) et qu’elle est uniquement utilisée par ShoppingCart, tu peux laisser la classe privée en la définissant à l’intérieur de la classe ShoppingCart

  2. Julien déc 2nd 2008 at 09:01 2

    Remarque judicieuse :-)
    Je n’utilise que rarement les classes privées, mais c’est un cas de figure ou j’aurai pu m’en servir. J’essayerai d’y penser la prochaine fois!

Trackback URI | Comments RSS

Leave a reply