Histoire de l'architecture Dodo IS : un ancien monolithe

Ou chaque entreprise malheureuse avec un monolithe est malheureuse à sa manière.

Le développement du système Dodo IS a commencé immédiatement, tout comme l'activité Dodo Pizza, en 2011. Il était basé sur l'idée d'une numérisation complète et totale des processus métier, et seul, qui déjà en 2011 a suscité beaucoup de questions et de scepticisme. Mais depuis 9 ans maintenant, nous suivons cette voie - avec notre propre développement, qui a commencé avec un monolithe.

Cet article est une « réponse » aux questions « Pourquoi réécrire l'architecture et faire des changements aussi importants et à long terme ? retour à l'article précédent "Histoire de l'architecture Dodo IS : la voie du Back Office". Je vais commencer par comment le développement de Dodo IS a commencé, à quoi ressemblait l'architecture d'origine, comment de nouveaux modules sont apparus et à cause de quels problèmes des changements à grande échelle ont dû être apportés.

Histoire de l'architecture Dodo IS : un ancien monolithe

Série d'articles "Qu'est-ce que Dodo IS ?" raconte :

  1. Premier monolithe de Dodo IS (2011-2015). (Tu es là)

  2. Le chemin du back office : bases et bus séparés.

  3. Le cheminement côté client : façade sur socle (2016-2017). (En cours...)

  4. L'histoire des vrais microservices. (2018-2019). (En cours...)

  5. Fini le sciage du monolithe et stabilisation de l'architecture. (En cours...)

Architecture initiale

En 2011, l'architecture Dodo IS ressemblait à ceci :

Histoire de l'architecture Dodo IS : un ancien monolithe

Le premier module de l'architecture est l'acceptation des commandes. Le processus métier était :

  • le client appelle la pizzeria ;

  • le gérant décroche le téléphone ;

  • accepte une commande par téléphone ;

  • le renseigne en parallèle dans l'interface d'acceptation de commande : informations sur le client, données sur le détail de la commande, adresse de livraison sont prises en compte. 

L'interface du système d'information ressemblait à ceci...

Première version d'octobre 2011 :

Légèrement amélioré en janvier 2012

Dodo Pizza Système d'Information Livraison Pizza Restaurant

Les ressources pour le développement du premier module de prise de commande étaient limitées. Nous avons dû faire beaucoup, rapidement et avec une petite équipe. Une petite équipe, c'est 2 développeurs qui ont jeté les bases de tout le futur système.

Leur première décision a déterminé le sort de la pile technologique :

  • Backend sur ASP.NET MVC, langage C#. Les développeurs étaient dotnetchiki, cette pile leur était familière et agréable.

  • Frontend sur Bootstrap et JQuery : interfaces utilisateur sur des styles et des scripts auto-écrits. 

  • Base de données MySQL : pas de frais de licence, facile à utiliser.

  • Serveurs sur Windows Server, car .NET ne pouvait alors être que sous Windows (on ne parlera pas de Mono).

Physiquement, tout cela s'exprimait dans le « dedic chez l'hébergeur ». 

Architecture d'application de prise de commandes

Ensuite, tout le monde parlait déjà de microservices, et SOA a été utilisé dans de grands projets pendant 5 ans, par exemple, WCF est sorti en 2006. Mais ensuite, ils ont choisi une solution fiable et éprouvée.

La voici

Histoire de l'architecture Dodo IS : un ancien monolithe

Asp.Net MVC est Razor, qui, à la demande d'un formulaire ou d'un client, restitue une page HTML avec un rendu serveur. Sur le client, les scripts CSS et JS affichent déjà des informations et, si nécessaire, effectuent des requêtes AJAX via JQuery.

Les requêtes sur le serveur aboutissent dans les classes *Controller, où le traitement et la génération de la page HTML finale ont lieu dans la méthode. Les contrôleurs adressent des requêtes à une couche de logique appelée * Services. Chacun des services correspondait à un aspect de l'entreprise :

  • Par exemple, DepartmentStructureService donnait des informations sur les pizzerias, sur les départements. Un rayon est un groupe de pizzerias exploité par un seul franchisé.

  • ReceivingOrdersService a accepté et calculé la composition de la commande.

  • Et SmsService a envoyé des SMS en appelant les services API pour envoyer des SMS.

