История на архитектурата на Dodo IS: Ранен монолит

Или всяка нещастна компания с монолит е нещастна по своему.

Развитието на системата Dodo IS започна веднага, подобно на бизнеса с Dodo Pizza, през 2011 г. Тя се основава на идеята за пълна и тотална цифровизация на бизнес процесите и сами, което още тогава през 2011 г. предизвика много въпроси и скептицизъм. Но вече 9 години вървим по този път - със собствено развитие, започнало с монолит.

Тази статия е „отговор“ на въпросите „Защо да пренаписваме архитектурата и да правим толкова мащабни и дългосрочни промени?“ назад към предишната статия „История на архитектурата на Dodo IS: Пътят на бек офиса“. Ще започна с това как започна разработката на Dodo IS, как изглеждаше оригиналната архитектура, как се появиха новите модули и поради какви проблеми трябваше да се направят мащабни промени.

История на архитектурата на Dodo IS: Ранен монолит

Поредица от статии "Какво е Dodo IS?" разказва за:

  1. Ранен монолит в Dodo IS (2011-2015). (ти си тук)

  2. Пътят на бек офиса: отделни бази и автобус.

  3. Пътеката от страна на клиента: фасада над основата (2016-2017). (В процес...)

  4. Историята на истинските микроуслуги. (2018-2019 г.). (В процес...)

  5. Завършено рязане на монолита и стабилизиране на архитектурата. (В процес...)

Първоначална архитектура

През 2011 г. архитектурата на Dodo IS изглеждаше така:

История на архитектурата на Dodo IS: Ранен монолит

Първият модул в архитектурата е приемане на поръчки. Бизнес процесът беше:

  • клиентът се обажда в пицарията;

  • управителят вдига телефона;

  • приема поръчка по телефона;

  • попълва го паралелно в интерфейса за приемане на поръчки: взема предвид информация за клиента, данни за подробности за поръчката, адрес за доставка. 

Интерфейсът на информационната система изглеждаше така ...

Първа версия от октомври 2011 г.:

Леко подобрен през януари 2012 г

Информационна система за доставка на Dodo Pizza Ресторант за пица

Ресурсите за разработването на първия модул за приемане на поръчки бяха ограничени. Трябваше да направим много, бързо и с малък екип. Малък екип е 2 разработчици, които поставиха основите на цялата бъдеща система.

Първото им решение определи съдбата на технологичния стек:

  • Бекенд на ASP.NET MVC, език C#. Разработчиците бяха dotnetchiki, този стек беше познат и приятен за тях.

  • Frontend на Bootstrap и JQuery: потребителски интерфейси на самостоятелно написани стилове и скриптове. 

  • MySQL база данни: без разходи за лиценз, лесна за използване.

  • Сървъри на Windows Server, защото тогава .NET можеше да бъде само под Windows (няма да обсъждаме Mono).

Физически всичко това се изразяваше в „дедик при домакина“. 

Архитектура на приложението за приемане на поръчки

Тогава всички вече говореха за микроуслуги, а SOA се използваше в големи проекти в продължение на 5 години, например WCF беше пуснат през 2006 г. Но тогава те избраха надеждно и доказано решение.

Ето го.

История на архитектурата на Dodo IS: Ранен монолит

Asp.Net MVC е Razor, който при поискване от формуляр или от клиент изобразява HTML страница със сървърно изобразяване. На клиента CSS и JS скриптовете вече показват информация и, ако е необходимо, изпълняват AJAX заявки чрез JQuery.

Заявките на сървъра завършват в класовете *Controller, където обработката и генерирането на крайната HTML страница се извършва в метода. Контролерите отправят заявки към логически слой, наречен *Services. Всяка от услугите съответства на някакъв аспект от бизнеса:

  • Например DepartmentStructureService даде информация за пицарии, за отдели. Отделът е група от пицарии, управлявани от един франчайзополучател.

  • ReceivingOrdersService прие и изчисли състава на поръчката.

  • И SmsService изпрати SMS чрез извикване на API услуги за изпращане на SMS.

Услугите обработват данни от базата данни, съхраняват бизнес логиката. Всяка услуга имаше едно или повече *Хранилища със съответното име. Те вече съдържаха заявки към съхранени процедури в базата данни и слой от карти. Имаше бизнес логика в хранилищата, особено много в тези, които издаваха отчетни данни. ORM не се използваше, всички разчитаха на ръчно написан sql. 

