Что Делать, Когда Все Советуют Разбить Проект На Микросервисы. И Ты Не Готов

Монолит часто обсуждается в негативном ключе.

Но не каждый сможет сразу перейти на микросервисы — и это не первая команда и компания, которая делится опытом построения «переходного звена»: модульной архитектуры.

Давайте подробнее рассмотрим, как создаются подобные проекты.



Что делать, когда все советуют разбить проект на микросервисы.
</p><p>
 И ты не готов

Недавно посмотрел два репортажа на эту тему: от Юлия Николаева из iSpring и Антон Губарев из Скайенга.

Поскольку мы с Антоном работаем в одной компании, я решил поговорить с Юлей для своего подкаста.

Ниже приводится текстовая расшифровка основных идей беседы.



«Мы взвесили причины, по которым мы хотели перейти на микросервисы, и реальные проблемы, которые мешали нам жить в монолитной архитектуре».

Юлия: Когда мы приступаем к разработке приложения, функционала мало и его нужно запустить как можно быстрее.

Но если дело пойдет дальше, на определенном этапе кодовая база начинает бесконтрольно разрастаться — и не принимается никаких новых архитектурных решений, которые бы сдерживали этот хаос.

Так произошло и с нашим проектом: он стартовал в 2009 году, но долгое время не было людей и времени заниматься неконтролируемым ростом кодовой базы.

И к 2018 году стало ясно: пора как-то реструктуризировать монолит. Когда встал вопрос, что делать с нашим монолитом, мы, естественно, подумали и о микросервисах.

Но.

Распределенная архитектура имеет свои накладные расходы.

А мы не были к этому готовы.

Например, у нас вообще не было devops-команды.



Не было нормального CI/CD как такового, не было ни Docker, ни Kubernetes — вообще ничего такого.

Плюс у нас не возникло острой проблемы масштабирования: таких нагрузок нет. Что действительно мешало, так это очень тесная связанность кода.

Изменение одной части часто приводило к ошибкам в куче других мест, плюс мы не могли правильно разработать некоторые фичи.

Вернее, мы могли бы развиваться параллельно, но объединиться.

на этом этапе могут возникнуть неожиданные логические конфликты.

Мы поняли: решив острые проблемы, мы можем какое-то время жить в монолите.

Но мы также подумали о том, как облегчить потом переход на микросервисную архитектуру? Наша тематика – дистанционное обучение.

И здесь можно выделить модули: геймификация, управление курсами, статистика, отчеты — такие поднаправления внутри основной предметной области.

Мы начали представлять наше приложение как набор независимых модулей.

Для нас все усугублялось тем, что кусок наследия был написан на Symfony 1.4 с использованием Propel 2, который не так-то просто использовать в парадигме DDD (Domain Driven Design).

И нам очень хотелось DDD. Для этого хорошо подходит ORM Doctrine, которую нелегко привязать к наследию.



Мы решили скрестить змею с ёжиком и теперь живём с двумя рамками.

Сейчас я думаю, что мы могли бы пойти другим путем: не привязывать новый фреймворк, не привязывать доктрину, а просто попытаться сделать чистую архитектуру на том, что у нас есть.

По сути, наша проблема заключалась в том, что бизнес-логика была в пирах Propel, моделях, контроллерах — везде, где ее не было.

А можно было, как и ребята из еще одна проблема , на старом фреймворке сначала выберите модули, попробуйте реализовать архитектуру, а потом перетащите все это в новый фреймворк.

Возможно, так было бы проще и быстрее.



«Буквально каждый день, на каждом шагу мы сталкивались со множеством вопросов, которые не знали, как решить»

Мы, тимлиды, сначала вместе, а потом втроем разрабатывали функционал в этой новой архитектуре.

Было много вопросов.

Начав с того, как организовать папки в проекте: однако, когда они это сделали и поняли, как и зачем, то нашли в Интернете просто массу источников, в которых все это было написано.

Наверное не так искали)

При этом часть команды разработчиков ушла в девопс.

Они начали переходить на кубернетес, докер и все такое.

Чтобы погрузить в тему остальную команду, мы подготовили специальный курс по DDD, который все прошли за пару выходных.

Нашим разработчикам пришлось реализовать два контекста — напрямую закодировать их.

Мы, конечно, поделились всеми идеями, но получили обратную связь, которая была очень сложной и «мозговой».

Да, поначалу тяжело.

Но когда разработчик опробует и примет новую архитектуру, и к нему придет фича, которую нужно сделать в легаси, у него сразу возникнет мысль: «Как мне сделать так, чтобы это было похоже на новую архитектуру».

Это очень сильно меняет ваше мышление.

Уже есть примеры, что, переделывая кусок, его стараются упорядочить и покрыть интерфейсами, чтобы он был более-менее похож на чистую архитектуру, чтобы потом было легче удалить.



На все про все у нас ушло, наверное, месяца три с половиной.

Мы не только перепроектировали архитектуру, но и реализовали новый функционал с удалением частей из наследия.

Плюс все эти три месяца команда devops настраивала нашу инфраструктуру, чтобы мы могли взлететь в Kubernetes. Сейчас в новом коде у нас 14-15 контекстов плюс большое наследие, где можно было выделить несколько контекстов.

