Je suis en train de construire un site de commerce électronique en utilisant C#, MVC3, Entity Framework 4. C'est la première fois que j'utilise MVC3 et Entity Framework et je veux m'assurer que l'architecture est solide. En particulier, je m'interroge sur l'utilisation que je fais des interfaces et de l'inversion de dépendances dans le cadre des couches Services et Référentiel. Je vais me concentrer sur une seule partie du système, le système de panier, pour des raisons de brièveté et de clarté.
Voici un exemple d'inversion d'interface utilisé par PluralSite pour expliquer la tendance typique à créer des interfaces sans tenir compte des dépendances appropriées.
Disons que vous avez une interface IKangaroo dont dépend une classe "BoxingMatch". Si vous ajoutez d'autres "boxeurs" comme IMikeTyson et IJoeBoxer, vous avez maintenant trois interfaces différentes, une pour chaque boxeur, que "BoxingMatch" doit connaître. IKangaroo, IMikeTyson et IJoeBoxer n'ont qu'une seule implémentation concrète chacune, ce qui signifie que vous n'avez même pas besoin de ces interfaces (vous pouvez tout aussi bien faire dépendre BoxingMatch directement des classes concrètes Kangaroo, MikeTyson et JoeBoxer). De plus, il n'est même pas logique qu'il y ait plus d'une implémentation de IKangaroo, ou IMikeTyson. Les interfaces sont donc inutiles et n'apportent aucune valeur à l'architecture.
En inversant les dépendances dans cet exemple, "BoxingMatch" définirait l'interface que les classes qu'il va utiliser (Kangourou, MikeTyson et JoeBoxer) vont mettre en œuvre. Ainsi, "BoxingMatch" dépendrait d'une interface IBoxer, et Kangaroo, MikeTyson et JoeBoxer implémenteraient tous IBoxer. Voilà l'inversion, et elle est parfaitement logique.
Maintenant, ma situation... Le constructeur de CartController a deux paramètres de dépendance injectés, ICartService et IProductService). CartService prend un seul argument de constructeur injecté (ICartRepository). CartController appelle AddItem() sur CartService, et CartService appelle AddItem() sur CartRepository.
ICartRepository a deux implémentations : CartRepository (dans le projet Web principal) et TestCartRepository (dans le projet Tests). ICartService n'a qu'une seule implémentation.
Mes questions : comment mon architecture se situe-t-elle par rapport à la leçon de l'exemple ci-dessus ? Je ne vois pas vraiment comment mon CartController pourrait être moins couplé qu'il ne l'est déjà au CartService. Le contrôleur ne dépend que de CartService, pas de ICartRepository. Il ne semble donc pas que je puisse inverser le contrôle ici en demandant au CartController de définir l'interface que CartService et ICartRepository vont utiliser, puisque CartService et CartRepository sont des couches entièrement différentes. Ai-je raison ?
Un niveau plus bas, et même question. CartService dépend de CartRepository. Le principe d'inversion ci-dessus s'appliquerait-il ici ? Ou ai-je déjà inversé la dépendance en exigeant un paramètre injecté ICartRepository dans le constructeur de CartService ?
Donc ma question est vraiment, est-ce que j'ai fait ça "correctement" ?
Toute réflexion ou tout conseil serait apprécié.
Mon code, pour référence :
CartController :
//constructor
public CartController(ICartService cartService, IProductService productService)
{
_cartService = cartService;
_productService = productService;
}
public RedirectToRouteResult AddItem(Cart cart, int productId)
{
var product = _productService.GetProduct(productId);
if (product != null)
{
_cartService.AddItem(cart, product, 1);
}
return RedirectToAction("Index");
}
CartService (implémentation) :
//constructor
public CartService(ICartRepository repository)
{
_repository = repository;
}
public void AddItem(Cart cart, Product product, int quantity)
{
//simplified for brevity
var cartProduct = _repository.CartProducts().SingleOrDefault(cp => cp.CartId == cart.CartId && cp.ProductId == product.ProductId);
_repository.AddCartItem(cartProduct);
}
Cart Repository (implémentation) :
public void AddCartItem(CartProduct cartProduct)
{
_context.CartProducts.Add(cartProduct);
_context.SaveChanges();
}