Comment jouer au Jenga avec du code : migrer une application complexe d'AngularJS dans React

by Juan Cabello
12 Minute Read

Le stress de prendre une seule brique de la tour et de la placer au sommet ; la pression monte à chaque mouvement : quiconque ayant déjà joué au Jenga comprend ce sentiment. Imaginez jouer au Jenga avec une énorme tour devant un public en direct, où toute erreur signifie un désastre total. L'expérience de mon équipe pour migrer une application d'AngularJS dans React a semblé être un défi similaire.

Imaginez une application frontale en tant que tour de Jenga et chaque brique en tant que composant interdépendant. Retirer une brique peut signifier la correction d'un bug dans le code existant, tout en remplaçant une brique qui pourrait être la programmation d'une nouvelle fonctionnalité. Les utilisateurs de l'application ne doivent pas remarquer de changement, sauf un design potentiellement actualisé, et l'application devra fonctionner au moins aussi bien qu'auparavant. Étant donné que les deux frameworks sont des frameworks JavaScript, cela ne devrait pas être un défi énorme, n'est-ce pas ?

Pour vous dire la vérité, nous avons en fait trouvé que cette migration a été un vrai défi. Planifier la migration, développer de nouvelles fonctionnalités sur notre feuille de route de produits et rembourser la dette technologique n'est pas facile, mais nous devions relever tous ces défis simultanément.

Malgré ces problèmes, nous avons décidé de passer d'AngularJS à React.

Les raisons du changement

