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 :
public class ShoppingCart { private int _id; private readonly IList<ShoppingCartItem> _items; public ShoppingCart() { } // + les getters } public class Product { private int _id; private readonly string _name; public Product(string name) { _name = name; } // + les getters } public class ShoppingCartItem { private readonly Product _product private readonly ShoppingCart _cart; private int _quantity; public ShoppingCartItem(ShoppingCart cart, Product product) { _cart = cart; _product = product } // + les getters }
La modélisation est simple et à priori valide. Si je veux ajouter un Product à mon ShoppingCart, j'écrirai le code suivant :
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 :
myCart.AddProduct(myProduct);
Une implémentation de AddProduct pourrait ressembler à cela :
public ShoppingCartItem AddProduct(Product product) { ShoppingCartItem item = _items.SingleOrDefault(x => x.Product == product); if(item == null) { _items.Add(item); } else { item.Quantity += 1; } return item; }
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


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
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!