Storia architettonica di Dodo IS: un primo monolite

Oppure ogni azienda infelice con un monolite è infelice a modo suo.

Lo sviluppo del sistema Dodo IS è iniziato subito, così come l'attività Dodo Pizza, nel 2011. Si basava sull'idea della digitalizzazione completa e totale dei processi aziendali e da solo, che già allora nel 2011 suscitò molti interrogativi e scetticismo. Ma ormai da 9 anni seguiamo questa strada, con il nostro sviluppo, iniziato con un monolite.

Questo articolo è la “risposta” alle domande “Perché riscrivere l’architettura e apportare cambiamenti così su larga scala e a lungo termine?” all'articolo precedente “La storia dell’architettura Dodo IS: il percorso del back office”. Inizierò spiegando come è iniziato lo sviluppo di Dodo IS, come appariva l'architettura iniziale, come sono apparsi i nuovi moduli e a causa di quali problemi sono state apportate modifiche su larga scala.

Storia architettonica di Dodo IS: un primo monolite

Serie di articoli “Cos’è Dodo IS?” parlerà di:

  1. Primo monolite a Dodo IS (2011-2015). (Tu sei qui)

  2. Percorso backoffice: sedi separate e bus.

  3. Percorso della parte committente: facciata sopra il basamento (2016-2017). (In corso...)

  4. La storia dei microservizi reali. (2018-2019). (In corso...)

  5. Taglio completato del monolite e stabilizzazione dell'architettura. (In corso...)

Architettura primordiale

Nel 2011, l'architettura Dodo IS si presentava così:

Storia architettonica di Dodo IS: un primo monolite

Il primo modulo dell'architettura è l'accettazione dell'ordine. Il processo aziendale era questo:

  • un cliente chiama la pizzeria;

  • Il direttore prende il telefono;

  • prende ordini al telefono;

  • Allo stesso tempo, lo compila nell'interfaccia di accettazione dell'ordine: vengono prese in considerazione le informazioni sul cliente, i dati sui dettagli dell'ordine e l'indirizzo di consegna. 

L'interfaccia del sistema informativo assomigliava a questa...

Prima versione di ottobre 2011:

Leggermente migliorato nel gennaio 2012

Sistema informativo Dodo Pizza Delivery Pizzeria Ristorante

Le risorse per sviluppare il primo modulo di raccolta ordini erano limitate. Era necessario fare molto, velocemente e con una piccola squadra. Il piccolo team è composto da 2 sviluppatori che hanno gettato le basi per l'intero sistema futuro.

La loro prima decisione ha determinato il destino futuro dello stack tecnologico:

  • Backend su ASP.NET MVC, linguaggio C#. Gli sviluppatori erano dei puntini; questo stack era familiare e piacevole per loro.

  • Frontend su Bootstrap e JQuery: interfacce utente basate su stili e script personalizzati. 

  • Database MySQL: nessun costo di licenza, facile da usare.

  • Server su Windows Server, perché .NET quindi potrebbe essere solo su Windows (non parleremo di Mono).

Fisicamente, tutto questo veniva espresso nella “scrivania dell’hoster”. 

Architettura dell'applicazione di accettazione dell'ordine

A quel tempo tutti parlavano già di microservizi e la SOA veniva utilizzata in grandi progetti già da circa 5 anni, ad esempio WCF è stato rilasciato nel 2006. Ma poi hanno scelto una soluzione affidabile e comprovata.

Eccolo.

Storia architettonica di Dodo IS: un primo monolite

Asp.Net MVC è Razor che, su richiesta di un form o di un client, produce una pagina HTML con rendering sul server. Sul client gli script CSS e JS visualizzano già le informazioni e, se necessario, eseguono richieste AJAX tramite JQuery.

Le richieste sul server rientrano nelle classi *Controller, dove il metodo elabora e genera la pagina HTML finale. I controller effettuano richieste a un livello logico chiamato *Servizi. Ciascuno dei servizi corrispondeva ad alcuni aspetti dell'attività:

  • Ad esempio, DepartmentStructureService ha fornito informazioni su pizzerie e reparti. Un reparto è un gruppo di pizzerie gestito da un affiliato.

  • ReceivingOrdersService ha ricevuto e calcolato il contenuto dell'ordine.

  • E SmsService ha inviato SMS chiamando i servizi API per l'invio di SMS.