AngularJS a marqué un nouveau départ dans le développement frontal, et il a montré qu'il y avait plus au développement frontal qu'un simple travail d'UI (interface utilisateur). Cependant, les niveaux d'abstraction d'AngularJS étaient encore trop compliqués pour la plupart des ingénieurs frontaux. On ne pouvait plus simplement écrire une fonction jQuery pour activer la visibilité des éléments. À ce moment-là, une ingénierie appropriée était nécessaire, de la mise à jour du DOM en fonction de l'état à la gestion des dépendances. (N'oubliez pas qu'il s'agissait de la période avant les modules bundlers.)

Depuis la sortie d'AngularJS, de nombreux frameworks d'interface utilisateur et de bibliothèques ont augmenté en popularité. Les difficultés d'AngularJS sont finalement devenues lourdes, donc les développeurs frontaux ont opté pour de nouveaux frameworks plus récents. En plus de cela, la dernière version d'AngularJS qui a proposé un LTS de trois ans était de 1,7, et a été lancé en juillet 2018. Les produits sérieux ne peuvent pas être construits sur des frameworks sans une assistance à long terme, c'était donc un signe nous informant que nous allions bientôt devoir changer de cadres.

Nous avons choisi React, principalement en raison de son adoption parmi les ingénieurs frontaux, et de la facilité d'embaucher ceux qui ont de l'expérience avec React. Ses performances sont également supérieures à Angular. L'écosystème de React est énorme et était déjà plus grand que celui d'Angular au moment où nous avons décidé d'effectuer ce changement...

Après avoir décidé de migrer, nous devions savoir par où commencer. Nous avons cherché les histoires d'autres entreprises qui avaient migré, et à notre surprise, il y avait peu d'informations disponibles... Alors nous avons suivi notre propre chemin.

L'ingénierie frontale est une question d'affichage de morceaux de données optimisés de manière conviviale pour les utilisateurs, ainsi qu'une résolution de problèmes. Qu'il s'agisse d'interagir avec un autre service ou de présenter des informations sur un produit, l'objectif principal de l'ingénierie frontale est de permettre aux utilisateurs d'interagir avec les morceaux de données affichés. La capacité de résolution de problèmes d'un produit peut être mesurée par trois facteurs : (1) le temps qu'un utilisateur à exécuter une tâche particulière ; (2) la disponibilité du service ; (3) et la cohérence des données. L'UX (expérience utilisateur) combine l'efficacité d'un produit avec le sentiment que l'utilisateur obtient en interagissant avec le produit.

Architectures différentes

S'il y a une dette technologique accumulée dans votre code AngularJS, vous ne souhaitez pas le transférer à votre nouvelle base de code React. Il est également imprudent de transférer des bugs qui n'ont pas été résolus. Pour un nouveau départ, vous devez commencer par les bases : l'architecture.

Pour commencer à développer une architecture appropriée, nous devions nous faire à l'idée des différences entre le développement d'une application dans AngularJS et dans React. Le premier grand problème que nous avons abordé était le fait qu'AngularJS ait un fournisseur verrouillé, ce que React n'a pas. (Le slogan de React est « une bibliothèque JavaScript pour créer des interfaces utilisateur. ») Dans Angular-land, vous devez suivre la méthode Angular et l'utiliser pour les appels d'API, l'état, et bien plus encore. Vous pourriez ne pas le faire, mais vous pourriez rencontrer des problèmes comme lors de la mise à jour de l'interface utilisateur si les données reçues n'apparaissaient pas dans la même pile d'appels (par exemple, des rappels, des promesses, etc.). En outre, il y a beaucoup de jargon technique à apprendre avec Angular, comme les contrôleurs, les directives, les modules, les services, etc. Pour gérer efficacement tout cela, la méthode AngularJS recommande de décharger les opérations de gestion d'état dans le contrôleur. L'état est récupéré par des services. Les contrôleurs sont le point focal qui récupère l'état d'un service, gère l'état et met à jour l'affichage. L'affichage dans AngularJS est composé de modèles qui affichent l'état actuel comme indiqué par les contrôleurs.

Dans React, la chose la plus importante à comprendre est le flux de données. Il suffit de se représenter une application React comme un arbre de données dans lequel chaque nœud est un composant (ou une brique de Jenga). Chaque composant a un état qui est géré par son composant respectif et reçoit des propriétés connues sous le nom de props, qui sont gérées par un composant parent. Ce système régule le flux de données dans l'application. Les props représentent les bords entre les nœuds, alors que l'état encapsule les données et est représenté par les nœuds eux-mêmes. L'état peut être transmis à l'arbre par le biais de props ou être utilisé pour ce composant particulier. Alternativement, vous pouvez créer un « contexte », qui crée des données partagées entre les nœuds qui sont abonnés à ce contexte.

Si vous souhaitez conserver le style architectural d'Angular, vous pouvez utiliser des modèles de conception analogues dans React. Par exemple, vous pouvez créer différents composants sans état qui sont similaires aux modèles d'Angular, mais qui affichent simplement les données et traitent les événements. Pour remplacer les contrôleurs d'Angular, vous pouvez utiliser un composant avec état, qui gère les données reçues soit par un serveur, un contexte, son état local ou les props de composants supérieurs et les fournit à de multiples composants sans état.

Si vous souhaitez faire les choses selon la méthode React, vous pouvez améliorer votre application avec une bibliothèque de conteneurs d'état centralisée, comme Redux.

Migration par couches

Maintenant, nous connaissons les différences entre le fonctionnement de chaque framework, mais y a-t-il des points communs dont nous pouvons tirer parti pour faciliter notre migration ? Dans les applications Web modernes, il y a souvent trois couches, toutes sont emballées dans le même fichier groupé : la couche d'état, qui gère la plupart des communications avec le serveur et agit comme une couche de cache ; la couche de contrôleur, qui gère les transitions d'état et la communication avec la couche d'état ; et la couche d'affichage qui montre l'état actuel de l'application.

Pour planifier une migration, vous devez considérer les trois couches de l'application. C'est important en particulier lors de la migration d'une application qui n'était pas efficacement architecturée, c'est un bon endroit pour commencer à faire les choses correctement. Nous avons examiné trois approches différentes pour migrer une application AngularJS :

  1. Migrer d'abord la couche d'état, puis le contrôleur et les couches d'affichage
  2. Migrer d'abord la couche d'affichage, puis les couches d'état et de contrôleur
  3. Migrer les trois couches en une seule fois par étapes continues

Examinons chacune des approches mentionnées ci-dessus.

Migrer d'abord la couche d'état, puis le contrôleur et les couches d'affichage

Cette approche se concentre d'abord sur le remplacement des services d'AngularJS. À cette fin, un conteneur d'état comme Redux, Flux ou MobX est un bon moyen de centraliser toutes les opérations de données et de découpler votre couche d'état depuis n'importe quelle autre couche. Assurez-vous de choisir celui qui convient le mieux à vos besoins, il y a beaucoup de ressources qui décrivent chaque conteneur d'état. En centralisant toutes les opérations de stockage et en les déchargeant d'AngularJS, nous obtenons également une liberté totale pour l'UI. Vous évitez d'être verrouillé dans une bibliothèque d'UI, ce qui facilite le remplacement par tout ce dont vous avez besoin. Dans ce cas, nous pouvons simplement remplacer les contrôleurs, les directives et les modèles d'AngularJS par des composants React jusqu'à ce que nous supprimions enfin tous les AngularJS de notre base de code.

Migrez d'abord la couche d'affichage, puis les couches d'état et de contrôleur

Cette approche se concentre sur les modèles AngularJS en premier. Étant donné que les modèles d'AngularJS sont principalement du HTML et du CSS, il est assez facile de les inscrire dans un composant React en utilisant les liaisons appropriées pour connecter votre composant React à vos contrôleurs Angular. Les bibliothèques comme react2angular ou ngReact vous permettent de lier les composants React directement à vos modèles Angular. Vous pouvez rencontrer des problèmes de performance, mais dans l'ensemble, les performances ne sont pas significativement affectées. En ce qui concerne une architecture découplée, il est toujours possible d'y arriver de cette façon, car il est s'agit simplement d'inverser l'ordre de migration de la première approche. Les contrôleurs sont le point focal donc dans tous les cas ils doivent être raccrochés à l'un ou l'autre des cas jusqu'à l'étape ultérieure. Cette dernière étape est la même que la première étape de la première approche que nous avons présentée.

Migrer les trois couches en une seule fois par étapes continues

La troisième et dernière approche que nous considérons se concentre sur des parties de l'application et vise à les faire migrer verticalement. Vous pouvez vous la représenter comme suit : les pages qui deviennent indépendantes, seront également migrées indépendamment. Il est tentant de choisir cette approche, mais il est également très facile de concevoir votre système d'une manière qui sera continuellement interrompue. En outre, les performances peuvent être affectées aussi longtemps que la migration est en cours. Lors de la migration de l'affichage du rendu, il y a des utilisations d'état cachées qui ne peuvent pas être prises en compte et qui nécessite en fin de compte le maintien de l'état dans deux parties différentes de votre application.

Ce que nous avons choisi

Nous avons en fait utilisé les trois approches dans notre migration. Nos premières étapes étaient, la migration de très petits composants suivie de la seconde approche, c'est-à-dire que nous utilisions toujours le contrôleur et la couche d'état d'Angular, mais que nous avons implémenté l'UI dans React. Nous avons conclu que de cette façon, nous prolongions le remboursement de la dette technologique ce qui pourrait éventuellement être contre-productif. L'occasion d'utiliser cette opportunité pour faire les choses correctement n'a pas été utilisée convenablement. Cependant, nous en avons tiré des leçons. Nous avons eu une expérience directe avec les bibliothèques, que nous avons réutilisée plus tard dans les autres approches que nous avons adoptées.

Nous avons réévalué nos options et avons choisi la troisième. Nous avons migré notre UI sur une base d'affichage préliminaire. Nous avons passé en moyenne un mois par affichage complet, y compris la réécriture de toutes les interactions, la migration de l'état vers Redux, etc. Nous avons fait des progrès et avons pu passer d'une application à l'autre, sans trop de tracas. Cependant, nous avons réalisé que cette approche prenait trop de temps. Notre délai de livraison était compromis et il augmentait le risque de la mauvaise qualité de code en raison de la pression du temps. Ainsi, après avoir migré deux pages complètes, nous nous sommes arrêtés et avons de nouveau réévalué nos options.

Nous voulions que les délais de livraison soient plus rapides et la possibilité de faire avancer nos étapes afin que nos ressources d'ingénierie soient réparties sur différents sprints sans perdre de temps. En outre, nous voulions utiliser cette opportunité pour faire les choses correctement et rembourser la dette technologique au fur et à mesure que nous avançions, ou du moins ne pas transférer la dette technologique dans notre nouvelle base de code.

En général, nous étions plus satisfaits de d'abord migrer l'état, et nous pensions que ce serait de plus en plus important lorsque nous commencerions à migrer notre composant le plus important et le plus lourd en termes de données. N'oubliez pas : l'ingénierie frontale repose sur l'affichage de morceaux de données. Donc, en vous assurant que les données ne sont pas basées sur le framework, vous pouvez commencer à vous concentrer entièrement sur l'optimisation de l'affichage pour vos utilisateurs et sur la manière la plus efficace de résoudre le problème.

Conclusion

Nous avons appris certaines leçons qui sont utiles pour quiconque désire migrer une application d'AngularJS dans React, et en fait pour quiconque désire effectuer une migration frontale. Voici quelques-unes des plus importantes :

  • Une bonne architecture aide à rembourser la dette technologique

Si vous n'avez pas d'architecture correcte pour votre application, il est également facile de migrer la dette technologique. Bien que l'investissement dans le temps soit déjà élevé, nous recommandons de résoudre la dette technologique lors de la migration, au lieu de la laisser traîner, car les coûts futurs de la résolution de la dette technologique sont encore plus élevés. Commencez par comprendre l'architecture de base d'une application React et AngularJS et ensuite quelle approche suivre.

  • Considérez la migration comme une fonctionnalité

La migration d'une application vous donne la possibilité de corriger ce qui était incorrect. Ne perdez pas cette opportunité et aidez votre équipe à planifier le développement de produits en conséquence. Cela peut prendre plus de temps, mais une bonne refactorisation (réécriture) remboursera son investissement dans le temps à mesure que votre produit se développe.