Eller sÄ Àr varje olycklig företag med monolit en olycklig pÄ sitt eget sÀtt.
Utvecklingen av Dodo IS-systemet började samma Ă„r som Dodo Pizzas verksamhet â 2011. Basen var idĂ©n om en fullstĂ€ndig och total digitalisering av affĂ€rsprocesserna, vilket , nĂ„got som redan dĂ„, 2011, vĂ€ckte mĂ„nga frĂ„gor och skepticism. Men nu, efter nio Ă„r, fortsĂ€tter vi pĂ„ denna vĂ€g â med egen utveckling som började med en monolit.
Den hÀr artikeln Àr ett "svar" pÄ frÄgorna "Varför skriva om arkitekturen och göra sÄdana omfattande och lÄngvariga förÀndringar?" till den föregÄende artikeln Jag börjar med hur utvecklingen av Dodo IS började, hur den ursprungliga arkitekturen sÄg ut, hur nya moduler kom till och vilka problem som ledde till omfattande förÀndringar.

Artikelserien "Vad Àr Dodo IS?" kommer att berÀtta om:
Den tidiga monoliten i Dodo IS (2011-2015). (Du Àr hÀr)
.
VĂ€gen för klientdelen: ett fasad över databasen (2016-2017). (PĂ„gĂ„râŠ)
Historien om verkliga mikrotjĂ€nster. (2018-2019). (PĂ„gĂ„râŠ)
Den avslutade nedskĂ€rningen av monoliten och stabiliseringen av arkitekturen. (PĂ„gĂ„râŠ)
Den ursprungliga arkitekturen
à r 2011 sÄg Dodo IS-arkitekturen ut sÄ hÀr:

Den första modulen i arkitekturen â bestĂ€llningsmottagning. AffĂ€rsprocessen var sĂ„dan:
kunden ringer till pizzerian;
managern svarar;
tar emot bestÀllningen per telefon;
samtidigt skriver han ner den i bestĂ€llningsgrĂ€nssnittet: information om kunden, detaljerna om bestĂ€llningen, leveransadressen beaktas.Â
GrĂ€nssnittet för informationssystemet sĂ„g ungefĂ€r ut sĂ„ hĂ€râŠ
Den första versionen frÄn oktober 2011:
Resurserna för utvecklingen av den första bestĂ€llningsmodulen var begrĂ€nsade. Mycket skulle göras snabbt och med liten personalstyrka. Liten personalstyrka â det var tvĂ„ utvecklare som lade grunden för hela det framtida systemet.
Deras första beslut bestÀmde den tekniska stackens framtid:
Backend pÄ ASP.NET MVC, sprÄk C#. Utvecklarna var .NET-programmerare, denna stack var bekant och behaglig för dem.
Frontend pĂ„ Bootstrap och jQuery: anvĂ€ndargrĂ€nssnitt med egen design och skript.Â
Databasen MySQL: utan licenskostnader, enkel att anvÀnda.
Servrar pÄ Windows Server, eftersom .NET dÄ endast kunde köras under Windows (Mono ska vi inte diskutera).
Fysiskt uttrycktes allt detta i "dedikera hos vĂ€rden".Â
Arkitekturen för bestÀllningsmottagningsapplikationen
DÄ pratade alla redan om mikrotjÀnster, medan SOA hade anvÀnts i fem Är i stora projekt, till exempel, WCF slÀpptes 2006. Men dÄ valde man en pÄlitlig och beprövad lösning.
HÀr Àr det.