Les services ont traité les données de la base de données, stocké la logique métier. Chaque service avait un ou plusieurs *Dépôts avec le nom approprié. Ils contenaient déjà des requêtes vers des procédures stockées dans la base de données et une couche de mappeurs. Il y avait une logique métier dans les stockages, en particulier beaucoup dans ceux qui émettaient des données de rapport. ORM n'a pas été utilisé, tout le monde s'est appuyé sur sql écrit à la main. 

Il y avait aussi une couche du modèle de domaine et des classes d'assistance communes, par exemple, la classe Order qui stockait la commande. Au même endroit, dans la couche, il y avait une aide pour convertir le texte d'affichage en fonction de la devise sélectionnée.

Tout cela peut être représenté par un tel modèle :

Histoire de l'architecture Dodo IS : un ancien monolithe

Manière de commander

Envisagez une première manière simplifiée de créer une telle commande.

Histoire de l'architecture Dodo IS : un ancien monolithe

Au départ, le site était statique. Il y avait des prix dessus, et en plus - un numéro de téléphone et l'inscription "Si vous voulez une pizza - appelez le numéro et commandez". Pour commander, nous devons implémenter un flux simple : 

  • Le client visite un site statique avec des prix, sélectionne des produits et appelle le numéro indiqué sur le site.

  • Le client nomme les produits qu'il souhaite ajouter à la commande.

  • Donne son adresse et son nom.

  • L'opérateur accepte la commande.

  • La commande est affichée dans l'interface des commandes acceptées.

Tout commence par l'affichage du menu. Un utilisateur-opérateur connecté n'accepte qu'une seule commande à la fois. Par conséquent, le projet de panier peut être stocké dans sa session (la session de l'utilisateur est stockée en mémoire). Il existe un objet Cart contenant des informations sur les produits et les clients.

Le client nomme le produit, l'opérateur clique sur + à côté du produit, et une requête est envoyée au serveur. Les informations sur le produit sont extraites de la base de données et les informations sur le produit sont ajoutées au panier.

Histoire de l'architecture Dodo IS : un ancien monolithe

Noter. Oui, ici, vous ne pouvez pas extraire le produit de la base de données, mais le transférer depuis le frontend. Mais pour plus de clarté, j'ai montré exactement le chemin de la base de données. 

Ensuite, entrez l'adresse et le nom du client. 

Histoire de l'architecture Dodo IS : un ancien monolithe

Lorsque vous cliquez sur "Créer une commande":

  • La requête est envoyée à OrderController.SaveOrder().

  • Nous obtenons le panier de la session, il y a des produits dans la quantité dont nous avons besoin.

  • Nous complétons le panier avec des informations sur le client et le transmettons à la méthode AddOrder de la classe ReceivingOrderService, où il est enregistré dans la base de données. 

  • La base de données contient des tables avec la commande, la composition de la commande, le client, et elles sont toutes connectées.

  • L'interface d'affichage des commandes extrait les dernières commandes et les affiche.

Nouveaux modules

Prendre la commande était important et nécessaire. Vous ne pouvez pas faire un commerce de pizza si vous n'avez pas de commande à vendre. Par conséquent, le système a commencé à acquérir des fonctionnalités - environ de 2012 à 2015. Pendant ce temps, de nombreux blocs différents du système sont apparus, que j'appellerai modules, par opposition à la notion de service ou de produit. 

Un module est un ensemble de fonctions unies par un objectif métier commun. En même temps, ils sont physiquement dans la même application.

Les modules peuvent être appelés blocs système. Par exemple, il s'agit d'un module de reporting, d'interfaces d'administration, traqueur de nourriture dans la cuisine, autorisation. Ce sont toutes des interfaces utilisateur différentes, certaines ont même des styles visuels différents. En même temps, tout est dans le cadre d'une application, d'un processus en cours d'exécution. 

