Fermer

septembre 26, 2019

Approche Agnostique Cadre (Partie 1)


À propos de l'auteur

Denys est un développeur frontend et un orateur public. Diplômé d'une école d'art et ingénieur, Denys est un passionné de psychologie, de physique,…
Plus d'informations sur
Denys
Mishunov

Certains d'entre nous préfèrent travailler avec Vue, d'autres comme React, d'autres apprécient Angular, tandis que d'autres ont l'impression que Svelte est notre avenir. Vous êtes peut-être même un de ceux qui ne craint pas le coeur et qui construisent des applications avec des composants Web. Le monde frontal contemporain peut satisfaire tous les goûts! Cependant, que se passe-t-il si votre projet reste bloqué dans le passé? Que se passe-t-il si vous passez le temps disproportionné de prendre en charge un système obsolète? La réponse typique à un tel problème est la migration de l'application. Cependant, tous les cadres frontaux sont différents et, par conséquent, les processus de migration doivent être différents exercices non triviaux. Droite? Pas nécessairement. Dans cet article, nous discutons de «Frankenstein Migration», nouvelle approche agnostique du processus de migration qui permet d'utiliser le même mécanisme pour migrer dans pratiquement tous les cadres de votre choix.

Migration, selon le modèle choisi. Oxford Learner's Dictionary est “le mouvement lent ou graduel de quelque chose d'un endroit à un autre”. Ce terme décrit beaucoup de choses et de phénomènes dans notre monde – avec une teinte positive et négative. Dans le développement de logiciels, le mot «migration», lorsque nous devons mettre à niveau ou modifier une technologie dans un projet, tombe généralement dans ce dernier cas, malheureusement.

«Bien», «Rapide», «Bon marché». Nous avions l'habitude d'en choisir deux dans de nombreuses situations où nous devions choisir, que ce soit dans le développement, dans les affaires ou dans la vie en général. En règle générale, la migration frontale, qui est le sujet principal de cet article, ne permet même pas cela: "pas cher" est hors de portée de toute migration et vous devez choisir "bon" ou " vite. ”Cependant, vous ne pouvez pas avoir les deux. Typiquement.

 Le monstre de Frankenstein ne devrait pas nécessairement être terrible. Il peut être mignon Parfois.
Le monstre de Frankenstein ne devrait pas nécessairement être terrible. Il peut être mignon Quelquefois. ( Image agrandie )

Dans une tentative de briser les stéréotypes, cet article suggère une approche peu typique de la migration indépendante du cadre d'applications frontales: le « Frankenstein Migration . ”Cette approche nous permet de combiner« bon »et« rapide »tout en maîtrisant les coûts de la migration.

Ce n'est pas pour autant une solution miracle. J'aime plutôt y penser comme une petite révolution migratoire. Et comme toute autre révolution, cette approche pourrait avoir des effets secondaires, des problèmes et des gens débordants d'énergie pour prétendre que cela ne fonctionnera pas avant même d'avoir essayé.

Nous allons certainement aborder plus avant les problèmes potentiels de cette approche. dans l'article, mais continuez avec moi et, peut-être, vous trouverez toujours une ou deux idées utiles pour votre prochaine migration.

En outre, la même approche que nous allons discuter peut être utilisée pour une plus grande gamme de tâches ne sont pas directement liés à la migration:

  • Combinaison de différentes parties de votre application écrites dans des cadres différents. Cela pourrait être utile pour le prototypage rapide, l'amorçage et même des expériences prêtes pour la production.
  • Découpler différentes fonctionnalités de votre application afin de pouvoir se déployer sans reconstruire l'ensemble de l'application . Peut-être même définissez-vous vos fonctionnalités principales sur le cycle de publication plus fréquent. Cela peut être utile dans les grands projets. En particulier, ceux qui parcourent CI / CD chaque fois que vous poussez des choses dans master (cela peut prendre très longtemps) et permettent de gagner du temps sur les nouvelles fonctionnalités.
  • Cette approche peut même vous permettre d’avoir politique d'embauche flexible : vous pouvez engager des développeurs intelligents même s'ils ne travaillent pas encore avec le cadre de votre projet. Les développeurs peuvent continuer à utiliser des outils et des cadres avec lesquels ils sont à l'aise et apportent de la valeur à votre entreprise dès le premier jour (particulièrement utiles dans les startups) tout en apprenant le fonctionnement de votre projet et en choisissant le cadre de votre choix.