Имаше също слой на модела на домейна и общи помощни класове, например класът Order, който съхраняваше поръчката. На същото място, в слоя, имаше помощник за конвертиране на текста на дисплея според избраната валута.

Всичко това може да бъде представено от такъв модел:

История на архитектурата на Dodo IS: Ранен монолит

Начин на поръчка

Помислете за опростен първоначален начин за създаване на такава поръчка.

История на архитектурата на Dodo IS: Ранен монолит

Първоначално сайтът беше статичен. На него имаше цени, а отгоре - телефонен номер и надпис "Ако искате пица - звъннете на номера и поръчайте". За да поръчаме, трябва да внедрим прост поток: 

  • Клиентът посещава статичен сайт с цени, избира продукти и се обажда на посочения в сайта номер.

  • Клиентът назовава продуктите, които иска да добави към поръчката.

  • Дава адреса и името си.

  • Операторът приема поръчката.

  • Поръчката се показва в интерфейса за приети поръчки.

Всичко започва с показване на менюто. Влезлият потребител-оператор приема само една поръчка наведнъж. Следователно, проектът на количката може да се съхранява в неговата сесия (сесията на потребителя се съхранява в паметта). Има обект Cart, съдържащ продукти и информация за клиента.

Клиентът назовава продукта, операторът кликва върху + до продукта и се изпраща заявка до сървъра. Информацията за продукта се извлича от базата данни и информацията за продукта се добавя към количката.

История на архитектурата на Dodo IS: Ранен монолит

Внимание. Да, тук не можете да изтеглите продукта от базата данни, а да го прехвърлите от интерфейса. Но за яснота показах точно пътя от базата данни. 

След това въведете адреса и името на клиента. 

История на архитектурата на Dodo IS: Ранен монолит

Когато щракнете върху „Създаване на поръчка“:

  • Заявката се изпраща до OrderController.SaveOrder().

  • Получаваме количка от сесията, има продукти в нужното ни количество.

  • Допълваме количката с информация за клиента и я предаваме на метода AddOrder на класа ReceivingOrderService, където се записва в базата данни. 

  • В базата данни има таблици с поръчката, състава на поръчката, клиента и всички те са свързани.

  • Интерфейсът за показване на поръчки отива и изважда най-новите поръчки и ги показва.

Нови модули

Поемането на поръчката беше важно и необходимо. Не можете да правите бизнес с пица, ако нямате поръчка за продажба. Следователно системата започна да придобива функционалност - приблизително от 2012 до 2015 г. През това време се появиха много различни блокове на системата, които ще нарека модули, за разлика от концепцията за услуга или продукт. 

Модулът е набор от функции, които са обединени от някаква обща бизнес цел. В същото време те са физически в едно и също приложение.

Модулите могат да бъдат наречени системни блокове. Например, това е модул за отчитане, администраторски интерфейси, проследяване на храна в кухнята, упълномощаване. Това са различни потребителски интерфейси, някои дори имат различни визуални стилове. В същото време всичко е в рамките на едно приложение, един работещ процес. 

Технически модулите бяха проектирани като Area (такава идея дори остана в asp.net ядро). Имаше отделни файлове за интерфейса, моделите, както и техните собствени класове контролери. В резултат на това системата беше трансформирана от това ...

История на архитектурата на Dodo IS: Ранен монолит

...в това:

История на архитектурата на Dodo IS: Ранен монолит

Някои модули се изпълняват от отделни сайтове (изпълним проект), поради напълно отделна функционалност и отчасти поради малко отделна, по-фокусирана разработка. Това:

  • Място - първа версия сайт dodopizza.ru.

  • Износ: качване на отчети от Dodo IS за 1C. 

  • Персонален - лична сметка на служителя. Той е разработен отделно и има собствена входна точка и отделен дизайн.

  • fs — проект за хостване на статики. По-късно се отдалечихме от него, премествайки цялата статика в Akamai CDN. 

Останалите блокове бяха в приложението BackOffice. 

История на архитектурата на Dodo IS: Ранен монолит