Asp.Net MVC Àr Razor, som genererar en HTML-sida med rendering pÄ servern pÄ begÀran frÄn formulÀr eller klienten. PÄ klienten visar CSS och JS-skript information och, vid behov, utför AJAX-anrop via JQuery.
Anropen pÄ servern hamnar i klasser **Controller, dÀr bearbetning och generation av den slutliga HTML-sidan Àger rum i metoden. Kontrollerna gör anrop till logiklagret, kallat **Services. Varje tjÀnst svarade mot en aspekt av affÀrer:
Till exempel gav DepartmentStructureService information om pizzerior, om avdelningarna. En avdelning Àr en grupp pizzerior under en franchisetagares ledning.
ReceivingOrdersService tog emot och berÀknade bestÀllningens innehÄll.
Och SmsService skickade sms genom att anropa API-tjÀnster för att skicka sms.
TjĂ€nsterna bearbetade data frĂ„n databasen, lagrade affĂ€rslogik. I varje tjĂ€nst fanns en eller flera **Repository med motsvarande namn. HĂ€r fanns redan anrop till lagrade procedurer i databasen och en lagermappare. Den lagrade innehöll affĂ€rslogik, sĂ€rskilt i de som utfĂ€rdade rapportdata. ORM anvĂ€ndes inte, alla litade pĂ„ handskriven SQL.Â
Det fanns ocksÄ ett lager av domÀnmodeller och allmÀnna hjÀlparklasser, till exempel klassen Order, som lagrade bestÀllningen. DÀr fanns ocksÄ en hjÀlpare för att konvertera visningstexten baserat pÄ den valda valutan.
Allt detta kan representeras av en sÄdan modell:

BestÀllningens vÀg
LÄt oss titta pÄ en förenklad initial vÀg för att skapa en sÄdan bestÀllning.

I början var webbplatsen statisk. Den hade priser och överst fanns telefonnumret och texten "Vill du ha pizza â ring numret för att bestĂ€lla". För att göra en bestĂ€llning behöver vi genomföra ett enkelt flöde:Â
Kunden gÄr in pÄ den statiska webbplatsen med priser, vÀljer produkter och ringer numret som anges pÄ webbplatsen.
Kunden nÀmner de produkter som han vill lÀgga till i bestÀllningen.
Han nÀmner sin adress och namn.
Operatören tar emot bestÀllningen.
BestÀllningen visas i grÀnssnittet för mottagna bestÀllningar.
Allt börjar med att visa menyn. En inloggad anvÀndaroperatör kan endast ta ett enda bestÀllning vid en given tidpunkt. DÀrför kan draft-korgen lagras i hans session (anvÀndarens session lagras i minnet). DÀr finns ett objekt Cart, som innehÄller produkter och information om kunden.
Kunden nÀmner produkten, operatören klickar pÄ + bredvid produkten och en begÀran skickas till servern. Informationen om produkten hÀmtas frÄn databasen och lÀggs till i korgen.

AnmĂ€rkning. Ja, hĂ€r behöver man inte hĂ€mta produkten frĂ„n databasen, utan kan överföra den frĂ„n front-end. Men för tydlighetens skull visade jag just vĂ€gen frĂ„n databasen.Â
DĂ€refter skriver vi in kundens adress och namn.Â

Vid klick pÄ "Skapa bestÀllning":
Skickar vi begÀran till OrderController.SaveOrder().
Vi fÄr Cart frÄn sessionen, dÀr ligger produkterna i den mÀngd vi behöver.
Vi kompletterar Cart med kundinformation och överför det till metoden AddOrder i klassen ReceivingOrderService, dĂ€r den sparas i databasen.Â
I databasen finns tabeller med bestÀllningar, bestÀllningens innehÄll, kunden, och de Àr alla kopplade.
GrÀnssnittet för att visa bestÀllningar gÄr och hÀmtar de senaste bestÀllningarna och visar dem.
Nya moduler
Mottagning av bestĂ€llningar var viktig och nödvĂ€ndig. Man kan inte driva en pizzeriabusiness utan en bestĂ€llningsmottagning. DĂ€rför började systemet att vĂ€xa i funktionalitet â ungefĂ€r mellan 2012 och 2015. Under denna tid kom mĂ„nga olika block av systemet, som jag kommer att kalla moduler, i motsats till begreppet tjĂ€nst eller produkt.Â
En modul Àr en uppsÀttning funktioner som Àr förenade av ett gemensamt affÀrsmÄl. Samtidigt finns de fysiskt i en enda applikation.
Moduler kan kallas block i systemet. Till exempel Ă€r detta modul för rapporter, admingrĂ€nssnitt, , autentisering. Det Ă€r alla olika grĂ€nssnitt för anvĂ€ndaren, och vissa har till och med olika visuella stilar. Samtidigt Ă€r alla inom en enda applikation, en enda fungerande process.Â
Tekniskt sett var modulerna utformade som Area (denna idĂ© kvarstod till och med i ). DĂ€r fanns separata filer för front-end, modeller, samt egna kontrollklass. Resultatet blev att systemet omvandlades frĂ„n sĂ„danâŠ