I servizi elaboravano i dati dal database e memorizzavano la logica aziendale. Ogni servizio aveva uno o più *Repository con il nome appropriato. Contengono già query alle procedure memorizzate nel database e un livello di mappatori. C'era una logica aziendale negli archivi, soprattutto in quelli che producevano dati di reporting. L'ORM non è stato utilizzato, tutti si affidavano a SQL scritto a mano. 

C'era anche un livello del modello di dominio e classi di supporto generali, ad esempio la classe Order, che memorizzava l'ordine. Lì, nel livello, c'era un aiutante per convertire il testo visualizzato nella valuta selezionata.

Tutto ciò può essere rappresentato da questo modello:

Storia architettonica di Dodo IS: un primo monolite

Modo d'ordine

Consideriamo un modo iniziale semplificato per creare un tale ordine.

Storia architettonica di Dodo IS: un primo monolite

Inizialmente il sito era statico. C'erano i prezzi sopra e in alto c'era un numero di telefono e la scritta "Se vuoi la pizza, chiama il numero e ordina". Per ordinare, dobbiamo implementare un semplice flusso: 

  • Il cliente accede a un sito statico con i prezzi, seleziona i prodotti e chiama il numero indicato sul sito.

  • Il cliente nomina i prodotti che desidera aggiungere all'ordine.

  • Dà il suo indirizzo e nome.

  • L'operatore accetta l'ordine.

  • L'ordine viene visualizzato nell'interfaccia degli ordini accettati.

Tutto inizia con la visualizzazione del menu. Un utente operatore registrato accetta solo un ordine alla volta. Pertanto, il carrello delle bozze può essere archiviato nella sua sessione (la sessione dell'utente viene archiviata in memoria). È presente un oggetto Carrello contenente prodotti e informazioni sul cliente.

Il cliente nomina il prodotto, l'operatore clicca su + accanto al prodotto e viene inviata una richiesta al server. Le informazioni sul prodotto vengono estratte dal database e le informazioni sul prodotto vengono aggiunte al carrello.

Storia architettonica di Dodo IS: un primo monolite

Nota. Sì, qui non devi estrarre il prodotto dal database, ma trasferirlo dal front-end. Ma per chiarezza ho mostrato esattamente il percorso dalla base. 

Successivamente, inserisci l'indirizzo e il nome del cliente. 

Storia architettonica di Dodo IS: un primo monolite

Quando fai clic su "Crea ordine":

  • Inviamo la richiesta a OrderController.SaveOrder().

  • Otteniamo il carrello dalla sessione, ci sono prodotti nella quantità di cui abbiamo bisogno.

  • Integriamo Cart con le informazioni sul cliente e le passiamo al metodo AddOrder della classe ReceivingOrderService, dove viene salvata nel database. 

  • Il database ha tabelle con l'ordine, il contenuto dell'ordine, il cliente e sono tutti collegati.

  • L'interfaccia di visualizzazione degli ordini va, estrae gli ultimi ordini e li visualizza.

Nuovi moduli

Ricevere l'ordine era importante e necessario. Non puoi gestire un'attività di pizza se non hai un ordine da vendere. Pertanto, il sistema ha iniziato ad acquisire funzionalità, dal 2012 al 2015 circa. Durante questo periodo sono apparsi molti blocchi diversi del sistema, che chiamerò moduli, in contrapposizione al concetto di servizio o prodotto. 

Un modulo è un insieme di funzioni unite da un obiettivo aziendale comune. Inoltre, si trovano fisicamente nella stessa applicazione.

I moduli possono essere chiamati blocchi di sistema. Ad esempio, questo è un modulo di reporting, interfacce di amministrazione, tracker dei prodotti da cucina, autorizzazione. Queste sono tutte interfacce utente diverse, alcune hanno anche stili visivi diversi. Inoltre, tutto è all'interno di un'unica applicazione, un processo in esecuzione. 

Tecnicamente i moduli sono stati progettati come Area (questa idea è rimasta anche in questo caso). nucleo asp.net). C'erano file separati per il frontend, i modelli e le rispettive classi controller. Di conseguenza, il sistema è stato trasformato da tale...

