web-dev-qa-db-fra.com

Quelle est la place de la «couche logique métier» dans une application MVC?

Tout d'abord, avant que quelqu'un ne crie dupe, j'ai eu du mal à le résumer dans un titre simple. Un autre titre aurait pu être "Quelle est la différence entre un modèle de domaine et un modèle MVC?" ou "Qu'est-ce qu'un modèle?"

Conceptuellement, je comprends qu'un modèle est les données utilisées par les vues et le contrôleur. Au-delà de cela, il semble y avoir beaucoup d'opinions divergentes sur ce qui compose le modèle. Qu'est-ce qu'un modèle de domaine, par rapport à un modèle d'application, par rapport à un modèle de vue, par rapport à un modèle de service, etc.

Par exemple, dans une question récente que j'ai posée sur le modèle de référentiel, on m'a dit à blanc que le référentiel fait partie du modèle. Cependant, j'ai lu d'autres opinions selon lesquelles le modèle devrait être séparé du modèle de persistance et de la couche de logique métier. Après tout, le modèle de référentiel n'est-il pas censé dissocier la méthode de persistance concrète du modèle? D'autres personnes disent qu'il existe une différence entre le modèle de domaine et le modèle MVC.

Prenons un exemple simple. AccountController inclus avec le projet MVC par défaut. J'ai lu plusieurs opinions selon lesquelles le code de compte inclus est de mauvaise conception, viole SRP, etc. etc. Si l'on devait concevoir un "bon" modèle d'adhésion pour une application MVC, quel serait-il?

Comment sépareriez-vous les services ASP.NET (fournisseur d'adhésion, fournisseur de rôles, etc.) du modèle? Ou le feriez-vous?

De mon point de vue, le modèle devrait être "pur", peut-être avec une logique de validation .. mais devrait être séparé des règles métier (autres que la validation). Par exemple, supposons que vous ayez une règle d'entreprise qui stipule que quelqu'un doit recevoir un e-mail lors de la création d'un nouveau compte. À mon avis, cela n'appartient pas vraiment au modèle. Alors, où appartient-il?

Quelqu'un veut-il faire la lumière sur cette question?

85
Erik Funkenbusch

La façon dont je l'ai fait - et je ne dis pas que c'est bien ou mal, c'est d'avoir ma vue, puis un modèle qui s'applique à ma vue. Ce modèle n'a que ce qui est pertinent à mon avis - y compris les annotations de données et les règles de validation. Le contrôleur héberge uniquement la logique de construction du modèle. J'ai une couche de service qui héberge toute la logique métier. Mes contrôleurs appellent ma couche de service. Au-delà, c'est ma couche de référentiel.

Mes objets de domaine sont hébergés séparément (dans leur propre projet, en fait). Ils ont leurs propres annotations de données et règles de validation. Mon référentiel valide les objets de mon domaine avant de les enregistrer dans la base de données. Parce que chaque objet de mon domaine hérite d'une classe de base avec validation intégrée, mon référentiel est générique et valide tout (et requiert qu'il hérite de la classe de base).

Vous pourriez penser qu'avoir deux ensembles de modèles est une duplication de code, et c'est dans une certaine mesure. Mais, il existe des cas parfaitement raisonnables où l'objet de domaine n'est pas approprié pour la vue.

Par exemple, lorsque je travaille avec des cartes de crédit - je dois exiger un cvv lors du traitement d'un paiement, mais je ne peux pas stocker le cvv (c'est une amende de 50 000 $ pour le faire). Mais, je veux aussi que vous puissiez modifier votre carte de crédit - changement d'adresse, de nom ou de date d'expiration. Mais vous n'allez pas me donner le numéro ou le cvv lors de la modification, et je ne vais certainement pas mettre votre numéro de carte de crédit en texte brut sur la page. Mon domaine a ces valeurs requises pour enregistrer une nouvelle carte de crédit parce que vous me les donnez, mais mon modèle d'édition n'inclut même pas le numéro de carte ou le cvv.

Un autre avantage de tant de couches est que si elles sont correctement architecturées, vous pouvez utiliser Structuremap ou un autre conteneur IoC et échanger des pièces sans nuire à votre application.

À mon avis, le code du contrôleur ne devrait être que du code ciblé sur la vue. Affichez-le, masquez-le, etc. La couche de service doit contenir la logique métier de votre application. J'aime avoir tout cela en un seul endroit, il est donc facile de changer ou de modifier une règle commerciale. La couche de référentiel doit être relativement stupide - dépourvue de logique métier et ne demander que vos données et renvoyer vos objets de domaine. En séparant les modèles de vue du modèle de domaine, vous avez beaucoup plus de flexibilité en ce qui concerne les règles de validation personnalisées. Cela signifie également que vous n'avez pas besoin de vider chaque élément de données dans votre vue dans des champs cachés et de le pousser d'avant en arrière entre le client et le serveur (ou de le reconstruire sur le backend). Votre modèle de vue ne contiendra alors que les informations pertinentes pour la vue - et il peut être personnalisé pour avoir des bools pour la logique de la vue ou des décomptes ou des énumérations afin que la vue elle-même ne soit pas encombrée de déclarations logiques compliquées comme

