Dot.Blog

C#, XAML, WinUI, WPF, Android, MAUI, IoT, IA, ChatGPT, Prompt Engineering

MVVM, je m’y perds ! [une réponse rapide]

[new:16/09/2010]MVVM est une pattern, une simple pattern, pas une technologie nouvelle. Elle est utilisable dans de nombreux contextes, sous ce nom ou un autre et sous des formes plus ou moins identiques. Rien de nouveau donc. Et pourtant tout change. “Je m’y perds” est une réflexion que j’entends souvent. Encore aujourd’hui, un lecteur de Dot.Blog me faisait part de ce sentiment d’être un peu “embrouillé”. Alors plutôt qu’une réponse en privé qui ne profiterait qu’à un seul, voici la réponse à ce lecteur, et bien sûr, à tous les autres qui partagent la même impression…

Renvoi implicite : je renvoie bien entendu le lecteur à tous les billets portant sur MVVM et notamment aux derniers gros articles qui abordent en profondeur MVVM. Ici il ne s’agit que de répondre globalement à un sentiment. Les réponses techniques se trouvent déjà en ligne dans le Blog.

MVVM ça change quoi au final ?

Tout le problème consiste bien entendu en une question de « point de vue », techniquement MVVM ne fait qu’utiliser des choses « normales ».

C’est donc la façon de segmenter le travail qui change.

Dans une application WinForms « bien faite » on avait les couches suivantes : le DAL (data access layer, la couche d’objet en relation avec la base de données et objectivant les infos remontant de celle-ci et s’occupant de faire les requêtes, d’utiliser les procédures stockées, etc), le BOL (business objet layer, couche d’objets métier, c’est ici qu’apparaissent les concepts de l’application par exemple la notion de client, de facture… le BOL repose sur le DAL pour l’accès aux données) et l’interface Windows Forms (c’est un exemple). Dans cette dernière on gérait en code, accroché au visuel d’une fiche, toute la logique d’affichage en utilisant les objets du BOL qui eux utilisaient donc le DAL pour lire les données ou les persister.

C’est un schéma assez classique et qui connaissait des variantes, par exemple l’utilisation d’une couche de persistance objet entre le BOL et la base de données au lieu d’un DAL écrit à la main, ou même des générateurs de DAL.

Bref c’est un découpage déjà complexe dans certains cas et qui laissait se concentrer une partie de “l’intelligence” dans la fiche, dans son “code-behind”, cela était même conçu pour.

On peut résumer le schéma comme cela : DAL<->BOL<->WinForm.

La Form ne dialoguait jamais directement avec le DAL (hmmm… :-) ) de même que le DAL n’affichait rien (re-hmmmm !). En tout cas les prémices d’une certaine “rigidité” étaient posés même s’ils n’étaient pas toujours parfaitement respectés. De même le code-behind devait se limiter à appeler des méthodes des objets du BOL, même si souvent on voyait des méthodes de Click de bouton pleine de code fonctionnel. C’est ce qu’on appelle le code spaghetti dont j’ai parlé ici quelques fois.

Le découpage à la MVVM

Avec MVVM on fait un découpage assez proche mais différent :

Le DAL ou la couche de persistance objet existe toujours, ce sont les sources de données objectivées, sous MVVM on les appelle les Modèles. C’est juste une question de nom.

La couche BOL existe toujours, il faut bien des objets métiers où les concepts de l’application apparaissent (facture, article, liste d’impayés…). Sous MVVM on intègre cette couche à la notion de « Modèle ». Les objets du BOL ne sont finalement que des données (même si elles sont plus complexes que celles du DAL).

Donc DAL et BOL existent toujours, sous MVVM ce sont des données, donc des Modèles.

La WinForm, la fiche, la surface d’affichage, peu importe la technologie, porte un nom particulier sous MVVM : la Vue.

La petite nuance c’est que si sous WinForm on écrivait beaucoup de code dans le « code behind » de la fiche, sous MVVM c’est quasi interdit (sauf si cela ne concerne que l’affichage).

Du coup ce code il faut bien l’extraire et le placer quelque part… C’est là qu’apparait le Modèle de Vue, ViewModel.

Résumé : du schéma DAL<->BOL<->WinForm on passe au schéma Modèle<->ViewModel<->Vue.

Sachant que le Modèle reprend en gros le groupe DAL<->BOL et que le ViewModel reprend à peu près ce qui se trouvait dans le code-behind des fiches sous WinForms.

La façon de programmer est en réalité la même, c’est juste la place du code et le nom du groupe auquel il appartient qui changent.

La gestion des données, lecture et écriture se fait dans des Modèles qui ne sont que des ensemble DAL/BOL (le DAL discutant avec un SGBD ou des services, peu importe).

L’affichage se fait toujours dans la fiche, sauf qu’on l’appelle la Vue.

