Où se situe la «couche de logique métier» dans une application MVC?

86

Premièrement, avant que quiconque crie dupe, j'ai eu du mal à le résumer dans un simple titre. 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 à bout portant que le référentiel faisait 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 Repository n'est-il pas censé découpler la méthode de persistance concrète du modèle? D'autres personnes disent qu'il existe une différence entre le modèle Domain et le modèle MVC.

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

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

À mon avis, 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, disons que vous avez une règle métier qui stipule qu'une personne doit recevoir un e-mail lors de la création d'un nouveau compte. Cela n'a pas vraiment sa place dans le modèle à mon avis. Alors, à quoi appartient-il?

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

Erik Funkenbusch
la source
1
C'est pourquoi vous devriez poser quatre questions distinctes.
John Farrell
3
Le mot-clé est "presque". C'est vraiment la même question, avec peut-être des sous-questions utilisées pour illustrer la question principale.
Erik Funkenbusch
3
Modèle Vue Contrôleur. Est reposirory / BL View? Non. Est-ce le contrôleur? Non. Que reste-t-il :)? C'est MVC, pas MSVC, pas MRVC, pas MBLVC. Il n'y a que trois couches. Le référentiel fait donc partie du modèle, BL fait partie du modèle. Et vous pouvez effectuer une séparation supplémentaire, mais cela se fait à l'intérieur du calque du modèle.
LukLed
3
@LukeLed, @bslm - Pas vraiment. MVC ne dit pas qu'il ne peut pas y avoir d'autres couches avec lesquelles le contrôleur ou le modèle interagit.
John Farrell
3
@LukLed - Pas d'accord - MVC est simplement un modèle de couche de présentation. Cela n'a aucun impact sur la façon dont vous structurez vos autres couches comme BLL et DAL.
Cory House du

Réponses:

69

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 pour mon point de vue - 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à de cela, il y a ma couche de référentiel.

Les objets de mon 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 qui a une validation intégrée, mon référentiel est générique et valide tout (et nécessite 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.

Le cas d'espèce est 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 souhaite également 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 affecter votre application.

À mon avis, le code du contrôleur ne devrait être que du code ciblé sur la vue. Montrez ceci, masquez cela, etc. La couche de service doit héberger la logique métier de votre application. J'aime avoir tout cela au même endroit, il est donc facile de changer ou d'ajuster une règle métier. La couche de référentiel doit être relativement stupide - dépourvue de logique métier et interroger uniquement 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 à vider chaque élément de données dans votre vue dans des champs cachés et à le pousser dans les deux sens entre le client et le serveur (ou à le reconstruire sur le backend).

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

Bien que tout semble étalé et superposé, il a pour but d'être architecturé de cette façon. Est-ce parfait? pas vraiment. Mais je préfère cela à certaines conceptions antérieures d'appeler des référentiels à partir du contrôleur et d'avoir une logique métier mélangée dans le contrôleur, le référentiel et le modèle.

Josh
la source
À peu près un miroir de ce que j'ai dans notre application MVC d'entreprise. Une architecture N-Tier. L'application MVC interagit uniquement avec les objets métier et les services dans les zones N-Tier.
Ed DeGagne
Surtout la même chose ici. Projets séparés pour les définitions, les modèles, les modèles de vue, DAL, etc. La seule différence est que mon DAL inclut une logique d'aplatissement des données pour le Web afin d'optimiser la distribution de données complexes pour des rapports ou des vues client personnalisées. J'évite maintenant de garder des choses dans le cache des applications pour les tables de recherche, etc., avec des fermes Web et des nuages ​​Azure en jeu.
Robert Achmann
1
@Josh, ce serait utile si vous pouviez montrer une capture d'écran de votre exemple de projet?
shaijut
@Josh et si votre projet n'a pas de base de données. Il interagit avec les références de service. Toutes les classes et méthodes de domaine proviennent de ces références. Ce scénario convient-il à une structure en couches?
user6395764
17

Je me suis trop souvent demandé comment exactement les éléments MVC s'intègrent 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 existe de nombreuses versions de cela.

Je crois que la confusion existe à cause de l'architecture énoncée ci-dessus, largement acceptée, qui utilise le modèle -anti du «modèle de domaine anémique» (prétendu). Je n'entrerai pas dans beaucoup de détails sur «l'anti-patternness» du modèle de données anémique (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 une architecture pilotée par domaine et que nos objets de domaine soient tels qu'ils sont censés être - ayant à la fois une logique d'état et une logique métier. Et dans cette perspective axée sur le domaine, les choses se mettent en place:

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

Je suppose que cela répond à vos principales questions. Les choses se compliquent lorsque nous ajoutons d'autres couches, comme la couche de référentiel. Il est souvent suggéré qu'il devrait être appelé par la logique métier placée dans le modèle (et par conséquent, 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. À propos, la conception axée sur le domaine n'exclut pas la couche de service, mais elle est supposée être «mince» et ne coordonner que les objets de domaine (donc pas de logique métier).

Pour le paradigme du modèle de données anémique, 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.

Bozho
la source
Excellent point! Une remarque: il y a le même désordre avec les services. Au moins, les services peuvent être des services d'application et des services de domaine. Le service d'application n'est qu'un mince wrapper, qui collecte des informations à partir de référentiels, etc. Le service de domaine fournit une logique métier, qui utilise une combinaison de modèles de domaine ou simplement des éléments qui ne correspondent pas toujours au modèle de domaine.
Artru
que faire si votre projet n'a pas de base de données. Il interagit avec les références de service. Toutes les classes et méthodes de domaine proviennent de ces références. Ce scénario est-il adapté à une structure en couches?
user6395764
3

À 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, donc il doit avoir des propriétés.

Logique métier -

Il doit être placé sur "Domain Services Layer", c'est une couche complètement séparée. En outre, ajoutera une couche supplémentaire ici "Services d'application".

App Services communique avec la couche des services de domaine pour appliquer la logique métier, puis renvoie enfin le modèle.

Ainsi, le contrôleur demandera au service d'application le modèle et le flux se déroulera comme suit:

    Controller->Application Services(using domain services)->Model
py2020
la source
2

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

Les propres exemples de MS incluent des 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? Y a-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 personne qui a mis à jour ou modifié un objet pour la dernière fois fait-elle partie de votre domaine, car quelqu'un d'autre doit le voir ou quelque chose pour le support backend?

L'exemple de courrier électronique est également cela dépend. Êtes-vous familier avec l'événementiel de domaine ou l'événementiel en particulier? Avez-vous un service séparé pour envoyer des e-mails? Le fait d'envoyer un e-mail fait-il partie de votre domaine ou s'agit-il d'un problème au niveau de l'application en dehors de la portée de votre système? L'interface utilisateur a-t-elle besoin de savoir si un e-mail a été envoyé avec succès ou non? Les e-mails qui ne sont pas envoyés doivent-ils être réessayés? 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 vastes et subjectives, mais je réponds pour que vous et tous ceux qui vous ont voté puissent comprendre cela.

Vos exigences / délais / ressources se retrouvent dans l'architecture de votre système. Même le modèle de revenus peut avoir un effet. Vous devez également tenir compte du motif que vous recherchez. DDD est très différent des applications de persistance en tant que modèle et tous les slop entre les deux sont également valables pour certaines applications. Tirez-vous pour tester l'application? Tout cela a un effet.

John Farrell
la source