âŠtill sĂ„dan:

Vissa moduler implementerades som separata webbplatser (exekverbara projekt) pÄ grund av deras helt olika funktionalitet och delvis pÄ grund av en mer separat, mer fokuserad utveckling. Dessa Àr:
Webbplats â webbplatsen dodopizza.ru.
Exportera: export av rapporter frĂ„n Dodo IS till 1C.Â
Personlig â den personliga inloggningen för anstĂ€llda. Utvecklades separat och har sin egen ingĂ„ngspunkt och design.
fs â projektet för hosting av statisk innehĂ„ll. Senare övergick vi frĂ„n det och flyttade all statisk till CDN Akamai.Â
Ăvriga block lĂ„g i BackOffice-applikationen.Â

Förklaring av namnen:
Cashier â Restaurangens kassa.
ShiftManager â grĂ€nssnitt för rollen "Skiftledare": aktuell statistik över pizzaförsĂ€ljningar, möjlighet att sĂ€tta produkter pĂ„ stopplistan, Ă€ndra bestĂ€llningar.
OfficeManager â grĂ€nssnitt för rollerna "Pizzeriaansvarig" och "Franchisetagare". HĂ€r samlas funktioner för att konfigurera pizzerian, dess bonuskampanjer, hantering av anstĂ€llda, rapporter.
PublicScreens â grĂ€nssnitt för TV-apparater och tabletter som hĂ€nger i pizzerior. PĂ„ TV:erna visas menyn, reklaminformation och bestĂ€llningsstatus vid leverans.Â
De anvĂ€nde ett gemensamt tjĂ€nstelager, en gemensam block av domĂ€nklasser Dodo.Core, samt en gemensam databas. Ibland kunde de ocksĂ„ göra övergĂ„ngar mellan varandra. Ăven separata webbplatser, sĂ„som dodopizza.ru eller personal.dodopizza.ru, kopplade till gemensamma tjĂ€nster.
Vid introduktionen av nya moduler strĂ€vade de efter att Ă„teranvĂ€nda redan skapad kod för tjĂ€nster, lagrade procedurer och tabeller i databasen sĂ„ mycket som möjligt.Â
För att bÀttre förstÄ omfattningen av modulerna som skapades i systemet, hÀr Àr en diagram frÄn 2012 med utvecklingsplaner:

à r 2015 var allt pÄ diagrammet och till och med mer i produktion.
BestÀllningsmottagningen utvecklades till en separat del av Kontaktcentret, dÀr bestÀllningar tas emot av operatörer.
Det dök upp offentliga skÀrmar med menyer och information som hÀnger i pizzerior.
I köket finns en modul som automatiskt Äterger röstmeddelandet "Ny pizza" nÀr en ny bestÀllning kommer in och skriver ut en följesedel för kuriren. Detta förenklar processerna i köket och gör att personalen kan fokusera mindre pÄ ett stort antal enkla uppgifter.
Leveransblocket blev en separat Leveranskassa, dĂ€r bestĂ€llningen gavs till kuriren som tidigare stĂ€llt sig pĂ„ skift. Hans arbetstid beaktades för att berĂ€kna lönen.Â
Parallellt, frĂ„n 2012 till 2015, kom mer Ă€n 10 utvecklare och 35 pizzerior öppnades, systemet rullades ut i RumĂ€nien och förbereddes för öppning av punkter i USA. Utvecklarna sysslade inte lĂ€ngre med alla uppgifter utan delades upp i team, dĂ€r varje team specialiserade sig pĂ„ sin del av systemet.Â
Problem
Delvis pÄ grund av arkitekturen (men inte bara).
Kaos i databasen
En databas Àr praktiskt. I den kan man uppnÄ konsekvens, dels tack vare funktioner inbyggda i relationsdatabaser. Att arbeta med den Àr bekant och praktiskt, sÀrskilt om det finns fÄ tabeller och lite data.
Men under fyra Ärs utveckling fanns det runt 600 tabeller och 1500 lagrade procedurer, mÄnga med egen logik. TyvÀrr ger lagrade procedurer inte nÄgot sÀrskilt fördel vid arbete med MySQL. De cachas inte av databasen och att lagra logik i dem försvÄrar utvecklingen och felsökningen. à teranvÀndning av kod Àr ocksÄ besvÀrligt.
MĂ„nga tabeller hade inte lĂ€mpliga index.Ă andra sidan fanns det, i vissa fall, vĂ€ldigt mĂ„nga index, vilket försvĂ„rade insĂ€ttning. Det behövde modifieras runt 20 tabeller â transaktionen för att skapa en bestĂ€llning kunde ta runt 3-5 sekunder.Â
Data i tabellerna var inte alltid i den mest lÀmpliga formen.I vissa fall behövdes denormalisering. En del data som regelbundet mottogs var i en kolumn i form av en XML-struktur, vilket ökade exekveringstiden, förlÀngde förfrÄgningarna och försvÄrade utvecklingen.
Det gjordes mycket heterogena frĂ„gor mot samma tabeller.SĂ€rskilt drabbades populĂ€ra tabeller, som den nĂ€mnda tabellen orders eller tabellen pizzeria.De anvĂ€ndes för att visa operativa grĂ€nssnitt i köket, för analys. Ăven webbplatsen, som nĂ€r som helst kunde fĂ„ mĂ„nga plötsliga förfrĂ„gningar.Â
Data var inte aggregerade och mĂ„nga berĂ€kningar skedde pĂ„ flyktiga sĂ€tt av databasen. Detta skapade onödiga berĂ€kningar och extra belastning.Â
Koden gick ofta till databasen nĂ€r den inte behövde göra det. I vissa fall saknades massoperationer, i andra fall behövde en frĂ„ga delas upp i flera genom koden för att öka hastigheten och förbĂ€ttra tillförlitligheten.Â
Koppling och förvirring i koden
Modulerna, som skulle ansvara för sitt affÀrsomrÄde, gjorde det inte Àrligt.. Vissa av dem hade dubbleringar i funktionerna för rollerna. Till exempel, för en lokal marknadsförare som ansvarade för nÀtverkets marknadsföringsaktiviteter i sin stad, var det nödvÀndigt att anvÀnda bÄde "Admin"-grÀnssnittet (för att skapa kampanjer) och "Kontorschef"-grÀnssnittet (för att se kampanjernas pÄverkan pÄ affÀrerna). SjÀlvklart anvÀnde bÄda modulerna en gemensam tjÀnst som hanterade bonuskampanjer.
TjÀnster (klasser inom ett stort monolitiskt projekt) kunde anropa varandra för att berika sina data.
SjĂ€lva klassmodellerna som lagrade data, arbetade i koden pĂ„ olika sĂ€tt. PĂ„ vissa stĂ€llen fanns konstruktorer dĂ€r man kunde ange obligatoriska fĂ€lt. PĂ„ andra stĂ€llen gjordes detta via publika egenskaper. SjĂ€lvklart var det varierande hur data hĂ€mtades och omvandlades frĂ„n databasen.Â
Logiken lĂ„g antingen i kontrollerna eller i servicelaser.Â
Detta kan verka vara smĂ„ problem, men de fördröjde utvecklingen avsevĂ€rt och försĂ€mrade kvaliteten, vilket ledde till instabilitet och fel.Â
SvÄrigheterna med stor utveckling
SvÄrigheter uppstod Àven i sjÀlva utvecklingen. Det behövdes olika block i systemet, vilket innebar att man var tvungen att arbeta parallellt. Att rymma behoven hos varje komponent i en gemensam kod blev allt svÄrare. Det var inte lÀtt att komma överens och tillfredsstÀlla alla komponenter samtidigt. Dessutom fanns det begrÀnsningar i teknologierna, sÀrskilt vad gÀllde databasen och frontend. Man var tvungen att överge jQuery till förmÄn för högre nivÄer av ramverk, sÀrskilt nÀr det gÀllde klienttjÀnster (webbplatsen).
I vissa delar av systemet kunde databaser som passade bĂ€ttre för detta anvĂ€ndas,. Till exempel hade vi senare ett exempel pĂ„ att vi gick frĂ„n Redis till CosmosDB för att lagra kundvagnen.Â
Team och utvecklare som arbetade inom sina omrĂ„den ville uppenbarligen ha mer sjĂ€lvstĂ€ndighet för sina tjĂ€nster, bĂ„de nĂ€r det gĂ€llde utveckling och distribution. Konflikter vid sammanslagningar, problem vid releaser. Om detta problem inte var betydelsefullt för 5 utvecklare, sĂ„ blev det allvarligare med 10, och Ă€nnu mer med planerad tillvĂ€xt. Dessutom skulle det vara utveckling av en mobilapp i framtiden (den startade 2017, och 2018 hade vi ).Â
Olika delar av systemet krÀvde olika stabilitetsmÄtt., men pÄ grund av systemets starka kopplingar kunde vi inte sÀkerstÀlla detta. Ett fel vid utvecklingen av en ny funktion i adminpanelen kunde mycket vÀl ha pÄverkat bestÀllningsmottagningen pÄ webbplatsen, eftersom koden Àr gemensam och ÄteranvÀndbar, och databasen och data ocksÄ Àr enhetliga.
Det skulle nog ha varit möjligt att undvika dessa fel och problem inom en sÄdan monolitisk-modulÀr arkitektur: att göra en tydlig ansvarsfördelning, genomföra refaktorering av bÄde koden och databasen, klart separera lagren frÄn varandra, och övervaka kvaliteten varje dag. Men de valda arkitektoniska besluten och fokus pÄ snabb funktionsexpansion ledde till problem med stabiliteten.
Hur bloggen Sila Uma bidrog till kassor i restauranger
Om tillvĂ€xten av pizzakedjan (och belastningen) hade fortsatt i samma takt, skulle fallen sĂ„ smĂ„ningom ha blivit sĂ„ stora att systemet inte skulle kunna Ă„terhĂ€mta sig. En historia som tydligt illustrerar de problem vi började möta runt 2015.Â
I bloggen ââ fanns det en widget som visade intĂ€ktsdata för hela kedjan under ett Ă„r. Widgeten gjorde anrop till Dodo's offentliga API, som tillhandahĂ„ller dessa uppgifter. Nu finns denna statistik tillgĂ€nglig pĂ„ . Widgeten visades pĂ„ varje sida och gjorde anrop var 20:e sekund. Anropet gick till api.dodopizza.ru och begĂ€rde:
antal pizzarestauranger i kedjan;
den totala intÀkten för kedjan sedan Ärets början;
intÀkten för idag.
StatistikförfrĂ„gan om intĂ€kter gick direkt till databasen och började begĂ€ra data om bestĂ€llningar, aggregera data i realtid och returnera summan.Â
I samma bestÀllningstabell gick kassorna i restauranger, de laddade upp listan över bestÀllningar som tagits emot idag, och nya bestÀllningar lades ocksÄ till. Kassorna gjorde sina förfrÄgningar var 5:e sekund eller vid siduppdateringar.
Schemat sÄg ut sÄ hÀr:

En höst skrev Fedor Ovchinnikov en lÄng och populÀr artikel pÄ sin blogg. MÄnga mÀnniskor kom till bloggen och började lÀsa noggrant. Medan varje besökare lÀste artikeln fungerade widgeten för intÀkter som den skulle och begÀrde API var 20:e sekund.
API anropade en lagrad procedur för att berĂ€kna summan av alla bestĂ€llningar sedan början av Ă„ret frĂ„n alla pizzerior i kedjan. Aggreringen skedde i tabellen orders, som var mycket populĂ€r. Denna tabell innefattade alla kassor frĂ„n alla öppna restauranger vid den tidpunkten. Kassorna slutade svara, bestĂ€llningar togs inte emot. Dessutom kunde inte bestĂ€llningar göras frĂ„n webbplatsen, de dök inte upp pĂ„ trackern och skiftchefen kunde inte se dem i sitt grĂ€nssnitt.Â
Detta Àr inte den enda historien. Fram till hösten 2015 var belastningen pÄ systemet kritisk varje fredag. Flera gÄnger stÀngde vi ner det offentliga API:et, och en gÄng var vi till och med tvungna att stÀnga av webbplatsen eftersom ingenting hjÀlpte. Det fanns till och med en lista över tjÀnster med ordningen för avstÀngning vid allvarliga belastningar.
FrĂ„n och med den tidpunkten började vĂ„r kamp mot belastningar och för stabilisering av systemet (frĂ„n hösten 2015 till hösten 2018). Det var just dĂ„ som "" intrĂ€ffade. Ăven efter det intrĂ€ffade ibland störningar, vissa var ganska kĂ€nsliga, men den övergripande perioden av instabilitet kan nu betraktas som överkommen.
Den kraftiga tillvÀxten av verksamheten
Varför kunde man inte "göra det bra frÄn början"? Man behöver bara titta pÄ följande diagram.