Et le code de « bricolage » qui permet « d’adapter » les données des Modèles pour la Vue ou de persister les états de la Vue, au lieu d’être dans le code-behind de celle-ci, il se trouve à part dans un code appelé ViewModel.

Pour conclure : tout ce qui est affichage est mis en page dans des Vues. Tout ce qui concerne les données et les objets métier se trouvent dans des Modèles, et tout ce qui permet d’adapter ces données aux Vues se trouve dans des ViewModel.

Les complications…

Ce petit changement de point de vue qu’introduit MVVM a malgré tout des implications dans la façon d’écrire le code. On ne peut pas se contenter simplement par un couper/coller de déplacer le code du code-behind d’une WinForm pour le placer dans un ViewModel WPF ou Silverlight…

La première complication réside dans le fait que l’ancienne méthode utilisait un principe « étudié pour » pour la gestion des commandes : on place un bouton dans une WinForm on double-clic dessus et automatiquement VS créé un gestionnaire d’événement du Click dans le code-béhind de la fiche.

Avec MVVM, comme ce code est forcément dans le ViewModel, on « s’enquiquine » un peu en le déplaçant : il faut que le ViewModel expose des propriétés de type ICommand et que les éléments d’interface se lient par Data binding à ces commandes transformées en propriétés.

Hélas ni WPF ni Silverlight (et encore moins ASP.NET ou Windows Forms) n’ont été conçus pour cela. Il a des ajouts récents comme ICommand ou le Command Manager de WPF qui permettent de régler en partie le problème, seulement en partie. Et encore ce n’est pas évident. Par exemple la classe Button expose une propriété Command aujourd’hui. On peut donc la lier par data binding facilement avec une propriété de type ICommand du ViewModel. Mais comment gérer le changement de valeur d’un Slider par exemple ? Cela n’est pas pris en compte tout simplement…

Du coup, et principalement au départ pour la gestion de commande, il faut utiliser des toolkits qui simplifient un peu les choses. MVVM Light se charge de le faire par exemple. Prism aussi, en plus complexe, et d’autres toolkits d’ailleurs.

La barrière de la communication

“Avant” on s’autorisait un peu tout… et puis comme beaucoup de code était dans le code-behind des fiches, on avait finalement peu d’états d’âme. Le code spaghetti n’était jamais loin non plus… Et du code même très récent que je peut auditer reste écrit globalement de la même façon qu’il y a 15 ans en “client/serveur” avec des langages comme Delphi ou C++. Pour ceux qui travaillent encore aujourd’hui de cette façon il est certain que le pas à sauter vers la rigueur qu’impose MVVM peut s’avérer un numéro d’équilibriste !

Après avoir gérer la complication introduite dans la gestion des commandes on s’aperçoit assez vite qu’un ViewModel ne pouvant rien afficher (interdiction de même nature que celle qui interdit à la Vue de contenir du code fonctionnel), cela complique encore plus les choses car là il n’y a vraiment rien de prévu, pas même un début de solution comme ICommand pour les commandes.

C’est ici que les toolkits deviennent indispensables pour s’éviter de réinventer la roue, car pour faire communiquer des ViewModels entre eux, ou des Modèles vers des Vue, ou dans le sens contraire des Vue vers les Modèles (même si ce n’est pas usuel cela peut être utile), pour tout cela il n’y a qu’une solution vraiment bonne : la gestion d’une messagerie. Il y a des émetteurs de message (tout objet de n’importe quelle partie en a le droit) et il y a des objets qui s’abonnent à des messages particuliers (idem, ces objets peuvent appartenir à n’importe quel bloc). Du coup, le découplage entre Modèle, ViewModel et View n’est pas violé et sans s’interdire certaines communications notamment transversales ou à « rebrousse-poil ».

Et le reste…

Bien entendu, à tout cela s’ajoute l’asynchronisme, le problème des UI qui ne peuvent être mises à jour que par le Thread principal, la nécessité de gérer du parallélisme (avec PLINQ par exemple), l’obligation aujourd’hui de designer les interfaces, gérer les animations, la cohérence des états, etc… Tout cela complique un peu le tableau final et n’est pas directement lié à MVVM mais prend une « saveur » particulière lorsqu’on utilise cette pattern…

Conclusion

MVVM ne change rien, et MVVM change tout… La seule façon d’intégrer cette nouvelle façon de développer est encore de la pratiquer. Conseil d’une étonnante banalité s’il en est. Mais je ne vais pas jouer les philosophes et vous expliquer le sens de la vie : c’est en buchant qu’on devient bucheron et c’est en sciant que Léonard de Vinci !

(elle est vraiment pas terrible mais il fallait bien finir sur une note de gaité !).

Alors, Stay Tuned, à force tout cela va finir par s’éclaircir !

Faites des heureux, partagez l'article !
blog comments powered by Disqus