À la découverte des architectures du front (4/4) Les applications universelles.
L’objectif de cette suite d’articles (sites statiques, MPA, SPA et applications universelles) est de faire le point sur les différentes architectures front-end. Pour cela, nous analyserons leur fonctionnement, avantages et inconvénients, ainsi que les besoins qui les ont faits émerger au fil du temps.
Comprendre l’historique de ces architectures permet de prendre de meilleures décisions lors du développement d’une nouvelle application.
Historique
Les applications universelles, aussi appelées isomorphiques, apparaissent quelques années après les Single Page Applications.
À l’origine, le web consiste à consulter des pages auprès de serveurs et à cliquer sur des liens pour naviguer de page en page. Chaque clic sur un lien aboutit donc à une nouvelle requête au serveur, pour récupérer la nouvelle page, et provoque donc un rafraîchissement.
Les Multiple Page Applications utilisent ce fonctionnement natif du web, en générant toutes les pages côté serveur, ce qui malheureusement peut aboutir à un couplage entre la logique front et le back.
À l’inverse, une SPA simule les comportements d’un site web classique, à l’aide du JavaScript et des APIs du navigateur. Cela permet d’améliorer l’interactivité des applications web tout en garantissant le découplage du code d’affichage et du code métier (situé sur le serveur). Mais cela entraîne quelques limitations, notamment du point de vue du référencement .
L’idée des applications universelles émerge afin de prendre le meilleur des deux mondes : chargement initial complet et référencement des pages, comme les sites générés côté serveur (MPA et les sites statiques), fluidité de navigation et séparation des responsabilités des SPA (cf. l’article précédent).
Cinématique
- l’utilisateur demande la page “/page1”
- le serveur exécute le JS et récupère les données de l’API pour rendre la page
- le serveur répond la page complète comme sur une MPA
- le navigateur va télécharger les assets : JavaScript, CSS, images
- l'application est exécutée dans le navigateur et se synchronise avec le HTML rendu côté serveur (cette phase est souvent appelée "réhydratation")
- la suite de la navigation se fait entièrement côté navigateur comme sur une SPA
- le client fait directement des appels d’API pour les données supplémentaires
Description
Une application isomorphique est une application qui mutualise du code client et serveur.
Ce concept peut être utilisé pour pallier les problèmes de référencement des SPAs en générant la première page demandée côté serveur (étapes 1 à 3 dans le schéma ci-dessus).
On a pu apercevoir quelques tentatives avec l’ancienne génération de frameworks (exemple : la bibliothèque Rendr pour Backbone.js). Malheureusement, ces derniers sont fortement couplés au contexte navigateur (accès direct au DOM pour le rendu des pages), rendant la tâche complexe et pas forcément performante.
En revanche, les frameworks modernes utilisent tous une couche d’abstraction avec le DOM (exemple : Virtual DOM pour React), ce qui facilite grandement l'exécution de code en dehors du navigateur. De plus, ils offrent tous des solutions dans leur documentation pour l'implémentation d’une application universelle.
En partant de ces documentations, on peut donc développer son serveur d’application en Node.js (car ils partagent le même langage : JavaScript). Il est également possible d’utiliser des structures de projet préconfigurées (boilerplate), ou des surcouches nativement pensées pour les applications universelles (Next pour React, Nuxt pour Vue).
Les implications du choix de ce type d’architecture sont multiples et ajoutent des contraintes pendant le développement de l’application.
La complexité technique induite nécessitera des développeurs à l’aise avec le JavaScript, que ce soit côté front ou côté back.
Cette complexité se retrouve durant tout le cycle de développement du projet, où il faut garder en tête que tout le code doit être isomorphique (par exemple l’accès aux cookies ne se fait pas de la même manière côté client et côté serveur).
Ensuite, d’un point de vue infrastructure, il faut un véritable serveur d’application qu’il faudra dimensionner comme on le ferait dans le cadre d’une MPA, et pas simplement un serveur de fichiers statiques comme on en a l’habitude avec les SPAs.
Cas d’utilisation type
L’application universelle peut être vue comme un hybride de MPA et de SPA. À ce titre, ses applications regroupent tous les cas d'utilisations citées dans les précédents articles.
L’exemple type serait un site grand public avec un fort besoin de référencement, une expérience utilisateur optimale et un code maintenable.
Néanmoins, il faudra garder en tête le surcoût induit par cette technologie :
- coût de développement, car le code isomorphique est intrinsèquement plus complexe que le code front classique, sans compter le développement du serveur (à moins d’utiliser un framework pensé dès le début pour les applications universelles comme Next ou Nuxt)
- coût d’infrastructure lié à la mise en place d’un serveur d’application web pour le rendu des pages
Caractéristiques
Hybride MPA/SPA : Reprend les avantages des Multiple Page Applications et des Single Page Applications
Affichage initial complet : Pas d’effet page blanche (contrairement à une SPA), la page initiale est directement remplie lors de la première réponse du serveur, ce qui peut améliorer la perception de performance.
Référencement optimal : Comme sur une MPA, la première page est entièrement rendue côté serveur, les robots d’indexation n’ont donc aucun mal à les lire.
Balise Head dynamique : Possibilité de modifier dynamiquement les balises dans le head, entre autre pour le partage sur les réseaux sociaux (exemple : protocole open graph de Facebook).
Navigation fluide : Tous les templates sont préchargés (comme pour une SPA), le client ne fait plus que des appels d’API pour simplement récupérer les données brutes dès la 2ème page.
Peut fonctionner sans JS côté client : En l’absence de JavaScript côté client, les liens fonctionnent toujours, et la navigation se fera de manière classique comme sur une MPA (mais l’interactivité de la page peut être dégradée).
Maintenabilité et testabilité : Séparation des responsabilités et testabilité similaire à une SPA, car le code front se trouve dans une application front bien séparée de l’API.
Limitations
Complexité d’architecture : Traitement différent de la première page (côté serveur), et de la suite de la navigation (côté client)
Contraintes de développement : Le code dédié au front-end doit être exécutable aussi bien côté client que côté serveur (isomorphique). Cela signifie qu’il faut éviter l’utilisation des variables liées au contexte du navigateur (window, cookies, …), ou en tout cas toujours prévoir le cas où le code serait exécuté du côté du serveur (par exemple aller chercher le cookie dans la requête plutôt qu’avec l’API du navigateur).
Infrastructure supplémentaire : Par rapport à une SPA, une application universelle nécessite un serveur Node.js bien dimensionné pour pouvoir rendre les premières pages de tous les clients. Il est également à noter que le rendu de longues chaînes de caractères HTML peut bloquer la boucle d’événements pendant un certain laps de temps et donc bloquer le serveur pour tous les utilisateurs.
Difficulté de design responsive : La première page se rendant côté serveur, on ne connaît pas forcément la taille de la fenêtre de navigation côté client. La page rendue par le serveur ne sera donc pas forcément adaptée à la taille du client dès sa réception, ce qui peut donner un effet de flash avant que l'application s’adapte à la fenêtre de navigation côté client (viewport).
Conclusion
Au fil des années, nous nous sommes habitués à des sites web aux contenus toujours plus riches, toujours plus dynamiques, mais qui doivent rester accessibles et performants, même sur des terminaux mobiles dans des conditions de connectivité limitée.
Les architectures web ont dû évoluer pour répondre à ces besoins, tout en respectant les contraintes fondamentales du web (navigateur, communication client/serveur, HTML/CSS/JS), quitte à se complexifier.
À première vue, l’architecture universelle semble avoir tout pour elle : performance, fluidité, référencement et testabilité.
Mais il ne faut pas perdre de vue que ce type d’architecture introduit une complexité non négligeable, que ce soit pour l’infrastructure (besoin d’un serveur de rendu), ou pour le développement en lui-même.
Pour pallier cette complexité grandissante, les frameworks et outils front modernes sont pensés pour être isomorphiques.
De quoi seront faites les architectures web front de demain ? Seront-elles la continuité de celles que nous avons présentées dans cette série d’articles, ou s’agira-t-il de technologies radicalement différentes ? Deux approches reviennent souvent.
L'a****pproche évolutionniste : c’est l’approche classique que le web a connu jusqu’à présent. Les architectures actuelles continuent d’évoluer pour être toujours plus optimisées et cette évolution pourrait aboutir à de nouveaux modèles d’architecture. On peut citer l’exemple des Progressive Web Applications (PWA) qui répondent aux besoins de mobilité grandissants sans remettre en cause les technologies actuelles du web (HTML/JS/CSS).
**L'**approche disruptive : faire table rase du passé pour s’affranchir de ses contraintes devenues bloquantes en termes de fonctionnalités et de performance (en particulier le DOM). De nouveaux modèles d’architecture apparaissent qui se basent sur des technologies pensées dès le début pour nos besoins actuels. Le standard WebAssembly peut être vu comme une évolution possible du web dans cette direction.