Situation
Plus tôt dans la soirée, j'ai répondu à une question sur StackOverflow.
La question:
La modification d'un objet existant doit être effectuée dans la couche référentiel ou dans le service?
Par exemple, si j'ai un utilisateur qui a des dettes. Je veux changer sa dette. Dois-je le faire dans UserRepository ou dans le service par exemple BuyingService en récupérant un objet, en le modifiant et en le sauvegardant?
Ma réponse:
Vous devez laisser la responsabilité de la mutation d'un objet sur ce même objet et utiliser le référentiel pour récupérer cet objet.
Exemple de situation:
class User {
private int debt; // debt in cents
private string name;
// getters
public void makePayment(int cents){
debt -= cents;
}
}
class UserRepository {
public User GetUserByName(string name){
// Get appropriate user from database
}
}
Un commentaire que j'ai reçu:
La logique métier doit vraiment être dans un service. Pas dans un modèle.
Que dit Internet?
Cela m'a donc incité à chercher, car je n'ai jamais vraiment (consciemment) utilisé une couche de service. J'ai commencé à lire sur le modèle de couche de service et le modèle d'unité de travail, mais jusqu'à présent, je ne peux pas dire que je suis convaincu qu'une couche de service doit être utilisée.
Prenons par exemple cet article de Martin Fowler sur l'anti-pattern d'un modèle de domaine anémique:
Il existe des objets, dont beaucoup portent le nom des noms de l'espace de domaine, et ces objets sont liés aux relations riches et à la structure des vrais modèles de domaine. Le problème vient lorsque vous observez le comportement et vous vous rendez compte qu'il n'y a pratiquement aucun comportement sur ces objets, ce qui en fait un peu plus que des sacs d'accesseurs et de passeurs. En effet, ces modèles sont souvent accompagnés de règles de conception stipulant que vous ne devez mettre aucune logique de domaine dans les objets de domaine. Au lieu de cela, il existe un ensemble d'objets de service qui capturent toute la logique du domaine. Ces services vivent sur le modèle de domaine et utilisent le modèle de domaine pour les données.
(...) La logique qui devrait figurer dans un objet de domaine est la logique de domaine - validations, calculs, règles de gestion - comme vous voulez l'appeler.
Pour moi, cela semblait correspondre exactement à la situation: je préconisais la manipulation des données d'un objet en introduisant des méthodes à l'intérieur de cette classe. Cependant, je me rends compte que cela devrait être une donnée dans les deux sens, et cela a probablement plus à voir avec la façon dont ces méthodes sont appelées (en utilisant un référentiel).
J'ai également eu le sentiment que, dans cet article (voir ci-dessous), une couche de services est davantage considérée comme une façade qui délègue le travail au modèle sous-jacent, plutôt qu'une couche à forte intensité de travail.
Couche d'application [son nom pour la couche de service]: définit les tâches que le logiciel est censé effectuer et demande aux objets du domaine expressif de résoudre les problèmes. Les tâches dont cette couche est responsable ont une signification pour l'entreprise ou sont nécessaires pour une interaction avec les couches d'application d'autres systèmes. Cette couche est mince. Il ne contient pas de règles commerciales ni de connaissances, mais coordonne uniquement les tâches et délègue le travail aux collaborations d'objets de domaine dans la couche suivante. Son état ne reflète pas la situation de l'entreprise, mais il peut en être de même pour la progression d'une tâche pour l'utilisateur ou le programme.
Ce qui est renforcé ici :
Interfaces de service. Les services exposent une interface de service à laquelle tous les messages entrants sont envoyés. Vous pouvez considérer une interface de service comme une façade qui expose la logique métier implémentée dans l'application (généralement la logique de la couche de gestion) aux consommateurs potentiels.
Et ici :
La couche de service doit être dépourvue de toute logique applicative ou commerciale et se concentrer principalement sur quelques préoccupations. Il doit encapsuler les appels de la couche de gestion, traduire votre domaine dans un langage commun que vos clients peuvent comprendre et gérer le support de communication entre le serveur et le client demandeur.
C'est un contraste sérieux avec d' autres ressources qui parlent de la couche de service:
La couche service doit être composée de classes avec des méthodes qui sont des unités de travail avec des actions appartenant à la même transaction.
Ou la deuxième réponse à une question que j'ai déjà liée:
À un moment donné, votre application voudra une logique métier. En outre, vous pouvez valider l’entrée pour vous assurer qu’il n’ya rien de mal ou de non performant qui est demandé. Cette logique appartient à votre couche de service.
"Solution"?
En suivant les directives de cette réponse , j'ai proposé l'approche suivante qui utilise une couche de service:
class UserController : Controller {
private UserService _userService;
public UserController(UserService userService){
_userService = userService;
}
public ActionResult MakeHimPay(string username, int amount) {
_userService.MakeHimPay(username, amount);
return RedirectToAction("ShowUserOverview");
}
public ActionResult ShowUserOverview() {
return View();
}
}
class UserService {
private IUserRepository _userRepository;
public UserService(IUserRepository userRepository) {
_userRepository = userRepository;
}
public void MakeHimPay(username, amount) {
_userRepository.GetUserByName(username).makePayment(amount);
}
}
class UserRepository {
public User GetUserByName(string name){
// Get appropriate user from database
}
}
class User {
private int debt; // debt in cents
private string name;
// getters
public void makePayment(int cents){
debt -= cents;
}
}
Conclusion
Dans l'ensemble, peu de choses ont changé ici: le code du contrôleur a été transféré à la couche service (ce qui est une bonne chose, donc cette approche présente des avantages.) Cependant, cela ne semble pas avoir un rapport avec ma réponse initiale.
Je me rends compte que les modèles de conception sont des directives et non des règles immuables à appliquer autant que possible. Pourtant, je n'ai pas trouvé d'explication définitive sur la couche de service et sur la manière dont elle devrait être considérée.
Est-ce un moyen d'extraire simplement la logique du contrôleur et de la placer dans un service?
Est-il censé former un contrat entre le contrôleur et le domaine?
Devrait-il y avoir une couche entre le domaine et le service?
Et, dernier point mais non le moindre: suivre le commentaire original
La logique métier doit vraiment être dans un service. Pas dans un modèle.
Est-ce correct?
- Comment pourrais-je introduire ma logique métier dans un service au lieu du modèle?