Néanmoins, cet article concerne avant tout migrations et avant de plonger profondément dans les eaux sombres de la migration Frankenstein, voyons où nous en sommes avec ces « bonnes » et les « rapides » alternatives de migration pour être conscients de leur force comme des points faibles.

«Bonne» migration: réécriture complète

Habituellement, la réécriture complète est considérée comme un meilleur moyen de migrer vos applications en termes de qualité. Cela a du sens: vous écrivez votre application à partir de rien et vous pouvez donc apporter toute votre expérience et votre sagesse de la mise en œuvre actuelle à la nouvelle, dès le début, et non après coup. C'est un gros plus pour ce type de migration. Cependant, une réécriture complète pose un problème pas si évident.

Pour obtenir cette qualité, vous avez besoin de temps . Parfois, un lot de temps. Alternativement, de nombreux développeurs se consacrent exclusivement à la réécriture. Toutes les entreprises ne peuvent pas se permettre ces options. De ce fait, le scénario le plus approprié pour ce type de migration est soit un petit projet personnel sans besoin de développer de nouvelles fonctionnalités à tout moment, soit un projet qui n’est pas essentiel à la mission de votre entreprise.

perspective du temps: une fois, je suis allé à une réécriture complète d'une application qui a pris deux ans. Pourtant, pendant tout ce temps, l'ancien projet avec tous ses bugs était opérationnel. Personne ne voulait y toucher et se concentrait plutôt sur le «nouveau et brillant». Typiquement.

En résumé pour ce type de migration:

PROS :

CONS :

  • Le temps nécessaire pour obtenir cette qualité jusqu'au bout
  • La quantité de travail à effectuer lors de la réécriture complète est énorme, ce qui rend difficile l'estimation du temps et des ressources nécessaires pour ce type de migration à l'avance.

Ceux qui envisagent de migrer sans avoir les moyens de se -write en raison de contraintes de temps ou de ressources, voudrez peut-être examiner le type de migration suivant.

Migration «rapide»: migration progressive

Contrairement à la réécriture complète, la migration progressive ne nécessite pas l'attente de la migration complète. . Au lieu de cela, vous migrez application par bit et mettez ces nouveaux bits à la disposition de vos utilisateurs dès qu'ils sont prêts. Dire que ce type de migration est rapide est un peu exagéré, si l’on parle de l’ensemble de l’application, mais il est clair que des fonctionnalités distinctes peuvent être fournies aux utilisateurs beaucoup plus rapidement. Cependant, donnons aussi le pour et le contre de la migration progressive sans biais:

PROS :

  • Lorsqu'il s'agit de fournir des parties d'application distinctes à l'utilisateur final, la migration progressive est en effet plus rapide que réécriture complète car nous n'avons pas besoin d'attendre que l'ensemble de l'application soit réécrit.
  • En fournissant progressivement de nouveaux éléments migrés, nous obtenons un retour d'informations sur eux (des utilisateurs finaux). aller. Cela nous permet de détecter les bogues et les problèmes plus rapidement et de manière plus isolée, en comparant pour terminer la réécriture, où nous déployons l'application migrée dans son ensemble et risquons d'ignorer certains problèmes ou bogues plus petits.

Pour mieux comprendre les problèmes de migration progressive, essayez d’installer React en parallèle avec Vue dans le même projet que dans la migration Vue à React. Je pense que vous devez vraiment aimer creuser les configurations et résoudre les erreurs de console pour profiter de ce processus. Cependant, nous n’avons même pas besoin d’avoir cette profondeur. Considérons l’exemple suivant :

 Les composants, quels que soient les modules CSS, sont très vulnérables aux styles globaux. Dans cet exemple simple, nous avons au moins quatre manières de décomposer visuellement le composant Vue:
Les composants, quels que soient les modules CSS, sont très vulnérables aux styles globaux. Dans cet exemple simple, nous avons au moins quatre manières de décomposer visuellement le composant Vue. ( Source ) ( Grand aperçu )

Nous intégrons ici un composant Vue dans une application Vanilla JS comme dans le scénario de migration potentiel Vanilla vers Vue. Les modules CSS sont responsables du style du composant Vue et offrent une portée appropriée à vos composants. Comme vous pouvez le constater, cependant, même si le style du composant Vue indique que le sous-en-tête doit être vert, il est complètement désactivé et l'exemple en présente quatre (mais il y en a vraiment beaucoup plus) de manières triviales de casser le style du composant. 19659005] En outre, d'autres styles globaux entrant dans ce composant Vue peuvent en élargir complètement l'apparence. Même s'il peut être perçu comme une fonctionnalité de certains projets, il est difficile de prévoir, de maintenir et n'est pas nécessairement ce que nous souhaitons. Cet exemple révèle le problème le plus courant et le plus difficile à résoudre de la migration graduelle: la partie «Cascade» de CSS peut facilement casser des composants.