Storia architettonica di Dodo IS: un primo monolite

...a questa:

Storia architettonica di Dodo IS: un primo monolite

Alcuni moduli sono implementati da siti separati (progetto eseguibile), a causa di funzionalità completamente separate e in parte a causa di uno sviluppo in qualche modo separato e più mirato. Questo:

  • Website - prima versione sito dodopizza.ru.

  • Esportare: download dei report da Dodo IS per 1C. 

  • MONITOR PERSONALI — conto personale del dipendente. È stato sviluppato separatamente e ha un proprio punto di ingresso e un design separato.

  • fs — progetto di hosting statico. Successivamente ce ne siamo allontanati, spostando tutti i contenuti statici sulla CDN Akamai. 

I blocchi rimanenti si trovavano nell'applicazione BackOffice. 

Storia architettonica di Dodo IS: un primo monolite

Spiegazione dei nomi:

  • Cassiere - Cassa del ristorante.

  • ShiftManager - interfacce per il ruolo “Shift Manager”: statistiche operative sulle vendite della pizzeria, possibilità di inserire i prodotti in una stop list, modificare un ordine.

  • OfficeManager - interfacce per i ruoli “Gestore Pizzeria” e “Franchisee”. Qui puoi trovare le funzioni per allestire una pizzeria, le sue promozioni bonus, ricevere e lavorare con i dipendenti e report.

  • PublicScreens - interfacce per TV e tablet appesi nelle pizzerie. I televisori visualizzano il menu, le informazioni pubblicitarie e lo stato dell'ordine al momento della consegna. 

Hanno utilizzato un livello di servizio comune, un blocco comune di classi di dominio Dodo.Core e una base comune. A volte potevano ancora collegarsi l'uno all'altro attraverso dei passaggi. Inoltre, anche i singoli siti, come dodopizza.ru o personal.dodopizza.ru, hanno avuto accesso a servizi comuni.

Quando sono apparsi nuovi moduli, abbiamo cercato di riutilizzare il più possibile il codice già creato per servizi, procedure memorizzate e tabelle nel database. 

Per una migliore comprensione della portata dei moduli realizzati nel sistema, ecco uno schema del 2012 con i piani di sviluppo:

Storia architettonica di Dodo IS: un primo monolite

Nel 2015 tutto era sulla buona strada e ancora di più era in produzione.

  • L'accettazione dell'ordine è diventata un blocco separato del Contact Center, dove l'ordine viene accettato dall'operatore.

  • Nelle pizzerie sono comparsi schermi pubblici con menù e informazioni.

  • La cucina è dotata di un modulo che riproduce automaticamente il messaggio vocale “Nuova Pizza” quando arriva un nuovo ordine e stampa anche una fattura per il corriere. Ciò semplifica notevolmente i processi in cucina e consente ai dipendenti di non essere distratti da un gran numero di semplici operazioni.

  • Il blocco di consegna è diventato una cassa di consegna separata, dove l'ordine è stato rilasciato al corriere, che in precedenza aveva preso il suo turno. Il suo orario di lavoro è stato preso in considerazione per il calcolo del suo stipendio. 

Parallelamente, dal 2012 al 2015, sono comparsi più di 10 sviluppatori, aperte 35 pizzerie, il sistema è stato implementato in Romania e preparato per l'apertura di punti negli Stati Uniti. Gli sviluppatori non erano più coinvolti in tutte le attività, ma erano divisi in team. ciascuno specializzato nella propria parte del sistema. 

Problematica

Anche per l'architettura (ma non solo).

Caos alla base

Una base è conveniente. È possibile raggiungere la coerenza, e questo a scapito degli strumenti integrati nei database relazionali. Lavorare con esso è familiare e conveniente, soprattutto se ci sono poche tabelle e pochi dati.