Обяснение на името:

  • Касиер - Касиер в ресторант.

  • ShiftManager - интерфейси за ролята "Мениджър на смяна": оперативна статистика за продажбите на пицария, възможност за поставяне на продукти в стоп списъка, промяна на реда.

  • OfficeManager - интерфейси за ролите "Управител на пицария" и "Франчайзополучател". Тук са събрани функции за създаване на пицария, нейните бонус промоции, прием и работа със служители, отчети.

  • PublicScreens - интерфейси за телевизори и таблети, окачени в пицарии. Телевизорите показват менюта, рекламна информация, статус на поръчката при доставка. 

Те използваха общ сервизен слой, общ блок на клас на домейн Dodo.Core и обща база. Понякога те все още можеха да водят по преходите един към друг. Включително отделни сайтове, като dodopizza.ru или personal.dodopizza.ru, преминаха към общите услуги.

Когато се появиха нови модули, ние се опитахме да използваме максимално вече създадения код от услуги, съхранени процедури и таблици в базата данни. 

За по-добро разбиране на мащаба на модулите, направени в системата, ето диаграма от 2012 г. с планове за развитие:

История на архитектурата на Dodo IS: Ранен монолит

До 2015 г. всичко беше на картата и още повече беше в производство.

  • Приемането на поръчки прерасна в отделен блок на Контактния център, където поръчката се приема от оператора.

  • В пицариите имаше окачени публични екрани с менюта и информация.

  • Кухнята разполага с модул, който автоматично пуска гласово съобщение "Нова пица" при постъпване на нова поръчка, както и отпечатване на фактура за куриера. Това значително опростява процесите в кухнята, позволява на служителите да не се разсейват от голям брой прости операции.

  • Звеното за доставка се превърна в отделна каса за доставка, където поръчката се издава на куриера, който преди това е поел смяната. Работното му време е взето предвид при изчисляване на заплатите. 

Успоредно с това от 2012 г. до 2015 г. се появиха повече от 10 разработчици, отвориха се 35 пицарии, внедриха системата в Румъния и се подготвиха за откриването на търговски обекти в Съединените щати. Разработчиците вече не се занимаваха с всички задачи, а бяха разделени на екипи. всяка специализирана в своята част от системата. 

Проблеми

Включително и заради архитектурата (но не само).

Хаос в основата

Една основа е удобна. В него може да се постигне съгласуваност и то за сметка на инструменти, вградени в релационни бази данни. Работата с него е позната и удобна, особено ако има малко таблици и малко данни.

Но за 4 години разработка се оказа, че базата данни има около 600 таблици, 1500 съхранени процедури, много от които също имат логика. Уви, съхранените процедури не носят много предимства при работа с MySQL. Те не се кешират от базата и съхраняването на логика в тях усложнява разработката и отстраняването на грешки. Повторното използване на код също е трудно.

Много таблици нямаха подходящи индекси, някъде, напротив, имаше много индекси, което затрудни вмъкването. Беше необходимо да се модифицират около 20 таблици - транзакцията за създаване на поръчка можеше да отнеме около 3-5 секунди. 

Данните в таблиците не винаги са били в най-подходящата форма. Някъде беше необходимо да се направи денормализация. Част от редовно получаваните данни бяха в колона под формата на XML структура, което увеличи времето за изпълнение, удължи заявките и усложни разработката.

Към същите таблици бяха произведени много разнородни заявки. Популярните маси пострадаха особено, като таблицата, спомената по-горе. заповеди или маси пицария. Използвани са за показване на оперативни интерфейси в кухнята, анализи. Друг сайт се свърза с тях (dodopizza.ru), където във всеки един момент могат внезапно да дойдат много заявки. 

Данните не бяха обобщени и много изчисления се извършваха в движение с помощта на базата. Това създаде ненужни изчисления и допълнително натоварване. 

Често кодът отиваше в базата данни, когато не можеше да го направи. Някъде нямаше достатъчно групови операции, някъде щеше да е необходимо една заявка да се разпространи на няколко чрез кода, за да се ускори и повиши надеждността. 

Кохезия и обфускация в кода

Модулите, които трябваше да отговарят за своята част от бизнеса, не го направиха честно. Някои от тях имаха дублиране на функции за роли. Например, местен маркетолог, който отговаря за маркетинговата дейност на мрежата в неговия град, трябваше да използва както интерфейса „Администратор“ (за създаване на промоции), така и интерфейса „Офис мениджър“ (за да види въздействието на промоциите върху бизнеса). Разбира се, вътре в двата модула се използва една и съща услуга, която работи с бонус промоции.

Услугите (класове в рамките на един монолитен голям проект) могат да се обаждат взаимно, за да обогатят своите данни.

Със самите моделни класове, които съхраняват данни, работата в кода беше извършена по различен начин. Някъде имаше конструктори, чрез които можеше да се задават задължителни полета. Някъде това ставаше чрез обществени имоти. Разбира се, получаването и трансформирането на данни от базата данни беше различно. 

Логиката беше или в контролерите, или в сервизните класове. 

Това изглеждат дребни проблеми, но значително забавиха развитието и намалиха качеството, което доведе до нестабилност и грешки. 

Сложността на голямо развитие

В самото развитие възникнаха трудности. Трябваше да се направят различни блокове от системата и то паралелно. Поставянето на нуждите на всеки компонент в един код ставаше все по-трудно. Не беше лесно да се съгласим и да угодим на всички компоненти едновременно. Към това бяха добавени ограничения в технологиите, особено по отношение на основата и интерфейса. Беше необходимо да се изостави jQuery към рамки на високо ниво, особено по отношение на клиентските услуги (уебсайт).

В някои части на системата могат да се използват по-подходящи бази данни за това.. Например, по-късно имахме случай на използване на преместване от Redis към CosmosDB, за да съхраняваме кошница за поръчки. 

Екипите и разработчиците, участващи в своята област, очевидно искаха повече автономия за своите услуги, както по отношение на разработката, така и по отношение на внедряването. Сливане на конфликти, освобождаване на проблеми. Ако за 5 разработчици този проблем е незначителен, то с 10 и още повече с планирания растеж всичко ще стане по-сериозно. А предстоеше разработката на мобилно приложение (започна през 2017 г., а през 2018 г. беше голямо падане). 

Различните части на системата изискват различни нива на стабилност, но поради силната свързаност на системата не можахме да предоставим това. Грешка в разработката на нова функция в админ панела може да е възникнала при приемането на поръчка в сайта, тъй като кодът е общ и може да се използва повторно, базата данни и данните също са едни и същи.

Вероятно би било възможно да се избегнат тези грешки и проблеми в рамките на такава монолитно-модулна архитектура: направете разделение на отговорностите, преработете кода и базата данни, ясно отделете слоевете един от друг, наблюдавайте качеството всеки ден. Но избраните архитектурни решения и фокусът върху бързото разширяване на функционалността на системата доведоха до проблеми по отношение на стабилността.

Как блогът Power of the Mind постави касите в ресторантите

Ако растежът на мрежата от пицарии (и натоварването) продължи със същите темпове, след известно време спадовете ще бъдат такива, че системата няма да се покачи. Добре илюстрира проблемите, с които започнахме да се сблъскваме до 2015 г., ето такава история. 

В блога "Сила на ума” беше джаджа, която показваше данни за приходите за годината на цялата мрежа. Джаджата получи достъп до публичния API на Dodo, който предоставя тези данни. Тази статистика в момента е достъпна на http://dodopizzastory.com/. Джаджата се показваше на всяка страница и правеше заявки на таймер на всеки 20 секунди. Заявката отиде до api.dodopizza.ru и поиска:

  • броя на пицариите в мрежата;

  • общи приходи от мрежата от началото на годината;

  • приходи за днес.

Заявката за статистически данни за приходите отиде направо в базата данни и започна да иска данни за поръчките, да събира данни в движение и да издава сумата. 

Касите в ресторантите отиваха на една и съща маса с поръчки, разтоварваха списък с поръчки, получени за днес, и към него се добавяха нови поръчки. Касовите апарати правеха своите заявки на всеки 5 секунди или при опресняване на страницата.

Диаграмата изглеждаше така:

История на архитектурата на Dodo IS: Ранен монолит

Една есен Фьодор Овчинников написа дълга и популярна статия в своя блог. Много хора дойдоха в блога и започнаха да четат всичко внимателно. Докато всеки от хората, които дойдоха, четеше статията, джаджата за приходи работеше правилно и изискваше API на всеки 20 секунди.

API нарече съхранена процедура за изчисляване на сумата от всички поръчки от началото на годината за всички пицарии в мрежата. Обобщаването се основава на таблицата с поръчки, която е много популярна. Към него отиват всички каси на всички отворени ресторанти по това време. Касите спряха да отговарят, поръчки не се приемаха. Те също не бяха приети от сайта, не се появиха на тракера, началникът на смяна не можеше да ги види в своя интерфейс. 

Това не е единствената история. До есента на 2015 г. всеки петък натоварването на системата беше критично. Няколко пъти изключихме публичния API, а веднъж дори се наложи да изключим сайта, защото нищо не помогна. Имаше дори списък с услуги със заповед за изключване при големи натоварвания.

От тук нататък започва нашата борба с натоварванията и стабилизирането на системата (от есента на 2015 до есента на 2018). Тогава се случи"голямо падане". Освен това понякога се случваха повреди, някои бяха много чувствителни, но общият период на нестабилност вече може да се счита за отминал.

Бърз растеж на бизнеса

Защо не можеше да стане веднага? Просто погледнете следващите диаграми.

История на архитектурата на Dodo IS: Ранен монолит

Също през 2014-2015 г. имаше откриване в Румъния и се подготвяше откриване в САЩ.

Мрежата се разрасна много бързо, бяха открити нови държави, появиха се нови формати на пицарии, например, пицария беше открита в заведението за хранене. Всичко това изисква значително внимание специално към разширяването на функциите на Dodo IS. Без всички тези функции, без проследяване в кухнята, отчитане на продукти и загуби в системата, показване на издаване на поръчка в залата за хранене, едва ли бихме говорили за „правилната“ архитектура и „правилния“ подход към развитие сега.

Друга пречка за навременното преразглеждане на архитектурата и като цяло вниманието към техническите проблеми беше кризата от 2014 г. Неща като това се отразяват силно на възможностите за растеж на екипите, особено за млад бизнес като Dodo Pizza.

Бързи решения, които помогнаха

Проблемите се нуждаят от решения. Условно решенията могат да бъдат разделени на 2 групи:

  • Бързи, които гасят огъня и дават малка граница на безопасност и ни печелят време за промяна.

  • Системен и следователно дълъг. Реинженеринг на редица модули, разделяне на монолитна архитектура на отделни услуги (повечето от тях изобщо не са микро, а по-скоро макро услуги и има нещо в това Репортаж на Андрей Моревски). 

Сухият списък с бързи промени е както следва:

Увеличаване на основния основен мащаб

Разбира се, първото нещо, което се прави за справяне с натоварванията, е увеличаването на капацитета на сървъра. Това беше направено за основната база данни и за уеб сървърите. Уви, това е възможно само до определен лимит, след това става твърде скъпо.

От 2014 г. се преместихме в Azure, ние също писахме за тази тема по това време в статията „Как Dodo Pizza доставя пица с помощта на Microsoft Azure Cloud". Но след поредица от увеличения на сървъра за базата, те се сблъскаха с цената. 

Базови реплики за четене

За основата са направени две реплики:

ReadReplica за референтни заявки. Използва се за четене на директории, тип, град, улица, пицария, продукти (бавно променен домейн) и в тези интерфейси, където е допустимо малко забавяне. Имаше 2 от тези реплики, ние осигурихме тяхната наличност по същия начин като майсторите.

ReadReplica за заявки за отчет. Тази база данни имаше по-ниска наличност, но всички отчети отиваха към нея. Нека имат тежки заявки за огромни преизчисления на данни, но те не засягат основната база данни и оперативните интерфейси. 

Кешира в код

Нямаше кеш никъде в кода (изобщо). Това доведе до допълнителни, не винаги необходими заявки към заредената база данни. Първо кешовете бяха както в паметта, така и във външна кеш услуга, това беше Redis. Всичко беше невалидно от времето, настройките бяха посочени в кода.

Множество бекенд сървъри

Бекендът на приложението също трябваше да бъде мащабиран, за да се справи с увеличените натоварвания. Беше необходимо да се направи клъстер от един iis-сървър. Пренасрочихме сесия на приложението от паметта към RedisCache, което направи възможно създаването на няколко сървъра зад прост балансьор на натоварването с кръгов режим. Първоначално същият Redis беше използван като за кешове, след което беше разделен на няколко. 

В резултат на това архитектурата стана по-сложна ...

История на архитектурата на Dodo IS: Ранен монолит

… но част от напрежението беше премахнато.

И тогава беше необходимо да преработим заредените компоненти, което ние предприехме. Ще говорим за това в следващата част.

Източник: www.habr.com

Добавяне на нов коментар