Techniquement, les modules ont été conçus comme Area (une telle idée est même restée en noyau asp.net). Il y avait des fichiers séparés pour le frontend, les modèles, ainsi que leurs propres classes de contrôleur. En conséquence, le système a été transformé à partir de cela ...

Histoire de l'architecture Dodo IS : un ancien monolithe

... dans ceci :

Histoire de l'architecture Dodo IS : un ancien monolithe

Certains modules sont implémentés par des sites séparés (projet exécutable), en raison d'une fonctionnalité complètement séparée et en partie en raison d'un développement légèrement séparé et plus ciblé. Ce:

  • site - première version site dodopizza.ru.

  • Exportations: téléchargement de rapports depuis Dodo IS pour 1C. 

  • Personnel - compte personnel du salarié. Il a été développé séparément et possède son propre point d'entrée et une conception distincte.

  • fs — un projet d'hébergement de statiques. Plus tard, nous nous en sommes éloignés, déplaçant toutes les statiques vers le CDN d'Akamai. 

Le reste des blocs se trouvait dans l'application BackOffice. 

Histoire de l'architecture Dodo IS : un ancien monolithe

Explication du nom :

  • Caissier - Caissier de restaurant.

  • ShiftManager - interfaces pour le rôle "Shift Manager": statistiques opérationnelles sur les ventes de pizzeria, possibilité de mettre des produits sur la liste d'arrêt, modifier la commande.

  • OfficeManager - interfaces pour les rôles "Pizzeria Manager" et "Franchisé". Voici les fonctions collectées pour la mise en place d'une pizzeria, ses promotions bonus, la réception et le travail avec les employés, les rapports.

  • PublicScreens - interfaces pour téléviseurs et tablettes suspendus dans les pizzerias. Les téléviseurs affichent les menus, les informations publicitaires, l'état de la commande à la livraison. 

Ils ont utilisé une couche de service commune, un bloc de classe de domaine Dodo.Core commun et une base commune. Parfois, ils pouvaient encore mener les transitions les uns vers les autres. Y compris les sites individuels, tels que dodopizza.ru ou personal.dodopizza.ru, sont allés aux services généraux.

Lorsque de nouveaux modules sont apparus, nous avons essayé de réutiliser au maximum le code de services, les procédures stockées et les tables déjà créés dans la base de données. 

Pour mieux comprendre l'ampleur des modules réalisés dans le système, voici un schéma de 2012 avec des plans de développement :

Histoire de l'architecture Dodo IS : un ancien monolithe

En 2015, tout était sur la carte et encore plus était en production.

  • L'acceptation des commandes est devenue un bloc séparé du centre de contact, où la commande est acceptée par l'opérateur.

  • Il y avait des écrans publics avec des menus et des informations accrochés dans les pizzerias.

  • La cuisine dispose d'un module qui lit automatiquement le message vocal "Nouvelle Pizza" lorsqu'une nouvelle commande arrive, et imprime également une facture pour le coursier. Cela simplifie grandement les processus en cuisine, permet aux employés de ne pas être distraits par un grand nombre d'opérations simples.

  • L'unité de livraison est devenue une caisse de livraison distincte, où la commande était passée au coursier qui avait précédemment pris le relais. Son temps de travail a été pris en compte pour le calcul de la masse salariale. 

En parallèle, de 2012 à 2015, plus de 10 développeurs sont apparus, 35 pizzerias ont ouvert, déployé le système en Roumanie et préparé l'ouverture de points de vente aux États-Unis. Les développeurs ne s'occupaient plus de toutes les tâches, mais étaient divisés en équipes. chacun spécialisé dans sa propre partie du système. 

Problèmes

Y compris à cause de l'architecture (mais pas seulement).

Chaos dans la base

Une base est pratique. La cohérence peut y être atteinte, et au détriment des outils intégrés aux bases de données relationnelles. Son utilisation est familière et pratique, surtout s'il y a peu de tables et peu de données.