Ma in 4 anni di sviluppo, il database conteneva circa 600 tabelle, 1500 procedure memorizzate, molte delle quali avevano anche una logica. Sfortunatamente, le procedure memorizzate non offrono molti vantaggi quando si lavora con MySQL. Non vengono memorizzati nella cache dal database e la memorizzazione della logica al loro interno complica lo sviluppo e il debug. Anche riutilizzare il codice è difficile.

Molte tabelle non avevano indici adatti, da qualche parte, al contrario, c'erano molti indici, il che rendeva difficile l'inserimento. È stato necessario modificare circa 20 tabelle: la transazione per creare un ordine poteva richiedere circa 3-5 secondi. 

I dati nelle tabelle non erano sempre nella forma più appropriata. Da qualche parte era necessario fare la denormalizzazione. Una parte dei dati ricevuti regolarmente si trovava in una colonna sotto forma di struttura XML, il che aumentava i tempi di esecuzione, allungava le interrogazioni e complicava lo sviluppo.

Le stesse tabelle erano soggette a molto richieste eterogenee. I tavoli più popolari, come quello menzionato sopra, sono stati particolarmente colpiti ordini o tavoli pizzeria. Sono stati utilizzati per visualizzare le interfacce operative in cucina e le analisi. Il sito li ha anche contattati (dodopizza.ru), dove potrebbero arrivare improvvisamente molte richieste in un dato momento. 

I dati non sono stati aggregati e molti calcoli sono stati eseguiti al volo utilizzando la base. Ciò ha creato calcoli non necessari e carico aggiuntivo. 

Spesso il codice finiva nel database quando non avrebbe potuto farlo. Da qualche parte mancavano le operazioni di massa, da qualche parte sarebbe stato necessario dividere una richiesta in più tramite codice per accelerare e aumentare l'affidabilità. 

Coesione e confusione nel codice

I moduli che avrebbero dovuto essere responsabili della loro parte di attività non lo hanno fatto onestamente. Alcuni di loro avevano una duplicazione delle funzioni per i ruoli. Ad esempio, un commerciante locale responsabile dell'attività di marketing di una rete nella sua città doveva utilizzare sia l'interfaccia "Amministratore" (per impostare le promozioni) sia l'interfaccia "Gestore ufficio" (per visualizzare l'impatto delle promozioni sul Attività commerciale). Naturalmente, all'interno di entrambi i moduli veniva utilizzato lo stesso servizio, che funzionava con promozioni bonus.

