Comment nous avons scié le monolithe. Partie 2, Frame Manager

Bonjour, je m'appelle Stas, je travaille dans l'équipe Tinkoff Business. Dans le dernier article, ma collègue Vanya a expliqué comment l'architecture de notre application est organisée . À plusieurs reprises, Vanya a mentionné un certain Frame Manager, qui sert d'orchestrateur d'application, et maintenant je vais vous en parler plus en détail.



image



Qu'est-ce que Tinkoff Business



Tinkoff Business propose des solutions pour les petites et moyennes entreprises: un projet de salaire, des services de liquidités et de règlement, un concepteur de documents et une vingtaine d'autres produits.

Tout cela est mis en œuvre dans les applications. Ces applications sont développées par des équipes distinctes et ont leurs propres cycles de publication. Et toutes ces applications fonctionnent avec une seule autorisation, contiennent une partie commune de la logique métier dans une bibliothèque distincte et utilisent des composants d'interface utilisateur communs.



Revenons en arrière il y a 2 ans



Une application Tinkoff Business typique ressemblait à ceci:



image



Ci-dessus - un en-tête avec navigation dans l'application, et à droite - une barre latérale avec la navigation du produit.

À l'époque, l'idée d'un microfront n'était pas encore si populaire, mais nous allions déjà dans cette direction: la barre latérale était une application Angular distincte. L'application principale a chargé la barre latérale dans une iframe, ce qui a permis à l'application d'être publiée indépendamment.



Cette approche avait ses inconvénients: lors du passage d'un produit à l'autre, il fallait attendre un chargement complet de la page avec deux applications Angular. Et comme la plupart des applications utilisent les mêmes requêtes d'API backend, les utilisateurs ont dû attendre leur ré-exécution.



Idée Frame Manager



Nous avons vécu avec une telle architecture jusqu'à ce qu'une tâche globale pour toutes les applications apparaisse: la refonte. Puis l'idée est venue: pourquoi ne pas faire une sorte d'inversion de contrôle et au lieu que les applications chargent la barre latérale à l'intérieur d'elles-mêmes, la barre latérale chargerait les applications elle-même?



Cela a permis de préserver les avantages de l'architecture actuelle et de se débarrasser des problèmes ci-dessus (et d'en apporter de nouveaux, haha).



Prototype initial



Au départ, nous avons créé un prototype avec les fonctionnalités minimales requises pour charger d'autres applications. Un domaine distinct a été créé sur le banc d'essai. Les routes dans nginx pour la statique des applications ont changé: auparavant , la statique des applications correspondantes était chargée le long des chemins / sme , / compte , / salaire , etc. Désormais, les statiques de Frame Manager ont été envoyées le long de tous les chemins et le suffixe / statique a été ajouté aux routes statiques des applications elles-mêmes .



Pour clarifier les choses, regardons un exemple: vous devez charger une application située sur le chemin / some-app avec some-route . Laissant de côté les détails, voyons quel processus se produit lors du chargement de / some-app / some-route / :

  1. Nginx envoie des statiques Frame Manager le long de ce chemin.
  2. Frame Manager est en cours de chargement. Sur la base de l'itinéraire, il comprend de télécharger une application .
  3. Un iframe est créé avec src = '/ some-app / static /' où l'application principale est chargée.


Dans le même temps, des améliorations significatives étaient nécessaires dans les applications elles-mêmes. Par conséquent, nous avons forké le maître de la branche d'application et y avons ajouté les modifications nécessaires, après quoi nous avons généré des copies individuelles des applications elles-mêmes avec les modifications apportées.



Premiers problèmes



Nous avons donc transféré 4 applications vers Frame Manager et nous nous sommes assurés que la solution fonctionne. Toutes les autres applications devaient être traduites. Et ici, nous avons rencontré un problème: il s'est avéré trop coûteux de maintenir simultanément la version régulière et la version pour travailler avec Frame Manager.



Nous devions constamment mettre à jour de nouvelles versions d'applications pour chaque changement du maître des anciennes versions, résoudre les conflits émergents, les fonctionnalités existantes étaient souvent cassées, le coût des tests de régression presque doublé - tout cela prenait trop de temps. Il était clair qu'une nouvelle solution était nécessaire.



Améliorations



Si une version de l'application pouvait fonctionner à la fois avec la barre latérale et Frame Manager, cela nous éviterait beaucoup de problèmes. Voyons ce qui peut être fait.



Tout d'abord, vous devez déterminer d'une manière ou d'une autre si l'application s'exécute dans Frame Manager. C'est assez simple: vous devez comparer les références de window.top et window.self. S'ils ne sont pas égaux, alors nous sommes dans un cadre, c'est-à-dire dans le gestionnaire de cadres! Mais, s'il existe des applications qui s'ouvrent dans une iframe par défaut, vous devez ajouter une logique supplémentaire. Nous avions donc une application de widget qui s'ouvrait initialement dans un cadre et commençait à supposer qu'elle se trouvait toujours dans le gestionnaire de cadres, c'est pourquoi elle a cassé dans l'ancien mode.



