mardi 16 mars 2010

Philosophies de frameworks...

Après m'être amusé un long moment avec Wicket, je me suis penché sur le nouveau framework web en vogue du moment : Play!. Il est difficile de comparer ces deux frameworks qui n'ont ni la même philosophie ni la même portée : Wicket est un framework web d'IHM orienté composant, alors que Play! est un framework "full stack" (il gère tout de la vue à l'accès à la base de données), totalement stateless et orienté RAD (rapid application development).
Cependant, il est intéressant de comparer ces frameworks sur certains points : Wicket  propose une forte de gestion de l'état et des composants côté serveur, tandis que Play choisit une approche dans l'esprit REST, avec une absence totale d'état sur le serveur.


L'esprit stateful de Wicket

Un des buts de Wicket est de permettre une programmation "à la Desktop" pour réaliser des applications Web. Pour cela il gère pour nous le rechargement des pages et la mémorisation de l'état des composants et des objets manipulés côté serveur. La gestion du bouton "back" du navigateur (souvent chaotique avec les frameworks Web stateful) ne pose pas de soucis, elle est assurée en mémorisant l' état des composants à un instant t à l'aide un identifiant dans l'URL. On a ainsi la possibilité de revenir aux états précédents quand on le désire, l'inconvénient de ce genre d'URL étant de ne pas être très "sexy" ou lisibles...
Quand vous affichez une liste d'objets, si vous effectuez des actions sur cette liste, Wicket gardera en mémoire cette liste tout au long de vos manipulations et rechargera la page (ou un fragment de page si vous êtes en mode Ajax) dès que cela est nécessaire. Prenons un exemple : nous voulons afficher une liste de marque de voitures. Chaque marque contient une liste de modèles. Au premier affichage de la page, on charge toutes les marques depuis la base de données. Wicket conserve ensuite la liste de marques en mémoire dans la session utilisateur. Si vous voulez afficher la liste des modèles d'une marque en cliquant sur l'IHM, Wicket fera appel à l'objet marque qu'il a en mémoire pour y effectuer un getModeles(), sans repasser par les couches service/DAO.Ceci est bien pratique mais peut tout de même poser quelques problèmes.

Au dela du fait que stocker des choses en session complique forcément les choses si on recherche de la scalabilité (les objets et les composants gardés en session prennent de la place en mémoire, si on déploie sur plusieurs serveurs il va falloir gérer la duplication de la session…), le fait de garder en mémoire des objets venant des couches d'accès aux bases de données peut vite provoquer des problèmes de désynchronisation avec l'accès à la base.
Je m'explique :  si la liste des modèles d'une marque est configurée en lazy loading dans votre outil de mapping objet/relationnel, vous aurez la mauvaise surprise d'avoir une exception  de lazy loading lorsque vous demanderez à votre page web de charger une liste de modèles. En effet à ce moment là vous êtes sorti de votre session hibernate (par exemple) et vous n'avez plus accès aux données "lazy loadées".  Il existe des contournements à ces problèmes. La configuration d'une "session in view" avec Spring permet de garder la session hibernate ouverte dans la couche Wicket pendant la durée du cycle requête/réponse http, le LoadableDetachableModel permet de ne pas garder de références directe aux objets dans Wicket et de rappeler la base de données quand on a besoin d'avoir accès à la session pour charger certaines propriétés. Mais globalement ces problématiques peuvent devenir difficiles à gérer lorsque l'on travaille sur des écrans un peu complexes.

Le style REST de Play!

A ce niveau là Play a choisi l'approche opposée de Wicket : le serveur n'a aucune mémoire, si on a besoin de travailler sur des objets pendant plusieurs requêtes d'affilé, soit on stocke une partie des données côté client, soit on retraverse toutes les couches jusqu'à la base de données à chaque rechargements des données sur l'IHM.