I servizi (classi all'interno di un grande progetto monolitico) potrebbero chiamarsi a vicenda per arricchire i propri dati.

Con le classi modello stesse che memorizzano i dati, il lavoro nel codice è stato svolto diversamente. Da qualche parte c'erano costruttori attraverso i quali era possibile specificare i campi obbligatori. Da qualche parte ciò è stato fatto attraverso proprietà pubbliche. Naturalmente, il recupero e la trasformazione dei dati dal database sono stati molteplici. 

La logica era nei controller o nelle classi di servizio. 

Sembrano problemi minori, ma hanno rallentato notevolmente lo sviluppo e ridotto la qualità, portando a instabilità e bug. 

Complessità di grande sviluppo

Le difficoltà sono sorte nello sviluppo stesso. È stato necessario creare diversi blocchi del sistema e in parallelo. Diventava sempre più difficile racchiudere le esigenze di ciascun componente in un unico codice. Non è stato facile mettere d'accordo e accontentare tutti i componenti allo stesso tempo. A ciò si aggiungevano limitazioni tecnologiche, soprattutto per quanto riguarda la base e il frontale. È stato necessario abbandonare JQuery a favore di framework di alto livello, soprattutto in termini di servizi client (sito web).

Alcune parti del sistema potrebbero utilizzare database più adatti a questo scopo. Ad esempio, in seguito abbiamo avuto un precedente relativo al passaggio da Redis a CosmosDB per archiviare un carrello degli ordini. 

I team e gli sviluppatori che lavoravano nella loro area desideravano chiaramente una maggiore indipendenza per i loro servizi, sia in termini di sviluppo che di implementazione. Conflitti durante le fusioni, problemi durante i rilasci. Se per 5 sviluppatori questo problema fosse insignificante, allora per 10, e ancor di più con la crescita prevista, tutto diventerebbe più serio. E quello che sarebbe seguito era lo sviluppo di un’applicazione mobile (è iniziato nel 2017, e nel 2018 c’è stata grande caduta). 

Parti diverse del sistema richiedevano indicatori di stabilità diversi, ma a causa della forte connettività del sistema, non abbiamo potuto fornirlo. Un errore durante lo sviluppo di una nuova funzione nel pannello di amministrazione avrebbe potuto comportare l'accettazione dell'ordine sul sito, perché il codice è comune e riutilizzabile, anche il database e i dati sono gli stessi.

Probabilmente sarebbe possibile evitare questi errori e problemi nel quadro di un'architettura così monolitica e modulare: creare una separazione delle responsabilità, effettuare il refactoring sia del codice che del database, separare chiaramente i livelli l'uno dall'altro e monitorare la qualità ogni giorno. Ma le soluzioni architettoniche scelte e l'attenzione alla rapida espansione delle funzionalità del sistema hanno portato a problemi in termini di stabilità.

Come il blog Power of Mind ha messo i registratori di cassa nei ristoranti

Se la crescita della rete di pizzerie (e del carico) continuasse allo stesso ritmo, dopo un po' i cali sarebbero tali che il sistema non si riprenderebbe. La storia seguente illustra bene i problemi che abbiamo iniziato ad affrontare nel 2015. 

Nel blog"Potere della mente"c'era un widget che mostrava i dati sulle entrate dell'anno per l'intera rete. Il widget ha avuto accesso all'API pubblica Dodo, che fornisce questi dati. Queste statistiche sono ora disponibili su http://dodopizzastory.com/. Il widget veniva mostrato su ogni pagina ed effettuava richieste con un timer ogni 20 secondi. La richiesta è andata a api.dodopizza.ru e chiedeva:

  • numero di pizzerie della rete;

  • ricavi totali della rete dall'inizio dell'anno;

  • le entrate di oggi.

Una richiesta di statistiche sulle entrate è andata direttamente al database e ha iniziato a richiedere i dati sugli ordini, aggregare i dati direttamente al volo ed emettere l'importo. 

I registratori di cassa nei ristoranti andavano alla stessa tabella degli ordini, caricavano un elenco degli ordini accettati per oggi e vi venivano aggiunti nuovi ordini. I registratori di cassa effettuavano le loro richieste ogni 5 secondi o al rinfresco della pagina.

Il diagramma sembrava così:

Storia architettonica di Dodo IS: un primo monolite

Un giorno d'autunno, Fyodor Ovchinnikov scrisse un lungo e popolare articolo sul suo blog. Molte persone sono venute sul blog e hanno iniziato a leggere tutto attentamente. Mentre tutte le persone presenti leggevano l'articolo, il widget delle entrate funzionava correttamente e richiedeva l'API ogni 20 secondi.

L'API ha richiamato una procedura memorizzata per calcolare l'importo di tutti gli ordini dall'inizio dell'anno per tutte le pizzerie della rete. L'aggregazione si basava sulla tabella degli ordini, molto utilizzata. Ad esso vanno tutte le casse di tutti i ristoranti aperti in quel momento. I registratori di cassa hanno smesso di rispondere e gli ordini non sono stati accettati. Inoltre non sono stati accettati dal sito, non sono apparsi sul tracker e il capoturno non è riuscito a vederli nella sua interfaccia. 

Questa non è l'unica storia. Nell’autunno del 2015, ogni venerdì il carico sul sistema era critico. Diverse volte abbiamo disattivato l'API pubblica e una volta abbiamo dovuto disattivare anche il sito perché nulla aiutava. C'era anche un elenco di servizi con l'ordine di chiusura sotto carichi pesanti.

Da questo momento inizia la nostra lotta con i carichi e per la stabilizzazione del sistema (dall’autunno 2015 all’autunno 2018). Fu allora che accadde"La Grande Caduta" Inoltre talvolta si sono verificati anche dei fallimenti, alcuni dei quali molto sensibili, ma il periodo generale di instabilità può ormai ritenersi terminato.

Rapida crescita aziendale

Perché non si poteva “fare bene subito”? Basta guardare i grafici seguenti.

Storia architettonica di Dodo IS: un primo monolite

Anche nel 2014-2015 c'è stata un'apertura in Romania ed era in preparazione un'apertura negli USA.

La catena è cresciuta molto rapidamente, si sono aperti nuovi paesi, sono comparsi nuovi formati di pizzerie, ad esempio una pizzeria aperta nella food court. Tutto ciò ha richiesto un'attenzione significativa specificatamente all'espansione delle funzioni di Dodo IS. Senza tutte queste funzioni, senza il tracciamento in cucina, la contabilizzazione dei prodotti e delle perdite nel sistema, la visualizzazione della consegna degli ordini nella sala ristorazione, difficilmente ora parleremmo dell'architettura “corretta” e del “corretto “approccio allo sviluppo.

Un altro ostacolo ad una tempestiva revisione dell’architettura e ad un’attenzione generale ai problemi tecnici è stata la crisi del 2014. Tali cose danneggiano le opportunità di crescita dei team, soprattutto per un’azienda giovane come Dodo Pizza.

Soluzioni rapide che hanno aiutato

I problemi necessitavano di soluzioni. Convenzionalmente le soluzioni possono essere divise in 2 gruppi:

  • Quelli veloci che spengono il fuoco e ci danno un piccolo margine di sicurezza e ci fanno guadagnare tempo per cambiare.

  • Sistemico e, quindi, lungo. Riprogettare una serie di moduli, dividendo l'architettura monolitica in servizi separati (la maggior parte di essi non sono micro, ma piuttosto macroservizi, e c'è di più su questo) relazione di Andrej Morevskij). 

