web-dev-qa-db-fra.com

Une couche de service doit-elle renvoyer des modèles de vue pour une application MVC?

Supposons que vous ayez un projet ASP.NET MVC et que vous utilisez une couche de service, comme dans ce didacticiel du gestionnaire de contacts sur le site asp.net: http://www.asp.net/mvc/tutorials/iteration- 4-make-the-application-vaguement couplé-cs

Si vous avez des modèles de vue pour vos vues, la couche de service est-elle l'endroit approprié pour fournir chaque modèle de vue? Par exemple, dans l'exemple de code de couche de service, il existe une méthode

    public IEnumerable<Contact> ListContacts()
    {
        return _repository.ListContacts();
    }

Si à la place vous vouliez un IEnumerable, devrait-il aller dans la couche service, ou y a-t-il un autre endroit qui est le "bon" endroit?

Peut-être plus approprié, si vous avez un modèle de vue distinct pour chaque vue associée à ContactController, ContactManagerService devrait-il avoir une méthode distincte pour renvoyer chaque modèle de vue? Si la couche de service n'est pas au bon endroit, où les objets viewmodel doivent-ils être initialisés pour être utilisés par le contrôleur?

62
erg39

En général, non.

Les modèles de vue sont destinés à fournir des informations vers et depuis les vues et doivent être spécifiques à l'application, par opposition au domaine général. Les contrôleurs devraient orchestrer l'interaction avec les référentiels, les services (je fais ici quelques hypothèses sur la définition du service), etc. et gérer la construction et la validation des modèles de vue, et contenir également la logique de détermination des vues à rendre.

En divisant les modèles de vue en une couche "service", vous brouillez vos couches et vous avez maintenant une application et une présentation spécifiques mélangées avec ce qui devrait être axé sur les responsabilités au niveau du domaine.

48
captaintom

Non je ne pense pas. Les services ne doivent s'intéresser qu'au domaine problématique, pas à la vue qui donne des résultats. Les valeurs de retour doivent être exprimées en termes d'objets de domaine, pas de vues.

25
duffymo

Selon l'approche traditionnelle ou la théorie, ViewModel devrait faire partie de la couche d'interface utilisateur. Au moins, le nom le dit.

Mais lorsque vous commencez à l'implémenter vous-même avec Entity Framework, MVC, Repository, etc., vous réalisez autre chose.

Quelqu'un doit mapper les modèles d'entité/DB avec ViewModels (DTO mentionné à la fin). Doit-on le faire dans [A] la couche UI (par le contrôleur), ou dans [B] la couche Service?

Je choisis l'option B. L'option A est non non car le simple fait que plusieurs modèles d'entité se combinent pour former un ViewModel. Nous ne pouvons pas transmettre des données inutiles à la couche d'interface utilisateur, alors que dans l'option B, le service peut jouer avec les données et transmettre uniquement le minimum requis à la couche d'interface utilisateur après le mappage (au ViewModel).

Encore une fois, allons-y avec l'option A, mettons ViewModel dans la couche UI (et le modèle d'entité dans la couche Service).

Si la couche Service doit être mappée au ViewModel, la couche Service doit accéder à ViewModel dans la couche UI. Quelle bibliothèque/projet? Le Viewmodel doit être dans un projet distinct dans la couche UI, et ce projet doit être référencé par la couche Service. Si le ViewModel n'est pas dans un projet séparé, il y a une référence circulaire, donc ne partez pas. Il semble gênant que la couche Service accède à la couche UI, mais nous pouvons toujours y faire face.

Mais que se passe-t-il s'il existe une autre application d'interface utilisateur utilisant ce service? Et s'il y a une application mobile? Dans quelle mesure le ViewModel peut-il être différent? Le service doit-il accéder au même projet de modèle de vue? Tous les projets d'interface utilisateur auront-ils accès au même projet ViewModel ou ont-ils le leur?

Après ces considérations, ma réponse serait de mettre le projet Viewmodel dans Service Layer. Chaque couche d'interface utilisateur doit accéder à la couche Service de toute façon! Et il pourrait y avoir beaucoup de ViewModels similaires qu'ils pourraient tous utiliser (donc le mappage devient plus facile pour la couche de service). Les mappages sont effectués via linq ces jours-ci, ce qui est un autre avantage.

Enfin, il y a cette discussion sur le DTO. Et aussi sur l'annotation des données dans ViewModels. Les ViewModels avec des annotations de données (Microsoft.Web.Mvc.DataAnnotations.dll) ne peuvent pas résider dans la couche service à la place ils résident dans la couche UI (mais ComponentModel.DataAnnotations.dll peut résider dans la couche service). Si tous les projets sont dans une seule solution (.sln), peu importe la couche que vous lui avez mise. Dans les applications d'entreprise, chaque couche aura sa propre solution.