Cet exemple simplifié artificiellement révèle également plusieurs autres grands problèmes liés à la migration progressive :

  • Comme nous combinons deux systèmes différents, le résultat peut s'avérer très encombré: nous devons prendre en charge deux systèmes différents avec leurs dépendances, leurs exigences et leurs opinions simultanément dans le même projet. Différents frameworks peuvent nécessiter les mêmes dépendances, mais dans des versions différentes, il en résulte des conflits de version.
  • Étant donné que l'application intégrée (Vue dans notre cas) est restituée dans l'arborescence DOM principale, la portée globale de JavaScript est sujette aux conflits: les deux systèmes voudrez peut-être manipuler des nœuds DOM qui ne leur appartiennent pas.
  • En outre, permettez-moi de répéter ceci, car nous allons arriver à ce point plusieurs fois dans cet article: En raison de la nature globale de cette intégration, les CSS débordent d’un système à l’autre sans trop de contrôle, polluant ainsi la portée globale de la même manière que JavaScript

Pour résoudre ces problèmes (ou au moins les tenir à distance), nous devons mettre en œuvre des solutions de contournement, des hacks et des applications. style de développement pour toute l'équipe à suivre. Tout cela conduit à une qualité de résultat inférieure, déterminée par des compromis, après une migration progressive. Il est également plus difficile de maintenir un tel projet que celui-ci après une réécriture complète.

Les deux options existantes ont des limites et des contraintes, mais nous devons encore en choisir un si la migration est requise . Cependant, ce choix devrait-il être aussi douloureux? Ne serait-il pas formidable de combiner les meilleures parties des deux, tout en minimisant les effets secondaires négatifs? Est-ce possible?

Permettez-moi de vous présenter Frankenstein Migration

Frankenstein Migration. Partie 1: Théorie

Cette partie de la série explique ce qu'est la migration Frankenstein. Nous allons découvrir en quoi cela diffère des autres types de migration. Nous allons également nous intéresser davantage à la théorie des technologies et des approches qui rendent même ce type de migration possible.

Pourquoi "Frankenstein"?

Le nom vient de la façon dont l’approche fonctionne. En substance, il fournit une feuille de route pour deux applications ou plus, écrites dans des cadres totalement différents, afin de fonctionner comme un corps solide et bien orchestré. Tout comme Victor Frankenstein a construit son monstre dans le livre de Mary Shelley «Frankenstein; ou Le Prométhée moderne ».

 Les gens ont tendance à avoir peur des monstres. Typiquement.
Les gens ont tendance à avoir peur des monstres. Typiquement. ( Image agrandie )

N'oubliez pas que des personnes et des organisations différentes ont récemment étudié de manière indépendante le problème de la combinaison de plusieurs cadres dans le même projet: Micro Frontends Allegro Tech etc. Frankenstein Migration, cependant, est une approche indépendante et structurée de la migration en premier lieu.

Il existe deux technologies / approches fondamentales au cœur de la migration Frankenstein:

Architecture des microservices

L’idée principale derrière les microservices ( contrairement à l’architecture monolithique ) est que vous concevez votre application à l’aide de services isolés et indépendants dédiés à un petit travail particulier [19659005] Je répète ce que vous devez garder à l'esprit:

  • «indépendant»
  • «un seul emploi»
 L'architecture de Microservices est un ensemble de services indépendants qui sont tous connectés twork.
L’architecture de Microservices est un ensemble de services indépendants qui sont tous connectés à un réseau. ( Grand aperçu )

Dans une application, ces services sont connectés à un réseau de communication qui permet d'ajouter, de supprimer ou de remplacer de nouveaux services à tout moment. C'est ce que nous appelons des «microservices». L'approche flexible est bien établie et largement adoptée par les architectes de back-end et de serveurs. Pouvons-nous cependant de véritables microservices sur le front-office ?

Voyons les principales caractéristiques du service dans une telle architecture :

  • De petite taille,
  • Limité par les contextes,
  • Construit et publié avec des processus automatisés,
  • Développé de manière autonome et
  • pouvant être déployé indépendamment.