<% if (!String.IsNullOrEmpty(Model.SomeObject.SomeProperty) && 
    Model.SomeObject.SomeInt == 3 && ...) { %>

Alors que tout semble étalé et superposé, il a un but d'être architecturé de cette façon. C'est parfait? pas vraiment. Mais je le préfère à certaines conceptions passées d'appels de référentiels à partir du contrôleur et de mélange de logique métier dans le contrôleur, le référentiel et le modèle.

69
Josh

Je me suis trop souvent demandé comment les éléments MVC s'intégraient exactement dans une structure d'application Web traditionnelle, où vous avez des vues (pages), des contrôleurs, des services et des objets de données (modèle). Comme vous l'avez dit, il en existe de nombreuses versions.

Je crois que la confusion existe en raison de l'architecture susmentionnée et largement acceptée, qui utilise le modèle "présumé" -anti du "modèle de domaine anémique". Je n'entrerai pas dans beaucoup de détails sur "l'anti-patternness" du modèle de données anémiques (vous pouvez regarder un de mes efforts pour expliquer les choses ici (basé sur Java, mais pertinent pour n'importe quel langage) ). Mais en bref, cela signifie que notre modèle ne contient que des données et que la logique métier est placée dans les services/gestionnaires.

Mais supposons que nous ayons architecture pilotée par domaine , et nos objets de domaine sont tels qu'ils sont censés être - ayant à la fois une logique d'état et une logique métier. Et dans cette perspective de domaine, les choses se mettent en place:

  • la vue est l'interface utilisateur
  • le contrôleur rassemble les entrées de l'interface utilisateur, invoque des méthodes sur le modèle et renvoie une réponse à l'interface utilisateur
  • le modèle est nos composants métier - contenant les données, mais aussi ayant une logique métier.

Je suppose que cela répond à vos principales questions. Les choses se compliquent lorsque nous ajoutons quelques couches supplémentaires, comme la couche de référentiel. Il est souvent suggéré qu'il devrait être invoqué par la logique métier placée dans le modèle (et donc chaque objet de domaine a une référence à un référentiel). Dans mon article que j'ai lié, je soutiens que ce n'est pas tout à fait une bonne pratique. Et qu'en fait, ce n'est pas une mauvaise chose d'avoir une couche de service. Soit dit en passant, la conception pilotée par domaine n'exclut pas la couche de service, mais elle est censée être "fine" et ne coordonner que les objets de domaine (donc pas de logique métier là-bas).

Pour le paradigme du modèle de données anémiques, qui est largement adopté (pour le meilleur ou pour le pire), le modèle serait à la fois la couche de service et vos objets de données.

17
Bozho

À mon avis,

Modèle -

Ne doit pas contenir de logique métier, il doit être enfichable (scénario de type WCF). Il est utilisé pour se lier à la vue, il doit donc avoir des propriétés.

Logique d'entreprise -

Il doit être placé dans "Domain Services Layer", il s'agit d'une couche distincte. Ajoutera également une couche supplémentaire ici "Application Services".

App Services parle à la couche Services de domaine pour appliquer la logique métier, puis renvoie enfin le modèle.

Ainsi, le contrôleur demandera le service d'application pour le modèle et le flux ira comme,

    Controller->Application Services(using domain services)->Model
3
paragy

Le modèle MVC et le cadre Asp.net ne font aucune distinction sur ce que devrait être le modèle.

Les propres exemples de MS incluent les classes de persistance dans le modèle. Votre question sur l'adhésion au modèle. Cela dépend. Les classes de votre modèle appartiennent-elles à quelque chose? Existe-t-il un lien entre qui se connecte et quelles données sont affichées? Le filtrage des données fait-il partie d'un système d'autorisations modifiable? La dernière personne qui a mis à jour ou modifié un objet fait-elle partie de votre domaine, car quelqu'un d'autre doit le voir ou quelque chose pour la prise en charge du backend?

L'exemple d'email est aussi cela dépend. Connaissez-vous le domaine événementiel ou le domaine événementiel en particulier? Avez-vous un service distinct pour envoyer des e-mails? L'envoi d'un e-mail fait-il partie de votre domaine ou s'agit-il d'une préoccupation au niveau de l'application en dehors de la portée de votre système? L'interface utilisateur doit-elle savoir si un e-mail a été envoyé avec succès ou non? Les e-mails qui ne parviennent pas à envoyer ont-ils besoin de nouvelles tentatives? Le contenu de l'e-mail envoyé doit-il être stocké pour les besoins d'assistance ou de service client?

Ces types de questions sont trop larges et subjectifs, mais je réponds pour que vous et tous ceux qui vous ont voté puissent comprendre cela.

Vos exigences/délais/ressources sont tous intégrés à l'architecture de votre système. Même le modèle de reven peut avoir un effet. Vous devez également tenir compte du motif que vous photographiez. DDD est très différent des applications de persistance en tant que modèle et toute la pente entre les deux est également valide pour certaines applications. Tirez-vous pour tester l'application? Tout cela a un effet.

2
John Farrell