Où placer ces ViewModel dans votre site web ASP.NET MVC
Et si on tentait d'optimiser l'emplacement
Introduction
Il est courant d’utiliser des modèles de vues (ViewModel) lors de la création de sites web en ASP.NET MVC.
Concrètement, on utilise un objet intermédiaire pour communiquer avec les vues afin d’éviter de coupler la vue au modèle de la base de données.
Est-ce que vous avez déjà remis en question la manière d’organiser les fichiers afin d’être plus efficace?
Approche naïve
Comme le suggère la structure de base du template, on pourrait donc se servir du dossier Models. Pour plus de propreté, on peut créer un dossier pour chaque contrôleur. On a donc quelque chose de semblable à :
- Controller
- Controller 1
- Controller 2
- Models
- Controller 1
- (ViewModel)
- Controller 2
- (ViewModel)
- Controller 1
- Views
- Controller 1
- (Vues)
- Controller 2
- (Vues)
- Controller 1
Approche hybride
Une amélioration de la première approche est de fusionner le dossier Models et Views. Il y a moins de dossiers et une proximité entre le ViewModel et la vue. À priori, cela semble vraiment mieux!
- Controller
- Controller 1
- Controller 2
- Views
- Controller 1
- (Vues)
- (ViewModel)
- Controller 2
- (Vues)
- (ViewModel)
- Controller 1
Approche optimisée
Comme on utilise un ViewModel généralement une fois et qu’il est très couplé au code du contrôleur, j’ai trouvé une approche que je trouve un peu plus optimisée.
Lors de la maintenance, ce qui est souvent le plus important, c’est d’avoir une proximité entre le code du contrôleur et le ViewModel.
Pourquoi ne pas essayer de les approcher physiquement dans le code source? Un ViewModel est généralement utilisé par deux méthodes du controlleur. Il y a une méthode pour afficher le formulaire et une autre pour intercepter la réponse.
Prenons l’exemple d’un contrôleur qui s’occupe de gérer des produits.
Je suppose que la clé primaire (champs Id) est un entier. (Vous pourriez très bien utiliser un Guid)
Pseudo-code de notre contrôleur :
classe ProductController classe CreateViewModel Create() Create(CreateViewModel form) classe DetailsViewModel Details(int id) classe EditViewModel extends CreateViewModel Edit(int id) Edit(EditViewModel form) classe DeleteViewModel Delete(int id) Delete(DeleteViewModel form)
On voit rapidement le regroupement de 2 ou 3. Un simple défilement de la vue est suffisant pour arriver à la zone voulue. On a donc un gain de temps non négligeable.
Généralement, le formulaire d’édition ajoute très peu de propriétés. Le plus souvent, on ajoute le champ Id puisqu’il existe à présent. L’héritage peut fonctionner. En plus de réduire la taille du code source, on peut passer en paramètre une instance de EditViewModel à une fonction qui s’attend à une instance de CreateViewModel pour faire de la validation côté serveur par exemple. Cela permet donc la réutilisation.
On remarque rapidement que cette approche ne permet pas la réutilisation entre contrôleurs. C’est voulu, car je tente ici de réduire le nombre de dépendances dans le code source. La maintenance est ainsi facilitée, car il y a moins d’impact lors d’un changement. On peut même oser supprimer des formulaires et les remplacer sans trop de problèmes.
Mapping Model -> ViewModel
Lorsqu’on veut afficher la page Details, Edit ou Delete, nous avons besoin d’une instance d’un ViewModel. Vous avez plusieurs options ici.
- Vous faites le mapping manuellement à chaque fois.
- Vous déclarez une fonction qui reçoit le modèle et retourne une instance du ViewModel.
- Vous utilisez AutoMapper. Il permet de remplir les ViewModel et d’assurer une validation au démarrage de l’application.
L’avantage de la méthode 2 est la réutilisation, éviter une dépendance à AutoMapper, plus performant et voir avant la compilation les erreurs de mapping.
La méthode 3 permet d’avoir moins de code et des mapping complexes facilement.
À vous de choisir la méthode la plus appropriée en fonction de vos besoins.
Mapping ViewModel -> Model
On a juste à placer comme paramètre dans la méthode appelée lors de l’envoi du formulaire le ViewModel. Le framework ASP.NET MVC s’occupe de remplir les champs et d’assurer la validation si vous utilisez des Attribute sur vos champs. Ensuite, s’il n’y a pas d’erreur dans les validations, il est plus prudent de faire le mapping ViewModel -> Model manuellement ou bien de passer les valeurs à une autre couche applicative. Vous pourriez envoyer vos paramètres à une couche de service par exemple qui s’occupe de conserver les données ou qui place l’opération dans une file d’attente.
Conclusion
Après plusieurs expérimentations et projets, j’en suis venu à utiliser l’approche décrite plus haut. Enfin pour l’instant. Avez-vous des suggestions?