Хотя большинство контроллеров остаются в Legacy, было неясно, куда поместить их код, поскольку контроллеры могут получать данные и манипулировать ими из разных контекстов.

И ура! Мы можем писать функции в 3-4 потока командой из 12 бэкендеров.

Сергей: В PHP мы не можем дать программисту пощечину и запретить ему использовать какой-то внутренний класс другого модуля.

Это все держится на устных договоренностях внутри команды.

Как все это организовать? Юлия: Такую архитектуру сложно контролировать.

Все разработчики должны быть погружены в эту идею и поделиться ею.

Через эти грабли мы прошли поначалу, когда привлекали команду.

Многим казалось, что это дикий оверинжиниринг: «Почему это так сложно, давайте не будем делать так».

Но через некоторое время, почувствовав разницу между написанием кода в новой архитектуре и в устаревшей, все вдохновились.

Некоторое время мы жили по договоренностям и старались все отслеживать во время проверки.

Но теперь между модулями есть API, через который они взаимодействуют. И мы не имеем права обойти это API и выдернуть что-то из модуля.



Мы нашли такую утилиту — департамент — статический анализатор кода, который помогает контролировать зависимости.

Утилита встроена в наш CI/CD и запускается автоматически: по сути, с ее помощью контролируется большая часть соглашений.

Логические ошибки мы пытаемся выловить на архитектурном ревью: это собрание, на котором разработчик, который будет нарезать фичу, представляет схемы, где все уже разбито на слои.

Там сразу понятно, что в каком контексте, какие отношения и т.д. Сергей: Как поддерживать такую архитектуру? Юлия: У нас есть статьи о том, как наслаивать классы.

Информацию о выделении контекстов очень сложно формализовать, поскольку для каждого признака это достаточно специфично.

Здесь не может быть никаких правил, это всего лишь архитектурный шаблон.

Сергей: У нас одна база данных, у нас один проект. Что мне делать, если моему модулю нужны данные из нескольких других контекстов? Юлия: Это один из самых сложных вопросов — наверное, как в модульных, так и в микросервисных архитектурах.

В одном из случаев мы пытались реализовать объединение данных из разных API: делали запросы, объединяли в памяти, сортировали, фильтровали.

Работало ужасно медленно.

Сейчас мы лукарим и читаем из чужих таблиц — это предположение, которое мы пока себе позволяем в связи с тем, что у нас нет ни времени, ни ресурсов, чтобы реализовать его нормальным образом.

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

Сергей: И тут мы подходим к проблеме дублирования кода.

Юлия: Дублирование происходит, например, если у нас есть уровень, который разрешает операции во всех контекстах — и мы дублируем его в каждом контексте.

То есть у нас почти один и тот же код, который спрашивает: «Есть ли у пользователей праваЭ» Но мы считаем, что это вполне приемлемое дублирование.

Сергей: Что делать, если одному модулю необходимо выполнить действие в другом модуле? Например, при совершении платежа, изменении баланса, создании уведомления — это все разные контексты.

Как с этим бороться в монолите? Юлия: Вы перечислили случаи, которые хорошо вписываются в событийную модель.

Мы многое сделали с помощью асинхронных событий.

Если контекст не должен ничего знать о том, что произойдет в других контекстах, то возникнет событие, которое будет обработано всеми заинтересованными в нем лицами.

Если действие тесно связано с выполняемой операцией и вам необходимо знать ее контекст, напрямую вызывает метод API.

Всё то же самое, что и в микросервисной архитектуре, грубо говоря.

Модель событий везде одинакова.

Одно событие может прослушиваться разными модулями и обрабатываться по-разному.

И даже в одном модуле может быть несколько разных обработчиков одного события, если это необходимо.

Сергей: Но если приложение постоянно усложняется функциями и контекстами, является ли модульный монолит всего лишь промежуточным шагом на пути к микросервисам? Юлия: Да, конечно.

Если подразумевается, что приложение будет постоянно расти по мере роста команды, это естественно.

Например, у нас была проблема, что автотесты выполняются нереально долго перед релизом, и если что-то отваливается, приходится запускать все заново.

Новые возможности, если они будут независимы, мы, вероятно, сразу реализуем в микросервисах.

Более того, у нас уже есть инфраструктура, есть шаблон, на основе которого мы можем создать новый микросервис.

Но, скажем, если вам нужно использовать какие-то данные или классы из Legacy, у которого нет своего API. У нас есть принцип, что Legacy может подтягивать API, но новые контексты из Legacy фактически не могут ничего получать и не общаются с ними, только через события.

Если мы переносим что-то в микросервис, и этому микросервису нужен доступ к устаревшему коду или какому-то функционалу, то сделать это будет достаточно сложно.

P.S. Больше выпусков подкаста «Между скобками» с интересными людьми из мира PHP можно найти здесь.

Здесь .

Теги: #Управление разработкой #Микросервисы #php #symfony #Perfect Code #утилиты #утилиты #утилиты #утилиты #модульный монолит #архитектура сервисов

Вместе с данным постом часто просматривают: