Eller sÄ Àr varje olyckligt monolitföretag olyckligt pÄ sitt eget sÀtt.
Utvecklingen av Dodo IS-systemet började sĂ„ snart Dodo Pizza-verksamheten startade â 2011. IdĂ©n baserades pĂ„ en fullstĂ€ndig och total digitalisering av affĂ€rsprocesser, och , vilket redan Ă„r 2011 vĂ€ckte mĂ„nga frĂ„gor och skepticism. Men i 9 Ă„r nu har vi följt den hĂ€r vĂ€gen â med vĂ„r 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Ä storskaliga och tidskrÀvande förÀndringar?" till föregÄende artikel . Jag börjar med hur utvecklingen av Dodo IS började, hur den ursprungliga arkitekturen sÄg ut, hur nya moduler dök upp och vilka problem som ledde till storskaliga förÀndringar.

Artikelserien "Vad Àr Dodo IS?" kommer att berÀtta om:
Tidig monolit i Dodo IS (2011-2015). (Du Àr hÀr)
.
Klientdelens vĂ€g: fasad över basen (2016-2017). (PĂ„gĂ„râŠ)
Historien om riktiga mikrotjĂ€nster. (2018â2019). (PĂ„gĂ„râŠ)
Slutförd tillskĂ€rning av monoliten och stabilisering av arkitekturen. (PĂ„gĂ„râŠ)
Ursprunglig arkitektur
à r 2011 sÄg Dodo IS-arkitekturen ut sÄ hÀr:

Den första modulen i arkitekturen Àr orderacceptans. AffÀrsprocessen var följande:
kunden ringer pizzerian;
chefen svarar i telefonen;
tar emot bestÀllningar via telefon;
Samtidigt matas den in i grÀnssnittet för orderacceptering: information om klienten, orderuppgifter och leveransadress beaktas.
Informationssystemets grĂ€nssnitt sĂ„g ut ungefĂ€r sĂ„ hĂ€râŠ
Första versionen frÄn oktober 2011:
Resurserna för att utveckla modulen för första orderacceptans var begrÀnsade. Det var nödvÀndigt att göra mycket, snabbt och med ett litet team. Det lilla teamet bestÄr av tvÄ utvecklare som lade grunden för hela det framtida systemet.
Deras första beslut avgjorde teknikstackens framtida öde:
Backend pÄ ASP.NET MVC, C# sprÄk. Utvecklarna var dotnet-utvecklare, och den hÀr stacken var bekant och trevlig för dem.
Frontend pÄ Bootstrap och JQuery: anvÀndargrÀnssnitt för anpassade stilar och skript.
MySQL-databas: inga licenskostnader, enkel att anvÀnda.
Servrar pÄ Windows Server, eftersom .NET bara kunde vara under Windows (Vi kommer inte att diskutera Mono).
Fysiskt uttrycktes allt detta i den "dedikerade servern hos vÀrden".
Arkitektur för orderacceptansapplikationen
DÄ pratade alla redan om mikrotjÀnster, och SOA hade anvÀnts i stora projekt i ungefÀr 5 Är; till exempel slÀpptes WCF 2006. Men dÄ valde de en pÄlitlig och beprövad lösning.
HÀr Àr det.

Asp.Net MVC Àr en Razor som producerar en serverrenderad HTML-sida nÀr den begÀrs frÄn ett formulÀr eller en klient. PÄ klienten visar CSS- och JS-skript redan information och kör, om nödvÀndigt, AJAX-förfrÄgningar via JQuery.
FörfrÄgningar pÄ servern gÄr till *Controller-klasserna, dÀr metoden bearbetar och genererar den slutliga HTML-sidan. Styrenheter gör förfrÄgningar till ett logiklager som kallas *TjÀnster. Var och en av tjÀnsterna motsvarade nÄgon aspekt av verksamheten:
Till exempel tillhandahöll DepartmentStructureService information om pizzerior, uppdelade efter avdelning. En avdelning Àr en grupp pizzerior som drivs av en franchisetagare.
ReceivingOrdersService accepterade och berÀknade ordersammansÀttningen.
Och SmsService skickade SMS genom att anropa API-tjÀnster för att skicka SMS.
TjÀnsterna bearbetade data frÄn databasen och lagrade affÀrslogik. Varje tjÀnst hade ett eller flera *Repositories med motsvarande namn. De innehöll redan frÄgor till lagrade procedurer i databasen och ett lager av mappers. Butikerna innehöll affÀrslogik, sÀrskilt de som producerade rapporteringsdata. ORM anvÀndes inte, alla förlitade sig pÄ handskriven SQL.
Det fanns ocksÄ ett lager av domÀnmodell och vanliga hjÀlpklasser, till exempel Order-klassen, som lagrade ordningen. DÀr, i lagret, fanns en hjÀlpfunktion för att konvertera visningstexten enligt den valda valutan.
Allt detta kan representeras av följande modell:

OrdervÀg
LÄt oss titta pÄ en förenklad inledande vÀg för att skapa en sÄdan order.

Ursprungligen var webbplatsen statisk. Den hade priser pĂ„ sig, och högst upp stod ett telefonnummer och orden âOm du vill ha pizza, ring det hĂ€r numret och bestĂ€llâ. För att bestĂ€lla behöver vi implementera ett enkelt flöde:
Kunden gÄr till en statisk webbplats med priser, vÀljer produkter och ringer numret som anges pÄ webbplatsen.
Kunden anger vilka produkter hen vill lÀgga till i bestÀllningen.
Anger sin adress och namn.
Operatören accepterar ordern.
Ordern visas i grÀnssnittet för accepterade ordrar.
Allt börjar med menyvisningen. En inloggad anvÀndare/operatör accepterar endast en bestÀllning Ät gÄngen. DÀrför kan utkastkorgen lagras i sin session (anvÀndarens session lagras i minnet). Det finns ett varukorgsobjekt som innehÄller produkter och kundinformation.
Kunden namnger produkten, operatören trycker + bredvid produkten, och en begÀran skickas till servern. Information om produkten hÀmtas frÄn databasen och information om produkten lÀggs till i varukorgen.

Notera. Ja, hÀr behöver du inte dra ut produkten ur databasen, utan överföra den frÄn front-end. Men för tydlighetens skull visade jag sökvÀgen frÄn databasen.
Ange sedan klientens adress och namn.

NÀr du klickar pÄ "Skapa bestÀllning":
Vi skickar begÀran till OrderController.SaveOrder().
Vi fÄr varukorgen frÄn sessionen, den innehÄller produkter i den mÀngd vi behöver.
Vi kompletterar Cart med information om klienten och skickar den till AddOrder-metoden i ReceivingOrderService-klassen, dÀr den sparas i databasen.
Databasen innehÄller tabeller med en order, ordersammansÀttning, klient, och de Àr alla sammankopplade.
OrdervisningsgrÀnssnittet hÀmtar de senaste ordrarna och visar dem.
Nya moduler
Att ta emot ordern var viktigt och nödvÀndigt. Du kan inte driva en pizzarestaurang om du inte har ett system för att ta emot bestÀllningar att sÀlja den till. DÀrför började systemet fÄ funktionalitet - ungefÀr frÄn 2012 till 2015. Under denna tid dök mÄnga olika systemblock upp, som jag kommer att kalla moduler, i motsats till begreppet tjÀnst eller produkt.
En modul Àr en uppsÀttning funktioner som förenas av ett gemensamt affÀrsmÄl. Samtidigt Àr de fysiskt placerade i samma applikation.
Moduler kan kallas block i systemet. Till exempel Àr detta en rapporteringsmodul, administratörsgrÀnssnitt, , auktorisering. Dessa Àr alla olika anvÀndargrÀnssnitt, vissa har till och med olika visuella stilar. Dessutom sker allt inom ramen för en enda applikation, en enda process.
Tekniskt sett var modulerna utformade som Area (denna idĂ© fanns till och med kvar i ). Det fanns separata filer för frontend, modeller och Ă€ven deras egna kontrollklasser. Som ett resultat omvandlades systemet frĂ„n dettaâŠ

...in i detta:

Vissa moduler implementeras som separata webbplatser (körbara projekt), pÄ grund av helt separat funktionalitet och delvis pÄ grund av nÄgot separat, mer fokuserad utveckling. Detta:
Plats - webbplatsen dodopizza.ru.
ExportLadda ner rapporter frÄn Dodo IS för 1C.
Personlig â den anstĂ€lldes personliga konto. Den utvecklades separat och har sin egen ingĂ„ngspunkt och separata design.
fs â ett projekt för att hosta statiskt innehĂ„ll. Senare övergick vi frĂ„n det och flyttade allt statiskt innehĂ„ll till Akamai CDN.
De ÄterstÄende blocken fanns i BackOffice-applikationen.

Förklaring av namnen:
Kassör â Restaurangens kassa.
ShiftManager â grĂ€nssnitt för rollen âShift Managerâ: operativ statistik om pizzeriaförsĂ€ljning, möjlighet att lĂ€gga till produkter pĂ„ en stopplista, Ă€ndra en bestĂ€llning.
OfficeManager â grĂ€nssnitt för rollen "Pizzeriachef" och "Franchisetagare". Det hĂ€r avsnittet innehĂ„ller funktioner för att skapa en pizzeria, dess bonuskampanjer, anstĂ€lla och arbeta med anstĂ€llda samt rapporter.
PublicScreens - grÀnssnitt för TV-apparater och surfplattor som hÀnger pÄ pizzerior. TV-apparaterna visar menyer, reklaminformation och orderstatus vid leverans.
De anvÀnde ett gemensamt servicelager, ett gemensamt block av Dodo.Core-domÀnklasser och en gemensam bas. Ibland kunde de ocksÄ leda genom gÄngar till varandra. Inklusive separata webbplatser, sÄsom dodopizza.ru eller personal.dodopizza.ru, anvÀndes Àven allmÀnna tjÀnster.
NÀr nya moduler dök upp försökte vi ÄteranvÀnda sÄ mycket som möjligt den redan skapade koden för tjÀnster, lagrade procedurer och tabeller i databasen.
För att bÀttre förstÄ skalan pÄ de moduler som tillverkats i systemet, hÀr Àr ett diagram frÄn 2012 med utvecklingsplaner:

à r 2015 var allt pÄ ritbordet och Ànnu mer var i produktion.
Ordermottagning har utvecklats till ett separat kontaktcenterblock, dÀr ordern accepteras av en operatör.
Offentligt tillgÀngliga skÀrmar med menyer och information har dykt upp pÄ pizzerior.
Köket har en modul som automatiskt spelar upp röstmeddelandet âNy pizzaâ nĂ€r en ny bestĂ€llning tas emot, och skriver Ă€ven ut en faktura till budfirman. Detta förenklar processerna i köket avsevĂ€rt och gör att de anstĂ€llda inte behöver bli distraherade av ett stort antal enkla operationer.
Leveransblocket blev en separat leveranskassa, dÀr ordern gavs till kuriren som tidigare hade börjat sitt skift. Hans arbetstid beaktades vid berÀkningen av hans lön.
Parallellt, frÄn 2012 till 2015, dök fler Àn 10 utvecklare upp, 35 pizzerior öppnade, systemet utökades till RumÀnien och förbereddes för öppningspunkter i USA. Utvecklarna hanterade inte lÀngre alla uppgifter, utan var indelade i team. var och en specialiserad pÄ sin egen del av systemet.
Problem
Inklusive pÄ grund av arkitekturen (men inte bara).
Kaos i basen
En bas Àr praktiskt. Konsekvens kan uppnÄs i det, och detta görs med hjÀlp av de verktyg som Àr inbyggda i relationsdatabaser. Den Àr bekant och bekvÀm att arbeta med, sÀrskilt om det finns fÄ tabeller och lite data.
Men under fyra Ärs utveckling innehöll databasen cirka 4 tabeller och 600 lagrade procedurer, varav mÄnga ocksÄ innehöll logik. TyvÀrr ger lagrade procedurer inte mycket fördelar nÀr man arbetar med MySQL. De cachas inte av databasen, och att lagra logik i dem komplicerar utveckling och felsökning. Att ÄteranvÀnda kod Àr ocksÄ svÄrt.
MĂ„nga tabeller hade inte lĂ€mpliga index., nĂ„gonstans, tvĂ€rtom, fanns det mĂ„nga index, vilket gjorde insĂ€ttningen svĂ„r. Det var nödvĂ€ndigt att modifiera cirka 20 tabeller â transaktionen för att skapa en order kunde ta cirka 3â5 sekunder.
Uppgifterna i tabellerna var inte alltid i den mest lÀmpliga formen.. NÄgonstans mÄste avnormalisering ske. En del av den regelbundet mottagna datan fanns i en kolumn i form av en XML-struktur, vilket ökade exekveringstiden, förlÀngde frÄgor och komplicerade utvecklingen.
VÀldigt mÄnga Àndringar gjordes i samma tabeller heterogena förfrÄgningar. PopulÀra bord, som det som nÀmnts ovan, drabbades sÀrskilt hÄrt ordrar eller bord pizzeria. De anvÀndes för att visa operativa grÀnssnitt i köket, analyser. Webbplatsen kontaktade dem ocksÄ (), dÀr det plötsligt kan komma in mÄnga förfrÄgningar nÀr som helst.
Uppgifterna aggregerades inte. och mÄnga berÀkningar gjordes i farten med hjÀlp av databasen. Detta skapade onödiga berÀkningar och extra belastning.
Ofta hamnade koden i databasen nÀr den inte behövde göra det. NÄgonstans fanns det inte tillrÀckligt med bulkoperationer, nÄgonstans skulle det vara nödvÀndigt att dela upp en förfrÄgan i flera via koden för att pÄskynda och öka tillförlitligheten.
SammanhÄllning och intrassling i kod
Modulerna som skulle ansvara för sin del av verksamheten gjorde det inte Àrligt.. NÄgra av dem hade dubbletter av funktioner för roller. Till exempel var en lokal marknadsförare som ansvarar för nÀtverkets marknadsföringsaktivitet i sin stad tvungen att anvÀnda bÄde grÀnssnittet "Admin" (för att stÀlla in kampanjer) och grÀnssnittet "Office Manager" (för att se kampanjernas inverkan pÄ verksamheten). Naturligtvis anvÀnde bÄda modulerna internt en tjÀnst som fungerade med bonuskampanjer.
TjÀnster (klasser inom ett enda monolitiskt stort projekt) skulle kunna anropa varandra för att berika sina data.
Med sjÀlva modellklasserna som lagrar data, arbetet med koden gjordes annorlunda. NÄgonstans fanns det konstruktörer genom vilka man kunde ange obligatoriska fÀlt. NÄgonstans gjordes detta genom offentliga fastigheter. Naturligtvis var det varierande att hÀmta och omvandla data frÄn databasen.
Logiken fanns antingen i styrenheter eller i serviceklasser.
Det hÀr verkar vara mindre problem, men de saktade ner utvecklingen avsevÀrt och minskade kvaliteten, vilket ledde till instabilitet och buggar.
Komplexiteten i storskalig utveckling
SvÄrigheter uppstod ocksÄ i sjÀlva utvecklingen. Det var nödvÀndigt att skapa olika block i systemet, och parallellt. Det blev allt svÄrare att anpassa behoven hos varje komponent till en enda kodbas. Det var inte lÀtt att komma överens och tillfredsstÀlla alla komponenter samtidigt. Till detta kom tekniska begrÀnsningar, sÀrskilt i basen och frontend. Det var nödvÀndigt att överge JQuery till förmÄn för ramverk pÄ hög nivÄ, sÀrskilt nÀr det gÀller klienttjÀnster (webbplats).
Vissa delar av systemet skulle kunna anvÀnda databaser som Àr mer lÀmpade för detta. Till exempel hade vi senare ett prejudikat dÀr vi bytte frÄn Redis till CosmosDB för att lagra ordervarukorgen.
Team och utvecklare som arbetade inom sitt omrÄde ville helt klart ha mer autonomi för sina tjÀnster, bÄde vad gÀller utveckling och utrullning. Konflikter vid sammanslagningar, problem vid utgÄvor. Om detta problem Àr obetydligt för 5 utvecklare, sÄ skulle allt bli allvarligare med 10, och sÀrskilt med den planerade tillvÀxten. Och framöver skulle utvecklingen av en mobilapplikation ske (den startade 2017, och 2018 fanns det ).
Olika delar av systemet krÀvde olika stabilitetsindikatorer., men pÄ grund av systemets starka anslutning kunde vi inte sÀkerstÀlla detta. Ett fel vid utvecklingen av en ny funktion i administratörspanelen kan mycket vÀl ha pÄverkat orderacceptansen pÄ webbplatsen, eftersom koden Àr gemensam och ÄteranvÀndbar, databasen och data Àr ocksÄ desamma.
Det skulle förmodligen vara möjligt att undvika dessa fel och problem inom ramen för en sÄdan monolitisk-modulÀr arkitektur: skapa en ansvarsseparation, refaktorera bÄde kod och databas, tydligt separera lagren frÄn varandra och övervaka kvaliteten varje dag. De valda arkitektoniska lösningarna och fokus pÄ snabb expansion av systemets funktionalitet ledde dock till problem vad gÀller stabilitet.
Hur bloggen The Power of the Mind fick kassaregister pÄ restauranger att gÄ sönder
Om pizzakedjans (och belastningens) tillvÀxt fortsatte i samma takt, skulle nedgÄngarna efter en tid vara sÄdana att systemet inte skulle stiga. Följande berÀttelse illustrerar vÀl de problem vi började möta 2015.
I bloggen «"Det fanns en widget som visade intÀktsdata för hela nÀtverket för Äret. Widgeten Ätkom till det publika Dodo API:et, som tillhandahÄller dessa data. Denna statistik finns nu tillgÀnglig pÄ . Widgeten visades pÄ varje sida och gjorde förfrÄgningar med en timer var 20:e sekund. BegÀran gick till api.dodopizza.ru och begÀrde:
antal pizzerior i kedjan;
nÀtverkets totala intÀkter sedan Ärets början;
intÀkter för idag.
BegÀran om intÀktsstatistik gick direkt till databasen och började begÀra data om ordrar, aggregera data i realtid och utfÀrda en summa.
Kassorna pÄ restaurangerna gick till samma bestÀllningsbord, laddade ner listan över accepterade bestÀllningar för idag, och nya bestÀllningar lades till i den. Kassorna gjorde sina förfrÄgningar var 5:e sekund eller nÀr sidan uppdaterades.
Diagrammet sÄg ut sÄ hÀr:

En höst skrev Fjodor Ovchinnikov en lÄng och populÀr artikel pÄ sin blogg. MÄnga kom till bloggen och började lÀsa allt noggrant. Medan varje person som kom lÀste artikeln fungerade intÀktswidgeten korrekt och begÀrde API:et var 20:e sekund.
API:et anropade en lagrad procedur för att berÀkna summan av alla bestÀllningar sedan Ärets början för alla pizzerior i kedjan. Aggregeringen baserades pÄ ordertabellen, som Àr mycket populÀr. Alla kassor pÄ alla restauranger som Àr öppna vid den tiden gÄr dit. Kassorna slutade svara och bestÀllningar accepterades inte. De accepterades inte heller frÄn webbplatsen, dök inte upp i spÄraren och skiftchefen kunde inte se dem i sitt grÀnssnitt.
Detta Àr inte den enda berÀttelsen. Hösten 2015 var systembelastningen kritisk varje fredag. Vi stÀngde av det publika API:et flera gÄnger, och en gÄng var vi till och med tvungna att stÀnga av webbplatsen eftersom ingenting lÀngre hjÀlpte. Det fanns till och med en lista över tjÀnster med den ordning i vilken de skulle stÀngas av vid hög belastning.
FrÄn och med denna tidpunkt börjar vÄr kamp med laster och för stabilisering av systemet (frÄn hösten 2015 till hösten 2018). Det var dÄ det hÀnde.". Senare förekom det Àven enstaka fel, av vilka nÄgra var ganska kÀnsliga, men den allmÀnna perioden av instabilitet kan nu anses vara över.
Snabb tillvÀxt av företag
Varför kunde det inte göras bra direkt? Titta bara pÄ följande grafer.