Det fanns Àven en öppning i RumÀnien 2014-2015 och en plan för en öppning i USA.
NÀtverket vÀxte mycket snabbt, nya lÀnder öppnades, nya format av pizzerior dök upp, till exempel en pizzeria i en food court. Allt detta krÀvde betydande uppmÀrksamhet till utvidgningen av funktioner i Dodo IS. Utan alla dessa funktioner, utan spÄrning i köket, hantering av ingredienser och förluster i systemet, samt visning av bestÀllningar i food courten, skulle vi troligen inte diskutera "rÀtt" arkitektur och "rÀtt" metodik för utveckling.
Ytterligare hinder för en snabb översyn av arkitekturen och överhuvudtaget uppmÀrksamheten pÄ tekniska problem var krisen 2014. SÄdana situationer slÄr hÄrt mot tillvÀxtmöjligheterna för team, sÀrskilt för ett ungt företag som Dodo Pizza.
Snabba lösningar som hjÀlpte
Problemen krÀvde lösningar. Villkorligt kan lösningarna delas in i tvÄ grupper:
Snabba, som slÀcker brand och ger en liten buffert samt vinner oss tid för förÀndringar.
Systematiska och dĂ€rmed lĂ„ngsamma. Reengineering av flera moduler, uppdelning av den monolitiska arkitekturen i separata tjĂ€nster (de flesta av dem Ă€r inte mikro utan snarare makrotjĂ€nster och detta Ă€r det som finns) ).Â
Den korta listan över snabba förÀndringar Àr som följande:
Skala upp masterdatabasen
SjÀlvklart, det första som görs för att hantera belastningarna Àr att öka serverns kapacitet. Detta gjordes för masterdatabasen och för webservrarna. TyvÀrr Àr detta endast möjligt upp till en viss grÀns, efter det blir det för dyrt.
Sedan 2014 har vi övergĂ„tt till Azure, om detta skrev vi ocksĂ„ vid den tiden i artikeln "". Men efter en serie av serverökningar för databasen nĂ„dde vi en kostnadsgrĂ€ns.Â
LÀsreplicor för databasen
Det gjordes tvÄ repliker för databasen:
ReadReplica för förfrÄgningar om referenser. Den anvÀnds för att lÀsa referenser, typ stad, gata, pizzerior, produkter (lÄngsamt förÀndrad domÀn), och i de grÀnssnitt dÀr en liten fördröjning Àr acceptabel. Dessa repliker var 2, vi sÀkerstÀllde deras tillgÀnglighet likvÀl som för mÀstaren.
ReadReplica för rapportförfrĂ„gningar. Denna databas hade lĂ€gre tillgĂ€nglighet, men alla rapporter gick genom den. Ăven om den hade tunga förfrĂ„gningar om enorma databerĂ€kningar, pĂ„verkar de inte masterdatabasen eller operationella grĂ€nssnitt.Â
Cache i koden
Det fanns inga cacher i koden (alls). Detta ledde till ytterligare, inte alltid nödvÀndiga, förfrÄgningar i den belastade databasen. Cacher fanns först bÄde i minnet och pÄ en extern cachningstjÀnst, det var Redis. Allt invalidierades efter en viss tid, instÀllningarna angavs i koden.
Flera servrar för backend
Backend för applikationen behövde ocksĂ„ skalas upp för att hantera ökade belastningar. Det var nödvĂ€ndigt att göra en kluster frĂ„n en iis-server. Vi flyttade frĂ„n minnet till RedisCache, vilket möjliggjorde flera servrar bakom en enkel lastbalanserare med round robin. Först anvĂ€ndes samma Redis som för cacherna, sedan fördelade vi det pĂ„ flera.Â
Resultatet blev en mer komplex arkitekturâŠ

âŠmen en del av trycket lyckades vi avlasta.
Och dÀrefter behövdes det göras om belastade komponenter, vilket vi satte igÄng med. Om detta kommer vi att berÀtta i nÀsta del.
KĂ€lla: habr.com