Les trois premiers points ne constituent pas un problème pour les technologies front-end. Presque tous les frameworks et bibliothèques modernes fournissent un type d'abstraction répondant à ces trois exigences. Cependant, l'indépendance des services pour le développement et le déploiement a toujours été un problème pour les technologies front-end. Même dans le paysage des cadres modernes qui fournissent le paradigme d’un composant (comme React ou Vue), ces composants restent généralement très dépendants du système et ne peuvent pas être autonomes ou indépendants de le cadre qui les a initialisé. Vous pouvez toujours vous replier sur iframe bien sûr, et obtenir ce niveau d'indépendance. Cependant, trouvons une alternative meilleure – pas si radicale –

Il existe un type de composant qui se rapproche de ce niveau d’indépendance, à savoir les composants Web. Il s’agit donc du deuxième élément constitutif de la migration Frankenstein.

Composants Web

Les gens disent que il suffit de mentionner “Composants Web” pour commencer un combat de nos jours. Des personnes comme Rich Harris même écrivent des billets de blog expliquant pourquoi ils n’utilisent pas les composants Web. Cependant, le but de cet article n'est pas de vous convaincre de l'utilité des composants Web ni de lancer un débat animé sur le sujet. Web Components n'est pas un outil make-everything-OK . Comme pour tout autre outil, il peut y avoir des limites et des effets secondaires possibles.

Serhii Kulykov fournit une série d'articles de meilleure qualité sur le sujet et prépare également un . “Référentiel de composants Web” dans lequel vous pouvez trouver beaucoup plus d’informations pour la discussion générale sur les composants Web. Cependant, en ce qui concerne Frankenstein Migration, les composants Web se révèlent être un instrument très approprié.

Jetons un coup d'œil sur les principaux éléments de Web Components qui en font des candidats appropriés pour combler les lacunes des microservices. adoption par le client:

En particulier, le DOM Shadow est l’outil capable de résoudre les problèmes que nous rencontrons généralement dans la migration progressive et fournit un mécanisme d’encapsulation réel pour le CSS du composant. Auparavant, nous avions indiqué que la gestion d'une cascade de fichiers CSS posait un problème lorsque nous essayions d'utiliser côte à côte des composants écrits avec différents frameworks ou bibliothèques dans la portée globale.

Voyons maintenant comment le DOM Shadow résout ce problème. ] Portée CSS vs encapsulation. Le style Shadow DOM

Le mécanisme d’encapsulation de Shadow DOM est essentiel à la compréhension car il diffère de la façon dont des outils populaires tels que CSS Modules ou ont été étendus dans Attribut dans Vue travail. Ces outils permettent de définir la portée des styles définis dans un composant sans rompre les styles globaux ni les autres composants. Cependant, ils ne protègent pas les composants des styles globaux s'infiltrant dans le composant (le problème même de la cascade évoqué ci-dessus) et, partant, susceptibles de briser vos composants.

En même temps, les styles définis dans Shadow Les DOM ne sont pas seulement étendus au composant actuel, ils sont également protégés des styles globaux qui n’ont pas d’accès explicite aux éléments internes du Shadow DOM, quelle que soit leur spécificité. Pour le voir en action, jetez un coup d'œil à l'exemple mis à jour :

Ici, nous avons déplacé les styles du composant Vue, directement dans le DOM Shadow et c'est ce qui se produit (automatique) lorsque vous définissez vos composants Vue pour fonctionner dans Shadow DOM . Cet exemple montre que le DOM Shadow fournit un mécanisme pour des composants réellement indépendants pouvant être utilisés dans n'importe quel contexte (bibliothèque, infrastructure) tout en préservant l'aspect et la fonctionnalité de ces composants.

Parlons maintenant des principaux concepts et étapes de la migration Frankenstein. pour voir comment exactement microservices et composants Web nous aident à migrer des applications frontales.

Supposons que vous souhaitiez migrer vers un autre framework.

 Notre projet de démonstration ne fonctionne pas si bien. -tout autre framework que nous souhaitons migrer.
Notre projet de démonstration sur un framework pas si chaud que nous souhaitons migrer. ( Image agrandie )

Peu importe le cadre / la bibliothèque vers lequel on émigre et le cadre / la bibliothèque auquel on souhaite accéder; le principe et les étapes sont les mêmes pour plus ou moins n'importe quel outil que vous choisissez (certaines exceptions génériques sont mentionnées plus loin dans l'article). C'est pourquoi on a appelé la migration Frankenstein l'approche dite du "framework-agnostic".

Maintenant, par où commencer?

  1. Identify Microservices
  2. Autorisez l'accès hôte à étranger . ] Écrire une composante extraterrestre
  3. Écrire un wrapper de composant Web autour du service Alien
  4. Remplacer le service hôte par une composante Web
  5. de Rinse And Répétez
  6. Basculez vers un étranger