Ăven under 2014-2015 öppnades det i RumĂ€nien och en öppning i USA förbereddes.
NÀtverket vÀxte mycket snabbt, nya lÀnder öppnade, nya pizzeriaformat dök upp, till exempel en pizzeria öppnade i en food court. Allt detta krÀvde betydande uppmÀrksamhet för att utöka funktionerna hos Dodo IS. Utan alla dessa funktioner, utan spÄrning i köket, redovisning av produkter och förluster i systemet, visning av bestÀllningens leverans i matsalen, skulle vi knappast diskutera den "rÀtta" arkitekturen och det "rÀtta" tillvÀgagÄngssÀttet för utveckling.
Ett annat hinder för en snabb revidering av arkitekturen och, i allmÀnhet, uppmÀrksamhet pÄ tekniska problem var krisen 2014. SÄdana hÀr saker skadar teamens tillvÀxtpotential, sÀrskilt för unga företag som Dodo Pizza.
Snabba lösningar som hjÀlpte
Problem behövde lösas. Konventionellt kan lösningar delas in i tvÄ grupper:
Snabba sÄdana som slÀcker branden och ger oss en liten sÀkerhetsmarginal och köper oss tid att göra förÀndringar.
Systemisk och dÀrför lÄngvarig. Omstrukturering av ett antal moduler, uppdelning av den monolitiska arkitekturen i separata tjÀnster (de flesta av dem Àr inte mikrotjÀnster utan snarare makrotjÀnster, och det finns ett inlÀgg om detta) ).
Den torra listan över snabba förÀndringar Àr följande:
Skala upp masterbasen
Naturligtvis Àr det första som görs för att bekÀmpa belastningen att öka serverkapaciteten. Detta gjordes för masterbasen och för webbservrar. TyvÀrr Àr detta bara möjligt upp till en viss grÀns; efter det blir det för dyrt.
Sedan 2014 har vi gÄtt över till Azure, vi skrev Àven om detta Àmne dÄ i artikeln "". Men efter en serie höjningar nÄdde servrarna för basen en prisgrÀns.
Basrepliker för lÀsning
Vi gjorde tvÄ replikor till basen:
LÀsreplika för referensfrÄgor. Den anvÀnds för att lÀsa kataloger, sÄsom stad, gata, pizzeria, produkter (lÄngsamt Àndrad domÀn), och i de grÀnssnitt dÀr en liten fördröjning Àr acceptabel. Det fanns tvÄ av dessa replikor, vi sÀkerstÀllde deras tillgÀnglighet pÄ samma sÀtt som för mÀstarna.
ReadReplica för rapportfrÄgor. Denna databas hade lÀgre tillgÀnglighet, men alla rapporter gick till den. De kan ha stora förfrÄgningar om enorma datamÀngder, men de pÄverkar Ätminstone inte huvuddatabasen och de operativa grÀnssnitten.
Cachar i kod
Det fanns inga cachar i koden nÄgonstans (alls). Detta ledde till ytterligare, inte alltid nödvÀndiga, förfrÄgningar till den laddade databasen. Cachar fanns initialt bÄde i minnet och pÄ en extern cachetjÀnst, som var Redis. Allt ogiltigförklarades av tiden, instÀllningarna var specificerade i koden.
Flera servrar för backend
Applikationens backend behövde ocksÄ skalas för att hantera ökade belastningar. Det var nödvÀndigt att skapa ett kluster frÄn en IIS-server. Vi flyttade frÄn minne till RedisCache, vilket gjorde det möjligt för oss att ha flera servrar bakom en enkel lastbalanserare i runda-rollen. Till en början anvÀndes samma Redis som för cacher, sedan delades den upp i flera.
Som ett resultat blev arkitekturen mer komplex...

...men en del av spÀnningen lÀttade.
Och sedan var det nödvÀndigt att omarbeta de belastade komponenterna, vilket Àr vad vi tog oss an. Vi kommer att prata om detta i nÀsta del.
KĂ€lla: will.com