Examinons maintenant de plus près les changements nécessaires dans les applications et comment vous pouvez prendre en charge le travail dans deux modes:

  1. url. iframe, . , - , — . url’ Frame Manager, . .

  2. . Frame Manager’ . : . . , , , post messages custom events. Frame Manager.

  3. . , Frame Manager. , Angular .

  4. . , , TCS, config.js . Frame Manager .

  5. base href. nginx, base href ( /static/). : , base href , . , , , , base href, .

  6. Autorisation. Pour l'autorisation dans toutes les applications, un script distinct est utilisé, qui est intégré dans index.html. Dans la nouvelle version, ce script est intégré dans Frame Manager et sa réutilisation dans les applications entraînera des erreurs. Vous pouvez modifier la logique du script pour qu'elle soit ignorée si l'application est chargée dans Frame Manager.



Ce sont toutes des solutions efficaces, mais elles ne sont pas assez flexibles. De nouvelles branches ont été ajoutées avec une logique qui doit également être maintenue à différents endroits. En général, tout ressemble à une structure trop compliquée et plutôt instable.



Réinventer l'iframe



Ensuite, j'ai eu l'idée de pirater un peu le processus de chargement de l'application index.html. Au lieu de charger l'application dans une iframe spécifiant l'attribut src, vous pouvez faire une requête xhr pour index.html, obtenir la page sous forme de texte, la traiter et la charger dans l'iframe. Cela vous donnera un contrôle complet sur l'application chargée: cela vous permettra de définir le href de base, de supprimer les scripts inutiles, les styles de patch, les variables de remplacement et bien plus encore!



Oui, le patching mokey est découragé par les développeurs et est considéré comme une mauvaise pratique, mais si l'équipe Angular l'utilise dans la bibliothèque zone.js, comment allons-nous pire? Des doutes sur les performances peuvent survenir: l'analyse HTML ressemble à une opération coûteuse. Mais, en règle générale, la page de démarrage d'une application Angular ne dépasse pas 50 lignes, et dans tous les navigateurs (même IE 10!) Il existe une api pratiqueDOMParser , qui vous permet d'obtenir le DOM à partir d'une chaîne.



Jetons un coup d'œil à ce que fait le Frame Manager lors du chargement de l'application (le Frame Manager lui-même est déjà chargé):

  1. En fonction du chemin, charge l'index.html de l'application.

  2. Analyse la page, la convertit en DOM, supprime les scripts inutiles en mémoire, remplace la base href, les variables globales par des configurations et des styles.

  3. Crée un élément iframe qui écrit le document résultant (reconverti en chaîne) à l'aide de document.write ().
  4. Place l'application sur un chemin auquel elle doit être câblée. Il alimente également les modèles nécessaires au fonctionnement de la logique métier via le service d'échange de données.



Ainsi, parmi les six changements de logique nécessaires ci-dessus, seul le premier (synchronisation url) doit être implémenté à l'intérieur de l'application, le reste est pris en charge par le Frame Manager!



Qu'est-ce qui a



Complètement changé l'apparence de l'application, pratiquement sans apporter de modifications au code de l'application elle-même.

Avant. La barre latérale est entourée de rouge. Intégré dans un iframe
sidebar



Après. Frame Manager est surligné en rouge. Application chargée dans l'iframe
frame manager



Vous avez la possibilité de remplacer ou d'ajouter des variables globales et des styles.

Par exemple, voici à quoi ressemble la configuration de style de l'application
export const business = {
    'sidebar.b-main__sidebar': {
        display: 'none'
    },
    '.b-main': {
        'margin-left': '260',
        position: 'relative',
        display: 'block',
        width: '1104px',
        'min-height': '100vh',
        margin: '0 auto'
    }
};




Et donc - la configuration de l'application elle-même
{
        id: 'products',
        name: ' ',
        icon: 'products',
        frameSupported: true,
        applications: [
            {
                id: 'products',
                path: '/products',
                apiPrefix: '/products',
                hasMenuConfig: true,
                dynamicCompanyChange: true,
            }
        ]
    }




Dans le même temps, les configurations sont dans un référentiel distinct du Frame Manager, ce qui vous permet de modifier certains paramètres du travail de l'application sans les relâcher.



Nous avons également créé des transitions transparentes entre les applications, effectué l'autorisation dans Frame Manager. Nous avons réalisé qu'en raison du partage de données entre Frame Manager et les applications, aucune demande inutile n'est faite.



Pas sans problèmes: certains plugins Chrome (CryptoPro, redux devtools) ont cessé de fonctionner dans l'application téléchargée, car le lien vers la fenêtre a été perdu lors de l'interaction. Des améliorations supplémentaires étaient nécessaires.



En conséquence, à la fin de 2019, nous avons transféré avec succès toutes les applications vers Frame Manager, et la barre latérale est tombée dans l'oubli. Mais le travail sur le Frame Manager s'est poursuivi, et une nouvelle question s'est posée: est-il possible d'améliorer et d'optimiser d'une manière ou d'une autre le travail du frontend dans Tinkoff Business? Il s'est avéré que vous pouvez! Mais plus à ce sujet dans le prochain article.



All Articles