1. Identify Microservices

C’est l’étape essentielle, essentielle au succès ou à l’échec du processus. Nous devrions donc plonger plus en profondeur ici.

Techniquement, nous devons scinder notre application existante en microservices pratiquement . C’est un processus entièrement subjectif et n’a pas de réponse «correcte». Cependant, que signifie-t-il en pratique alors?

Par "pratiquement", je veux dire qu'en général, vous n'avez pas besoin de modifier physiquement votre application existante: il suffit de disposer une structure, quelle que soit sa forme, même sur papier.

Dans notre application actuelle, nous devons séparer clairement les services suivants:

  • Indépendant;
  • Dédié à un petit travail

Un champ de saisie permettant d'ajouter de nouveaux éléments à une base de données pourrait constituer un exemple. d'un service: il est dédié à un travail particulier (ajout de nouveaux éléments) et effectue le travail sans dépendre d'un autre service. Sinon, la liste complète des éléments déjà ajoutés à la base de données: sa fonctionnalité est triviale et, encore une fois, ne dépend pas d’autres composants pour la liste des éléments . Cela ne semble pas trop compliqué, je pense, mais cela pourrait être un sentiment trompeur.

Commençons par les parties faciles: si un cadre dans votre projet actuel est basé sur le concept de «composant» (React, Vue) , vous avez probablement déjà une base raisonnable pour ce type de migration. Vous pouvez traiter chaque composant de votre application comme un service distinct dans une architecture de microservices.

Si votre projet repose actuellement sur une base héritée (par exemple, jQuery), vous devez activer votre imagination et réfléchir à la façon dont vous . voudrait structurer votre application en suivant les principes d'indépendance et de travail unique par service de microservices.

 Nous pouvons structurer notre application comme bon nous semble tant que nous suivons les principes de microservices.
Nous pouvons structurer notre application comme bon nous semble, à condition de respecter les principes de microservices. ( Grand aperçu )

Un refactor si nécessaire

Je déteste ma capacité à répéter des choses plusieurs fois, mais dans ce cas, cela a beaucoup de sens: assurez-vous que vos services (ou composants, ou conteneurs) , ou ce que vous préférez appeler vos blocs de construction) ne dépendent pas d’autres services. Sinon, les deux services doivent être traités comme un seul, dans un souci d'indépendance et d'isolement.

Un simple test pour vous assurer que votre service est correctement indépendant : Supprimez le code HTML de votre composant / service du Hébergez et rechargez l'application. S'il n'y a pas d'erreur JS dans la console et que la partie restante de l'application fonctionne comme prévu, le service en question est probablement assez indépendant du reste de l'application.

Pour vous donner une meilleure explication, considérons exemple suivant hérité artificiellement simplifié:

index.html

    index.js

     const form = document.getElementById ("form");
    form.addEventListener ("submit", ev => {
      ev.preventDefault ();
      const list = document.getElementById ("list");
      const input = document.getElementById ("inputTodo");
      const newEntry = document.createElement ("li");
      newEntry.innerHTML = input.value;
      input.value = "";
      listing.prepend (newEntry);
      list.classList.remove ("d-none");
    

    Ici, #form s'attend à ce que #listing soit présent dans le balisage lorsque son gestionnaire submit met la liste à jour directement. Par conséquent, ces deux facteurs dépendent l'un de l'autre et nous ne pouvons pas les diviser en services distincts: ils font partie du même travail et s'aident mutuellement à atteindre le même objectif.

    Cependant, comme éventuellement meilleure solution de rechange , nous pourrions refactoriser ce code pour rendre les deux composants indépendants l’un de l’autre et satisfaire à l’exigence d’indépendance:

    index.js

     function notifyAboutNewItem (ev) {
      ev.preventDefault ();
      const input = document.getElementById ("inputTodo");
      const event = new CustomEvent ("new-todo", {detail: {val: input.value}});
      document.dispatchEvent (event);
      input.value = "";
    }
    function updateList (ev) {
      const list = document.getElementById ("list");
      const newEntry = document.createElement ("li");
      newEntry.innerHTML = ev.detail.val;
      listing.prepend (newEntry);
      list.classList.remove ("d-none");
    }
    
    document.getElementById ("formulaire"). addEventListener ("submit", notifyAboutNewItem);
    document.addEventListener ("new-todo", updateList); 

    Nos composants #form et #listing ne communiquent pas entre eux directement, mais par le biais de l'événement DOM. (il peut s'agir plutôt d'une gestion d'état ou de tout autre mécanisme de stockage avec notification): lorsqu'un nouvel élément est ajouté, notifyAboutNewItem () envoie un événement, tandis que nous nous abonnons #listing pour écouter. à cet événement. Maintenant, n'importe quel composant peut envoyer cet événement. De plus, n'importe quel composant peut l'écouter: nos composants sont devenus indépendants les uns des autres et nous pouvons donc les traiter séparément dans notre migration.

    Trop petit pour un service?

    Une autre chose à garder Gardez à l'esprit que lorsque vous divisez votre application avec des composants existants (tels que React ou Vue) en services, certains de vos composants risquent d'être trop petits pour assurer un service correct. Cela ne veut pas dire qu'ils ne peuvent pas être petits, car rien ne vous empêche de structurer votre application aussi atomiquement que vous le souhaitez, mais la plupart des composants d'interface utilisateur réutilisables (comme le bouton de formulaire ou le champ de saisie dans l'exemple précédent) sont mieux inclus dans Des services plus larges pour minimiser le travail à votre place.

    À plus grande échelle, vous pouvez aborder l'étape n ° 1 aussi chaotique que vous le souhaitez. Vous n’avez pas besoin de lancer Frankenstein Migration avec le plan global: vous pouvez commencer avec un seul élément de votre application . Par exemple, divisez certains complexes

    en services. Alternativement, vous pouvez structurer votre application une route ou page entière à la fois, puis peut-être que votre
    deviendra un service unique. Cela n’a pas beaucoup d’importance; toute structure est meilleure que l'application monolithique lourde et difficile à maintenir. Cependant, je suggérerais de faire attention à l'approche trop granulaire – c'est ennuyeux et ne vous donne pas beaucoup d'avantages dans ce cas.

    Ma règle de base: vous obtenez le meilleur flux de processus avec des services qui peuvent être migrés et poussés en production en une semaine. Si cela prend moins, vos services sont un peu trop petits. Si cela prend plus de temps, vous essayez peut-être de mâcher trop de gros morceaux, il est donc préférable de les scinder. Cependant, tout dépend de votre capacité et des besoins de votre projet.

    Après avoir scindé pratiquement votre application actuelle en services, nous sommes prêts à passer à l’étape suivante.

    2. Autoriser l'accès hôte à étranger

    Cela devrait bien entendu être un titre absolument flou. Nous n'avons pas non plus discuté de ce qui est Host et nous n'avons pas encore mentionné Alien . Commençons donc par les clarifier.

    Nous avons mentionné que les services dans notre application actuelle devraient être indépendants. Cependant, ce n’est pas le seul endroit où nous aspirons à l’indépendance. Contrairement à l'approche de migration progressive classique, où nous mettons tout dans le même pot et développons de nouveaux composants avec les anciens, Frankenstein Migration nous demande de développer de nouveaux composants en dehors de l'application actuelle.

    Ours avec moi.

    , dans l'article, nous allons utiliser le mot Host pour faire référence à l'application actuelle, écrite avec le cadre que nous sommes sur le point de migrer . . Dans le même temps, la nouvelle application, écrite avec le cadre que nous migrons de à s'appellera Alien car elle injectera ses services dans Host à un moment donné.

     Host - est notre application actuelle; Alien - notre application migrée sur le nouveau cadre
    «Hôte» est notre application actuelle, tandis que «Alien» est notre application migrée sur le nouveau cadre. ( Grand aperçu )

    Oui, nous ne considérons pas Alien comme un ensemble de composants, mais comme une application appropriée que nous construisons au fil du temps. Techniquement, Host et Alien doivent être deux applications complètement différentes écrites avec le framework de votre choix, avec leurs propres dépendances, leurs outils de groupement, etc. Il est essentiel d'éviter les problèmes typiques de migration progressive, mais cette approche présente un avantage supplémentaire non négligeable. En gardant Host et Alien indépendants, les deux systèmes sont déployables à tout moment – si nous en avons besoin à un moment de la migration.

    Il existe plusieurs façons d'organiser Host et Alien:

    • Différents domaines ou Adresses IP
    • Différents dossiers sur votre serveur
    • Les sous-modules git ;
    • Et ainsi de suite.

    La condition première de tout scénario que vous choisissez est que l'hôte doit avoir accès aux actifs d'Alien. Donc, si vous choisissez de travailler avec différents domaines, vous devez jeter un coup d'œil à la configuration de de CORS pour votre domaine Alien. Si vous décidez de l'organiser aussi simplement que différents dossiers sur votre serveur, assurez-vous que les ressources du dossier de l'hôte ont accès au dossier de Alien. Si vous allez avec le sous-module git, avant d'ajouter Alien comme sous-module de votre hôte, assurez-vous de lire la documentation et savoir comment cela fonctionne: ce n'est pas aussi difficile que cela puisse paraître.

     L'hôte devrait avoir accès à Alien
    L'hôte devrait avoir accès à Alien. ( Grand aperçu )

    Une fois que vous avez configuré vos applications et fourni un accès entre hôte à Alien, les choses se passent plutôt bien.

    3. Write An Alien Component

    Je crois que le titre devrait être explicite. À ce stade, nous avons:

    • Un aperçu clair des services de notre application hôte,
    • La configuration de base d'application pour Alien et
    • a autorisé l'accès aux ressources de Alien à partir de l'hôte.

    Il est maintenant temps de choisissez d'abord un service hôte que vous souhaitez migrer et réécrivez ce service dans une application Alien, à l'aide du nouveau cadre. Gardez à l'esprit: nous n'attendons pas que l'ensemble de l'application soit réécrit comme s'il s'agissait d'une «réécriture complète». Nous migrons au lieu de cela, comme dans la migration progressive.

    La prochaine partie pratique du L'article contiendra plus de détails sur les astuces sur la façon d'écrire votre composant Alien pour une intégration plus facile. Cependant, pour l'instant, vous pouvez vous poser une question:

    Si Alien et Host sont des systèmes totalement différents, comment pouvons-nous alors intégrer notre service Alien nouvellement écrit dans Host?

    deuxième composante de l'approche: les composants Web.

    4. Écrire un wrapper de composants Web autour d'un service étranger

    Le wrapper de composants Web est au cœur de notre partie intégration. Avant d’en dire davantage sur ce sujet, il convient de garder à l’esprit quelques points:

    1. Tout d’abord, vous êtes libre de choisir n’importe quelle couche d’abstraction de votre composant Web. Vous pouvez choisir lit-element Stencil ou vraiment tout ce qui vous donne des composants Web à la fin. Cependant, les composants Web dont nous avons besoin pour la migration Frankenstein sont si purs (ce ne sont que des wrappers et rien de plus) que je pense que l'utilisation d'une couche d'abstraction pour cela est excessive.
    2. Deuxièmement, votre wrapper de composant Web vit du côté de l'hôte. . Ainsi, en fonction des besoins et des exigences de votre hôte, vous devez décider vous-même si vous devez ou non polyfiller des composants Web. Il suffit de vérifier le support pour deux technologies sur lesquelles nous allons nous appuyer:
      1. Shadow DOM et
      2. Custom Elements .

        Le support pour les deux est assez similaire, et avec Basculement entre Edge et Chromium dans la version 75, la prise en charge native des composants Web dans les navigateurs est très impressionnante. Nevertheless, should you need the polyfills to run your Web Components in IE11, for example, take a look at the stable polyfill.

    Web Component is a pure wrapper around Alien service
    Web Component is a pure wrapper around Alien service. (Large preview)

    The main functions of our Web Component wrapper:

    • Setting up a boilerplate for a new Custom Element with Shadow DOM;
    • Importing our Alien component;
    • Rendering Alien component within Shadow DOM of the wrapper;
    • Importing relevant styles and putting them in the Shadow DOM together with the Alien component itself (only if required by the Alien component).

    As a sneak-preview of how such component can feel like, take a look at the very basic example of importing a React component (HeaderApp) into Web Component wrapper (frankenstein-header-wrapper):

    import React from "../../react/node_modules/react";
    import ReactDOM from "../../react/node_modules/react-dom";
    import HeaderApp from "../../react/src/components/Header";
    
    class FrankensteinWrapper extends HTMLElement {
      connectedCallback() {
        const mountPoint = document.createElement("div");
        this.attachShadow({ mode: "open" }).appendChild(mountPoint);
        ReactDOM.render(mountPoint);
      }
    }
    customElements.define("frankenstein-header-wrapper", FrankensteinWrapper);

    Note: Take a closer look at the imports. We do not install React in our Host but instead import everything from Alien’s location with all of its dependencies. In this case, Alien has been added to Host as a git submodule and hence is visible to Host as a sub-folder that makes accessing its contents from Host a trivial task. Here, Alien is still a separate entity that is independent from Host though. It should explain the importance of Step #2 where we allowed access from Host to Alien.

    That’s pretty much it for the functions of the wrapper. After you wrote your Web Component, imported your Alien service and rendered it within the Web Component, we need to replace our Host service with our Web Component (that brings Alien service with itself).

    5. Replace Host Service With Web Component

    This step is very trivial, I believe. What you need to do effectively is replace markup of your Host service with your Web Component. The next chapter will cover different ways of setting up communication between your Host and Alien (that sits within Web Component) components, but in essence, there is no rocket science here:

    1. We have to connect both services to the same storage;
    2. We have to dispatch and listen (on both sides) to events when storage gets updated.
    After we’ve wrapped Alien’s service with the Web Component wrapper, it’s time to replace the corresponding Host service with the wrapper
    After we’ve wrapped Alien’s service with the Web Component wrapper, it’s time to replace the corresponding Host service with the wrapper. (Large preview)

    This schema should be the same no matter whether you have a state management system(s), route your communication through localStorage, or communicate with simple DOM events. By replacing your Host service with the Web Component wrapper, you finish the migration of the service and can enjoy this cute Frankenstein in your project.

    However, it doesn’t smell like a real migration just yet. There has to be something else to it.

    6. Rinse And Repeat

    After you’ve migrated your first service, you need to go through Steps 3 to 5 for all of your services/components. All the principles and recommendations remain valid. Just continue evolving your Alien as if you do a complete re-write: you’re working on a new application in parallel with your Host. You have to be able to start and build your Alien at any time and any way you want. The only difference now is that you can push your Alien services into production on Host whenever they are ready.

    At some point, you get all your services migrated, but you won’t have Host services anymore because all of them are replaced with Web Component wrappers that containing Alien services. Technically speaking, you get Alien application with remaining glue from Host. You could leave your application like this, but it’s not performant (we discuss performance tips and tricks in one of the next parts of the article) and looks quite messy, to be honest. There is a better way.

    When all of the Host services got replaced with Web Component wrappers, our Host resembles Alien and it’s time for a simple trick
    When all of the Host services got replaced with Web Component wrappers, our Host resembles Alien and it’s time for a simple trick. (Large preview)

    I have to repeat the core idea: “At this point, you have Alien application with remaining glue from Host.” It means that instead of serving our users this not-so-cute-anymore Frankenstein, we can serve real Alien instead of Host. At this moment, Alien should represent precisely the same picture as we have in Host, but orchestrated by Alien’s natural means and without any Web Components. The only question is: “How do we do that?”

    7. Switch To Alien

    Remember when we said that an independence of Host and Alien is essential for this type of migration, and so we split them into two separate applications? Well, now it’s time to enjoy the benefits of that decision.

    If you kept your Host and Alien independent after all Host services got replaced, you should be able to switch your server configuration to serve requests from Alien and forget about Frankenstein
    If you kept your Host and Alien independent after all Host services got replaced, you should be able to switch your server configuration to serve requests from Alien and forget about Frankenstein. (Large preview)

    I assume you serve your Host with a configurable web server. By “configurable”, I mean that you have control over the configuration file of your server. It allows you to control routing to your site.

    If this assumption is correct, you should be able to switch your server to serve requests from your Alien’s folder instead of Host for all incoming HTTP requests. For example, in your Apache’s httpd.confif you used git submodule for adding a React application to your Host, you should be able to update DocumentRoot.

    For example, the default setting:

    DocumentRoot "/var/www/html"

    becomes something like:

    DocumentRoot "/var/www/html/react/dist"

    That’s it! From now on, we’re directing HTTP traffic to our React subfolder.

    When this configuration is confirmed to be working and your users are served your fully migrated Alien application instead of your Host, your Alien becomes your new Host. Now, the old Host and all of its Frankenstein parts (including the Web Component wrappers) are not needed anymore and can be safely thrown away! Your migration is over.

    Conclusion

    All in all, Frankenstein Migration — is an attempt to combine “good” and “fast” migration types in which we get high-quality results such as the complete re-write that is combined with the delivery speed of gradual migration. This way, we’re able to deliver migrated services to the end-users as soon as the services are ready.

    I realize that the ideas in this article may feel provoking for some readers. Others may feel like we’re overdoing things. Keep in mind that this type of migration still needs testing with as many possible frameworks, libraries, and their combinations. The next part of this article is going to show practical examples of this approach along with code examples and git repositories for you to play with at your own pace. We wouldn’t want people to form a false opinion by claiming that it’s not going to work without even trying, would we?

    Frankenstein, even if cute, has to go for now. See you in the next part, Frank.
    Frankenstein, even if cute, has to go for now. See you in the next part, Frank. (Large preview)
    Smashing Editorial(dm, yk, il)






    Source link