Mais sur 4 ans de développement, la base de données s'est avérée avoir environ 600 tables, 1500 procédures stockées, dont beaucoup avaient également une logique. Hélas, les procédures stockées n'apportent pas beaucoup d'avantages lorsque vous travaillez avec MySQL. Ils ne sont pas mis en cache par la base, et y stocker de la logique complique le développement et le débogage. La réutilisation du code est également difficile.

De nombreuses tables n'avaient pas d'index appropriés, quelque part, au contraire, il y avait beaucoup d'index, ce qui rendait l'insertion difficile. Il a fallu modifier environ 20 tables - la transaction pour créer une commande pouvait prendre environ 3 à 5 secondes. 

Les données des tableaux n'étaient pas toujours sous la forme la plus appropriée. Quelque part il fallait faire de la dénormalisation. Une partie des données reçues régulièrement se trouvait dans une colonne sous forme de structure XML, cela augmentait le temps d'exécution, allongeait les requêtes et compliquait le développement.

Pour les mêmes tableaux ont été produits très demandes hétérogènes. Les tables populaires ont particulièrement souffert, comme la table mentionnée ci-dessus. passer commande ou tableaux pizzeria. Ils ont été utilisés pour afficher des interfaces opérationnelles dans la cuisine, des analyses. Un autre site les a contactés (dodopizza.ru), où à tout moment un grand nombre de demandes peuvent arriver soudainement. 

Les données n'ont pas été agrégées et de nombreux calculs ont eu lieu à la volée en utilisant la base. Cela a créé des calculs inutiles et une charge supplémentaire. 

Souvent, le code est allé dans la base de données alors qu'il n'aurait pas pu le faire. Quelque part il n'y avait pas assez d'opérations en masse, quelque part il faudrait répartir une requête en plusieurs via le code afin d'accélérer et d'augmenter la fiabilité. 

Cohésion et obfuscation dans le code