Donc, DTO est en fait un ViewModel car la plupart du temps, il y aura un mappage un à un entre les deux (par exemple avec AutoMapper). Encore une fois, le DTO a toujours la logique nécessaire pour l'interface utilisateur (ou plusieurs applications) et réside dans la couche de service. Et la couche UI ViewModel (si nous utilisons Microsoft.Web.Mvc.DataAnnotations.dll) est juste pour copier les données de DTO, avec un certain 'comportement'/attributs ajoutés.

[Maintenant, cette discussion est sur le point de prendre une tournure intéressante à lire ...: I]

Et ne pensez pas que les attributs d'annotation de données sont uniquement destinés à l'interface utilisateur. Si vous limitez la validation à l'aide de System.ComponentModel.DataAnnotations.dll, le même ViewModel peut également être utilisé pour la validation frontale et backend (supprimant ainsi l'interface utilisateur-ViewModel-copy-of-DTO). De plus, les attributs peuvent également être utilisés dans les modèles d'entité. Par exemple: à l'aide de .tt, les modèles de données Entity Framework peuvent être générés automatiquement avec des attributs de validation pour effectuer des validations de base de données telles que la longueur maximale avant d'être envoyées au serveur principal. Un autre avantage est que si la validation du backend change dans la base de données, alors .tt (lit les spécificités de la base de données et crée l'attribut pour la classe d'entité) le récupérera automatiquement. Cela peut également forcer les tests unitaires de validation de l'interface utilisateur à échouer, ce qui est un gros plus (nous pouvons donc le corriger et informer toutes les interfaces utilisateur/consommateurs au lieu d'oublier et d'échouer accidentellement). Oui, la discussion s'oriente vers une bonne conception du cadre. Comme vous pouvez le voir, tout est lié: validation par niveau, stratégie de test unitaire, stratégie de mise en cache, etc.

Bien que n'étant pas directement lié à la question. 'ViewModel Façade' mentionné dans ce doit regarder lien du canal 9 vaut également la peine d'être exploré. Cela commence exactement à 11 minutes 49 secondes dans la vidéo. Parce que ce serait la prochaine étape/pensée une fois que votre question actuelle donnée ci-dessus est triée: 'Comment organiser ViewModels?'

Dans votre exemple, "_repository.ListContacts ()" renvoie également un ViewModel à partir du référentiel. Ce n'est pas une façon mature. Les référentiels doivent fournir des modèles d'entité ou des modèles de base de données. Celui-ci est converti en modèles de vue et c'est ce modèle de vue qui est renvoyé par la couche de service.

21
Blue Clouds

Je suppose que cela dépend de ce que vous considérez comme les "services". Je n'ai jamais vraiment aimé le terme service dans le contexte d'une seule classe; c'est incroyablement vague et ne vous dit pas grand-chose sur le but réel de la classe.

Si la "couche de service" est une couche physique, comme un service Web, alors absolument pas; services dans un contexte SOA devrait exposer les opérations de domaine/métier, pas les données et non la logique de présentation. Mais si service est juste utilisé comme concept abstrait pour un niveau supérieur d'encapsulation, je ne vois aucun problème à l'utiliser comme vous le décrivez.

Ne mélangez pas les concepts. Si votre service traite des modèles de vue, il doit s'agir d'un service de présentation et être superposé au-dessus du modèle réel, sans jamais toucher directement la base de données ou toute logique métier.

5
Aaronaught

Cela est venu un peu "ça dépend" où je travaille - nous avons généralement eu un contrôleur consommant certains services - puis combinant les DTO retournés dans un "ViewModel" qui serait ensuite transmis au client - soit via le résultat JSON ou lié dans le modèle de rasoir.

La chose est, environ 80% du temps - le mappage de DTO à ViewModel a été 1-1. Nous commençons à nous diriger vers `` Là où cela est nécessaire, consommez directement le DTO, mais lorsque le DTO et ce dont nous avons besoin dans notre client/vue ne correspondent pas - alors nous créons un ViewModel et faisons le mappage entre les objets selon les besoins ''.

Bien que je ne sois toujours pas convaincu que ce soit la meilleure ou la bonne solution - car cela finit par conduire à des discussions animées sur `` ajoutons-nous simplement X au DTO pour répondre aux besoins de la vue? ''

5
e82