Pour revenir à notre exemple précédent, si on veut afficher notre écran qui liste les marques et les modèles de voitures, 2 solutions : 
  1. on charge tout depuis la base et on traite les données côté client avec du JavaScript, avec la possibilité de stocker des infos en  local , par exemple avec sessionStorage ou localStorage en  html5 (play! est vraiment dans l'air du temps vous voyez)
  2. on charge la liste des marques, quand l'utilisateur veut lister les modeles d'une marque on rappele la base avec une nouvelle requete et on recharge la liste en ajax 
Dans les 2 cas on n'a pas gardé de référence aux marques en mémoire dans la session côté serveur. De cette manière, outre le gain au niveau de consommation mémoire et de la scalabilité, on ne rencontre plus du tout de problème de lazy loading.
Pour ce qui est du bouton "back", pas de problème non plus, le serveur ne fait que répondre à de simples requêtes, tous les paramètres dont on a besoin pour afficher une page sont dans l'URL (ou dans la requête pour le POST) et si on retourne à une URL précédente, le serveur effectuera le traitement correspondant sans sourciller, rien n'est caché dans une quelconque session. Play mise d'ailleurs sur des URL les plus propres et expressives posssible. Par exemple pour éditer les modèles de la marque Peugeot, l'URL pourrait être :  http://myserver/marques/Peugeot . Pour créer une nouvelle marque dans la base, ce serait par exemple http://myserver/marques/new .

Conclusion?

Les composants côté serveur de Wicket lui apportent néanmoins certains avantages :
  • vos composants sont écris en Java: un seul langage à maitriser, pas de JavaScript à coder (même l'ajax se fait en Java), maintenance facilitée 
  • vos composants sont débuggables sur le serveur avec votre IDE java préféré, et c'est un point non négligeable

A propos des composants, je ferais une petite parenthèse. Avec Wicket il est aisé de créer des composants réutilisables grâce à Java, on peut utiliser des vrais notions objet, de la composition et de l'héritage de composants.
Cependant si on veut aller plus côté RIA et afficher quelques animations sympa ou des effets graphiques non gérés par Wicket, on est soit obligés de rajouter du JavaScript en plus de notre code Java, ce qui est un peu le contraire de ce que l'on recherche avecWicket, soit d'intégrer certains effets JavaScript dans des composants Wicket. Ceci a par exemple été  fait par WiQuery qui propose des composants Wicket basés sur JQuery, utilisables directement en Java. Mais créer des tels composants n'est pas forcément évident et demande un certain investissement et là aussi, une maitrise du JavaScript!
Play se base sur un système de templates et de tags extensibles pour travailler côté HTML et inclue dans sa distribution de base la bibliothèque JQuery, que l'utilisateur utilisera directement dans la vue (on peut bien sûr rajouter d'autres libirairies JavaScript dans Play, finalement côté vue ce n'est que du HTML/CSS/JavaScript standard).
Alors oui ça fait un framework de plus à apprendre, oui il faudra coder du JavaScript, mais en choisissant cette approche vous ne serez jamais limité par le framework pour faire ce que vous voulez côté client. Et puis vous verrez JQuery est très simple, bien documenté et plutôt sympa, même pour un développeur Java, pas de quoi être effrayé :)

Pour conclure, les 2 approches ont leurs bons et mauvais côtés. Un développeur Java amoureux de l'objet préferera surement coder avec Wicket. Quelqu'un en recherche de scalabilité, de performances et de simplicité ira plûtot voir du côté de Play, mais devra garder à l'esprit que pour faire quelques composants sympa, le Java ne lui suffira plus et qu'il devra toucher un peu de JavaScript.

Si vous travaillez sur des sites qui risquent de devoir supporter de forts trafics ou si vous cherchez une solution qui sera facilement déployable dans le Cloud (dans lequel votre application peut se trouver sur un grand nombre d'instances à un instant t), la capacité à être scalable est en tout cas un facteur que vous ne devrez pas négliger.