Les modules qui étaient censés être responsables de leur part de l'entreprise ne l'ont pas fait honnêtement. Certains d'entre eux avaient une duplication des fonctions pour les rôles. Par exemple, un marketeur local responsable de l'activité marketing du réseau dans sa ville devait utiliser à la fois l'interface "Admin" (pour créer des promotions) et l'interface "Office Manager" (pour visualiser l'impact des promotions sur le business). Bien sûr, à l'intérieur des deux modules utilisaient le même service qui fonctionnait avec des promotions bonus.

Les services (classes au sein d'un grand projet monolithique) pourraient s'appeler pour enrichir leurs données.

Avec les classes de modèles elles-mêmes qui stockent des données, le travail dans le code a été effectué différemment. Quelque part, il y avait des constructeurs à travers lesquels il était possible de spécifier les champs obligatoires. Quelque part, cela se faisait par le biais de propriétés publiques. Bien sûr, l'obtention et la transformation des données de la base de données étaient variées. 

La logique était soit dans les contrôleurs, soit dans les classes de service. 

Ceux-ci semblent être des problèmes mineurs, mais ils ont considérablement ralenti le développement et réduit la qualité, entraînant une instabilité et des bogues. 

La complexité d'un grand développement

Des difficultés sont apparues dans le développement lui-même. Il fallait faire différents blocs du système, et en parallèle. Intégrer les besoins de chaque composant dans un code unique devenait de plus en plus difficile. Il n'a pas été facile de se mettre d'accord et de plaire à toutes les composantes en même temps. À cela s'ajoutaient des limitations technologiques, notamment en ce qui concerne la base et le frontend. Il a fallu abandonner jQuery vers des frameworks de haut niveau, notamment en termes de services clients (site web).

Dans certaines parties du système, des bases de données plus adaptées à cela pourraient être utilisées.. Par exemple, plus tard, nous avons eu le cas d'utilisation du passage de Redis à CosmosDB pour stocker un panier de commandes. 

Les équipes et les développeurs impliqués dans leur domaine souhaitaient clairement plus d'autonomie pour leurs services, tant en termes de développement que de déploiement. Conflits de fusion, problèmes de publication. Si pour 5 développeurs ce problème est insignifiant, alors avec 10, et plus encore avec la croissance prévue, tout deviendrait plus sérieux. Et en avant devait être le développement d'une application mobile (ça a commencé en 2017, et en 2018 c'était grosse chute). 

Différentes parties du système nécessitaient différents niveaux de stabilité, mais en raison de la forte connectivité du système, nous ne pouvions pas fournir cela. Une erreur dans le développement d'une nouvelle fonction dans le panneau d'administration pourrait bien avoir eu lieu lors de l'acceptation d'une commande sur le site, car le code est commun et réutilisable, la base de données et les données sont également les mêmes.

Il serait probablement possible d'éviter ces erreurs et problèmes dans le cadre d'une telle architecture monolithique-modulaire : faire un partage des responsabilités, refactoriser à la fois le code et la base de données, séparer clairement les couches les unes des autres, surveiller la qualité au quotidien. Mais les solutions architecturales choisies et l'accent mis sur l'expansion rapide des fonctionnalités du système ont entraîné des problèmes de stabilité.

Comment le blog Power of the Mind a mis les caisses enregistreuses dans les restaurants

Si la croissance du réseau de pizzerias (et de la charge) se poursuivait au même rythme, au bout d'un moment les baisses seraient telles que le système ne remonterait pas. Illustre bien les problèmes auxquels nous avons commencé à faire face en 2015, voici une telle histoire. 

Dans le blog"Pouvoir mental” était un widget qui affichait des données sur les revenus pour l'année de l'ensemble du réseau. Le widget a accédé à l'API publique Dodo, qui fournit ces données. Cette statistique est actuellement disponible sur http://dodopizzastory.com/. Le widget a été affiché sur chaque page et a fait des demandes sur une minuterie toutes les 20 secondes. La demande est allée à api.dodopizza.ru et a demandé :

  • le nombre de pizzerias du réseau ;

  • chiffre d'affaires total du réseau depuis le début de l'année ;

  • revenus pour aujourd'hui.

La demande de statistiques sur les revenus est allée directement à la base de données et a commencé à demander des données sur les commandes, agrégeant les données à la volée et donnant le montant. 

Les caisses des restaurants sont allées au même tableau de commandes, ont déchargé une liste des commandes reçues pour aujourd'hui et de nouvelles commandes y ont été ajoutées. Les caisses enregistreuses faisaient leurs demandes toutes les 5 secondes ou lors du rafraîchissement de la page.

Le schéma ressemblait à ceci :

Histoire de l'architecture Dodo IS : un ancien monolithe

Un automne, Fyodor Ovchinnikov a écrit un article long et populaire sur son blog. Beaucoup de gens sont venus sur le blog et ont commencé à tout lire attentivement. Pendant que chacune des personnes venues lisait l'article, le widget de revenus fonctionnait correctement et demandait l'API toutes les 20 secondes.

L'API a appelé une procédure stockée pour calculer la somme de toutes les commandes depuis le début de l'année pour toutes les pizzerias du réseau. L'agrégation était basée sur la table des commandes, qui est très populaire. Toutes les caisses de tous les restaurants ouverts à ce moment-là s'y rendent. Les caisses ne répondaient plus, les commandes n'étaient pas acceptées. Ils n'étaient pas non plus acceptés depuis le site, n'apparaissaient pas sur le tracker, le chef de quart ne pouvait pas les voir dans son interface. 

Ce n'est pas la seule histoire. À l'automne 2015, chaque vendredi, la charge du système était critique. Plusieurs fois, nous avons désactivé l'API publique, et une fois, nous avons même dû désactiver le site, car rien n'y faisait. Il y avait même une liste de services avec un ordre d'arrêt sous de fortes charges.

Désormais, notre lutte contre les charges et pour la stabilisation du système commence (de l'automne 2015 à l'automne 2018). C'est alors que c'est arrivé"grande chute". De plus, des pannes se produisaient aussi parfois, certaines étaient très sensibles, mais la période générale d'instabilité peut maintenant être considérée comme révolue.

Croissance rapide de l'entreprise

Pourquoi n'a-t-on pas pu le faire tout de suite ? Regardez simplement les graphiques suivants.

Histoire de l'architecture Dodo IS : un ancien monolithe

Toujours en 2014-2015, il y avait une ouverture en Roumanie et une ouverture aux États-Unis était en préparation.

Le réseau s'est développé très rapidement, de nouveaux pays ont été ouverts, de nouveaux formats de pizzerias sont apparus, par exemple, une pizzeria a été ouverte au food court. Tout cela a nécessité une attention particulière à l'expansion des fonctions de Dodo IS. Sans toutes ces fonctions, sans suivi en cuisine, comptabilisation des produits et des pertes dans le système, affichage de l'émission d'une commande dans le hall de l'aire de restauration, nous ne parlerions guère de la "bonne" architecture et de la "bonne" approche de développement maintenant.

La crise de 2014 a constitué un autre obstacle à la révision rapide de l'architecture et à l'attention générale portée aux problèmes techniques. Des choses comme celle-ci ont durement touché les opportunités de croissance des équipes, en particulier pour une jeune entreprise comme Dodo Pizza.

Des solutions rapides qui ont aidé

Les problèmes nécessitaient des solutions. Classiquement, les solutions peuvent être divisées en 2 groupes :

  • Des rapides qui éteignent le feu et donnent une petite marge de sécurité et nous donnent le temps de changer.

  • Systémique et donc longue. Réingénierie d'un certain nombre de modules, division d'une architecture monolithique en services distincts (la plupart ne sont pas du tout des micro, mais plutôt des macro services, et il y a quelque chose à ce sujet Le rapport d'Andreï Morevski). 

La liste sèche des changements rapides est la suivante :

Mettre à l'échelle le maître de base

Bien sûr, la première chose à faire pour faire face aux charges est d'augmenter la capacité du serveur. Cela a été fait pour la base de données principale et pour les serveurs Web. Hélas, cela n'est possible que jusqu'à une certaine limite, puis cela devient trop cher.

Depuis 2014, nous sommes passés à Azure, nous avions d'ailleurs écrit sur ce sujet à l'époque dans l'article «Comment Dodo Pizza livre des pizzas à l'aide du cloud Microsoft Azure". Mais après une série d'augmentations du serveur pour la base, ils se sont heurtés au coût. 

Réplicas de base pour la lecture

Deux répliques ont été réalisées pour la base :

LireRéplica pour les demandes de références. Il est utilisé pour lire les répertoires, le type, la ville, la rue, la pizzeria, les produits (domaine lentement modifié) et dans les interfaces où un petit délai est acceptable. Il y avait 2 de ces répliques, nous avons assuré leur disponibilité au même titre que les masters.

ReadReplica pour les demandes de rapport. Cette base de données avait une disponibilité inférieure, mais tous les rapports y étaient allés. Laissez-les avoir de lourdes demandes de recalculs de données énormes, mais ils n'affectent pas la base de données principale et les interfaces opérationnelles. 

Caches dans le code

Il n'y avait aucun cache nulle part dans le code (du tout). Cela entraînait des requêtes supplémentaires, pas toujours nécessaires, vers la base de données chargée. Les caches étaient d'abord à la fois en mémoire et sur un service de cache externe, c'était Redis. Tout a été invalidé par le temps, les paramètres ont été spécifiés dans le code.

Plusieurs serveurs principaux

Le backend de l'application devait également être mis à l'échelle pour gérer les charges de travail accrues. Il était nécessaire de créer un cluster à partir d'un serveur iis. Nous avons reporté session de candidature de la mémoire vers RedisCache, ce qui a permis de faire plusieurs serveurs derrière un simple load balancer avec round robin. Au début, on utilisait le même Redis que pour les caches, puis il a été scindé en plusieurs. 

Du coup, l'architecture est devenue plus compliquée...

Histoire de l'architecture Dodo IS : un ancien monolithe

… mais une partie de la tension a été supprimée.

Et puis il a fallu refaire les composants chargés, ce que nous avons entrepris. Nous en parlerons dans la partie suivante.

Source: habr.com

Ajouter un commentaire