L'elenco secco dei cambiamenti rapidi è:

Ingrandisci il master di base

Naturalmente, la prima cosa da fare per combattere il carico è aumentare la potenza del server. Ciò è stato fatto per il database master e i server Web. Purtroppo questo è possibile solo fino a un certo limite, oltre diventa troppo costoso.

Dal 2014 ci siamo spostati su Azure; di questo argomento abbiamo parlato anche allora nell’articolo “Come Dodo Pizza consegna la pizza utilizzando il cloud Microsoft Azure" Ma dopo una serie di aumenti dei server, il costo della base ha raggiunto un limite. 

Repliche di database per la lettura

Abbiamo realizzato due repliche per la base:

LeggiReplica per le richieste di directory. Viene utilizzato per leggere directory, come città, via, pizzeria, prodotti (dominio cambiato lentamente) e in quelle interfacce in cui è accettabile un piccolo ritardo. C'erano 2 di queste repliche, ne abbiamo garantito la disponibilità allo stesso modo del master.

ReadReplica per le richieste di report. Questo database aveva una disponibilità inferiore, ma tutti i report venivano indirizzati ad esso. Possono avere pesanti richieste di enormi ricalcoli di dati, ma non influiscono sul database principale e sulle interfacce operative. 

Cache nel codice

Non c'erano cache da nessuna parte nel codice (affatto). Ciò ha portato a richieste aggiuntive, non sempre necessarie, al database caricato. Inizialmente, le cache erano sia in memoria che su un servizio cache esterno, era Redis. Tutto era invalidato dal tempo, le impostazioni erano specificate nel codice.

Server multipli per il backend

Anche il backend dell'applicazione doveva essere ridimensionato per resistere a carichi maggiori. Era necessario creare un cluster da un server IIS. Ci siamo trasferiti sessione di candidatura dalla memoria su RedisCache, che ha permesso di creare diversi server dietro un semplice bilanciatore di carico con round robin. Inizialmente è stato utilizzato lo stesso Redis delle cache, poi sono state suddivise in più. 

Di conseguenza, l'architettura è diventata più complessa...

Storia architettonica di Dodo IS: un primo monolite

...ma parte della tensione si è allentata.

E poi è stato necessario rifare i componenti caricati, ed è quello che abbiamo intrapreso. Di questo parleremo nella parte successiva.

Fonte: habr.com

Aggiungi un commento