Одна статья для понимания стратегии рефакторинга микросервисной архитектуры.

Микросервисы

Скорее всего, вы имеете дело с большими, сложными монолитными приложениями, а ежедневная разработка и развертывание приложений требует много времени и усилий. Микросервисы выглядят отлично подходящими для вашего приложения, но в то же время это недостижимая нирвана. Как встать на путь микросервисной архитектуры? Вот несколько стратегий, которые помогут вам выбраться из монолитного ада без необходимости переписывать приложение с нуля.

Монолитная архитектура может быть постепенно преобразована в архитектуру микросервисов путем разработки так называемых приложений-удушителей. Идея приложения-душителя возникла из лиан-душителей, которые растут в тропических лесах, растут вокруг деревьев и иногда даже убивают их. Приложение-удушитель — это новое приложение, состоящее из микрослужб, реализованное путем использования новых функций в качестве службы и постепенного извлечения служб из монолитного приложения. Со временем, по мере того как приложение-душитель реализует все больше и больше функций, оно сжимается и в конечном итоге убивает монолит. Важным преимуществом разработки приложения Strangler является то, что, в отличие от полного переписывания в стиле Большого взрыва, оно может приземлиться немедленно и быстрее принести пользу бизнесу.

Есть три основных стратегии «задушить» монолит и постепенно заменить его микросервисами:

1) Реализовать новый функционал как услугу. 2) Разделите уровень представления и серверную часть. 3) Декомпозировать монолиты, извлекая функциональность в сервисы.

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

Рассмотрим эти стратегии ниже.

1. Реализовать новый функционал как услугу

«Закон ям» гласит: если вы попали в беду, перестаньте копать себе ямы. Это отличный совет, которому стоит следовать, когда ваш монолит становится неуправляемым. Другими словами, если у вас есть большое сложное монолитное приложение, не реализуйте новые функции, добавляя код в монолит. Это сделает ваш монолит больше и сложнее в управлении. Вместо этого вы должны реализовать новую функциональность как сервис.

Это отличный способ начать перенос монолитного приложения на архитектуру микросервисов. Это замедляет рост монолитов, ускоряет разработку новых функций (поскольку разработка выполняется в совершенно новой кодовой базе) и быстро демонстрирует ценность внедрения микросервисной архитектуры.

Интеграция нового сервиса с монолитом

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

■ Шлюз API: направляет запросы на новые функции в новую службу, а устаревшие запросы — в монолит.

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

图 1 新功能作为服务实现,服务是绞杀者应用程序的一部分。集成胶水将服务与单体架构集成,并由实现同步和异步 API 的适配器组成。API Gateway 将调用新功能的请求路由到服务

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

Когда внедрять новую функциональность как услугу

В идеале вы должны реализовать каждую новую функцию в приложении-душителе, а не в монолите. Вы будете реализовывать новую функциональность как новый сервис или как часть существующего сервиса. Таким образом, вы избегаете работы с монолитными кодовыми базами. К сожалению, не каждую новую функцию можно реализовать как услугу.

Потому что суть архитектуры микросервисов — это набор слабо связанных сервисов, организованных вокруг бизнес-функций. Например, функция может быть слишком маленькой, чтобы быть значимой услугой. Например, вам может просто понадобиться добавить некоторые поля и методы в существующий класс. Или новая функциональность может быть тесно связана с кодом в монолите. Если вы попытаетесь реализовать такую ​​функциональность как службу, вы, как правило, обнаружите снижение производительности из-за чрезмерного взаимодействия между процессами. Вы также можете столкнуться с проблемами согласованности данных. Если новую функциональность нельзя реализовать как сервис, решение обычно состоит в том, чтобы сначала реализовать новую функциональность в монолите. После этого вы можете извлечь эту функциональность вместе с другими связанными функциями в свой собственный сервис.

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

2. Изолируйте уровень представления и серверную часть

Одной из стратегий сокращения монолитных приложений является отделение уровня представления от уровней бизнес-логики и доступа к данным. Типичное корпоративное приложение состоит из следующих уровней:

■ Уровень логики представления. Он состоит из модулей, которые обрабатывают HTTP-запросы и генерируют HTML-страницы, реализующие веб-интерфейс пользователя. В приложениях со сложными пользовательскими интерфейсами уровень представления обычно содержит много кода.

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

■ Уровень логики доступа к данным: содержит модули, обеспечивающие доступ к службам инфраструктуры, таким как базы данных и брокеры сообщений. Обычно существует четкая граница между уровнем логики представления и уровнем логики бизнеса и доступа к данным. Бизнес-уровень имеет крупномодульный API, состоящий из одного или нескольких фасадов, которые инкапсулируют бизнес-логику. Этот API представляет собой естественный шов, по которому можно разделить монолит на два меньших приложения, как показано на рисунке 2.

图2 从后端拆分出前端可以使每个部分独立部署。它还公开了用于服务调用的 API

Одно приложение содержит уровень представления, а другое — уровень логики бизнеса и доступа к данным. После разделения приложение логики представления выполняет удаленные вызовы приложения бизнес-логики.

Такое разделение монолита имеет два основных преимущества. Он позволяет разрабатывать, развертывать и масштабировать оба приложения независимо друг от друга. В частности, это позволяет разработчикам презентаций быстро перебирать пользовательские интерфейсы и легко выполнять A/B-тестирование без развертывания серверной части. Еще одним преимуществом этого подхода является то, что он предоставляет набор удаленных API-интерфейсов для бизнес-логики, которые могут вызываться микросервисами, разработанными позже.

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

3. Извлеките бизнес-возможности из услуг

Внедрение новой функциональности в виде услуги и отделение интерфейсного веб-приложения от серверного ни к чему не приведет. В конечном итоге вам все равно придется много работать над монолитным кодом. Если вы хотите значительно улучшить архитектуру своего приложения и увеличить скорость разработки, вам необходимо разделить монолит, постепенно перенося бизнес-функциональность из монолита в сервис. При использовании этой стратегии количество бизнес-функций, реализуемых службой, со временем увеличивается, а монолит сокращается.

Функциональность, которую вы хотите внедрить в сервис, представляет собой «вертикальный срез» монолита сверху вниз. Срез содержит следующее:

■ Входящие адаптеры, реализующие конечные точки API. ■ Логика домена. ■ Исходящие адаптеры, такие как логика доступа к базе данных. ■ Монолитная схема базы данных.

Как показано на рис. 3, этот код был извлечен из монолита и перемещен в отдельную службу. Шлюз API направляет запросы, вызывающие извлеченную бизнес-функцию, в службу, а другие запросы — в монолит. Монолиты и сервисы сотрудничают, интегрируя связующий код. Клей интеграции состоит из адаптеров в службе и монолита, использующего один или несколько механизмов межпроцессного взаимодействия.

图3通过提取服务来打破单体。你可以识别一系列功能,包括业务逻辑和适配器,以提 取到服务中。你将该代码移动到服务中。新提取的服务和单体通过集成胶水提供的API 进行协作。

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

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

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

Эти проблемы возникают при использовании сервисов:

■ Разобрать модель предметной области. ■ Рефакторинг базы данных.

Демонтаж модели предметной области

Чтобы извлечь сервисы, вам нужно извлечь связанную с сервисом модель домена из монолитной модели домена. Вам нужно сделать большие шаги, чтобы разделить модель предметной области. Одна из проблем, с которой вы столкнетесь, — устранение ссылок на объекты, выходящих за границы службы. Классы, которые остаются в монолите, могут ссылаться на классы, перемещенные в сервисы, и наоборот. Например, представьте, что, как показано на рис. 4, вы извлекли сервис Order, чей класс Order ссылается на класс Monolithic Restaurant. Поскольку экземпляр службы обычно представляет собой процесс, не имеет смысла иметь ссылки на объекты, пересекающие границы службы. Вам необходимо исключить ссылки на объекты этого типа.

图4  Order 领域类引用了 Restaurant 类。如果我们将 Order 提取到一个单独的服务中,我们需要将它对 Restaurant 的引用做一些改造,因为进程之间的对象引用没有意义。

Хороший способ приблизиться к этому — думать с точки зрения агрегатов DDD. Агрегаты ссылаются друг на друга с помощью первичных ключей, а не ссылок на объекты. Таким образом, вы можете рассматривать классы Order и Restaurant как агрегаты, как показано на рис. 5, заменив ссылку на Restaurant в классе Order полем restaurantId, в котором хранится значение первичного ключа.

图 5 Order 类对 Restaurant 的引用将替换为 Restaurant 的主键,以消除跨越进程边界的对象引用。

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

Извлечение службы обычно требует гораздо больше работы, чем перемещение всего класса в службу. Более сложной задачей для разделения моделей предметной области является выделение функций, встроенных в классы с другими обязанностями. Эта проблема часто возникает в классах Бога со слишком большим количеством обязанностей. Например, класс Order — один из богов в приложении FTGO. Он реализует множество бизнес-функций, включая управление заказами, управление доставкой еды и многое другое. Сущность Delivery реализует функциональность управления доставкой, ранее связанную с другими функциями в классе Order.

Рефакторинг базы данных

Разделение модели предметной области включает в себя больше, чем просто изменение кода. Многие классы модели предметной области сохраняются в базе данных. Их поля сопоставляются с определенными схемами базы данных. Таким образом, когда вы извлекаете сервисы из монолита, вы также перемещаете данные. Вам нужно переместить таблицу из базы данных монолита в базу данных сервиса.

Кроме того, когда вы разделяете сущность, вам необходимо разделить соответствующую таблицу базы данных и переместить новую таблицу в службу. Например, при извлечении управления доставкой еды в службу вы должны разделить сущность «Заказ» и извлечь сущность «Доставка». На уровне базы данных вы разделяете таблицу ORDERS и определяете новую таблицу DELIVERY. Затем переместите таблицу DELIVERY в сервис.

Скопируйте данные, чтобы избежать более широких изменений

Как упоминалось выше, для извлечения сервисов необходимо внести изменения в модель предметной области монолита. Например, замените ссылки на объекты первичными ключами и разделите классы. Эти типы изменений могут повлиять на кодовую базу и потребовать внесения обширных изменений в каждую затрагиваемую часть монолита. Например, если вы разделите сущность «Заказ» и извлечете сущность «Доставка», вам придется изменить каждую часть кода, на которую повлияли ссылки на перемещенные поля. Внесение этих изменений может занять много времени и стать огромным препятствием для разрушения монолита.

Хороший способ отсрочить и, возможно, избежать внесения этих дорогостоящих изменений — использовать подход, аналогичный описанному в книге «Рефакторинг базы данных». Основным препятствием при рефакторинге базы данных является изменение всех клиентов этой базы данных для использования новой схемы. Решение, предложенное в этой книге, состоит в том, чтобы сохранить старый режим в течение переходного периода и использовать триггеры для синхронизации между старым режимом и новым режимом. Затем вы можете перевести клиентов из старого режима в новый режим.

Мы можем использовать аналогичный подход при извлечении сервисов из монолита. Например, при извлечении сущности «Доставка» мы оставляем сущность «Заказ» практически неизменной на переходный период. Как показано на рис. 6, мы делаем поля, связанные с доставкой, доступными только для чтения и поддерживаем их в актуальном состоянии, копируя данные из службы доставки обратно в монолит. Так что нам просто нужно найти, где обновить эти поля в коде монолита и изменить их для вызова новой службы доставки.

图 6 通过将与新提取的 Delivery Service 相关的数据复制回单体的数据库,最大限度地减少对 FTGO 单体的更改范围。

Сохранение структуры объекта Order путем копирования данных из Службы доставки значительно сокращает объем работы, которую нам необходимо выполнить сразу. Со временем мы можем перенести код, который использует связанные с доставкой поля объекта Order или столбцы таблицы ORDERS, в службу доставки. Что еще более важно, нам может никогда не понадобиться вносить изменения в монолит. Если код затем извлекается в службу, служба может получить доступ к DeliveryService.

Определите, какие услуги следует отключить и когда

Как я уже говорил, снос монолита занимает много времени. Он децентрализует человеческие ресурсы для реализации новых функций. Таким образом, вы должны тщательно определить порядок, в котором извлекаются службы. Вам нужно сосредоточиться на получении услуг, которые принесут вам максимальную отдачу от затраченных средств. Что еще более важно, вы хотите постоянно демонстрировать бизнесу ценность перехода на архитектуру микросервисов.

В любом путешествии очень важно знать, куда вы направляетесь. Хороший способ начать миграцию на микросервисы — использовать временные рамки для определения работы. Вы должны потратить короткий период времени, скажем, несколько недель, на мозговой штурм идеальной архитектуры и определение набора сервисов. Это даст вам цель. Однако важно помнить, что эта архитектура не высечена на камне. По мере того, как вы разрушаете монолит и набираетесь опыта, вы должны применять свой опыт для своевременного внесения корректировок в план рефакторинга.

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

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

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

■ Ускорение разработки. Если в дорожной карте вашего приложения указано, что определенная часть вашего приложения будет активно разрабатываться в течение следующего года, преобразование ее в службу может ускорить разработку.

■ Устранение проблем с производительностью, масштабируемостью или надежностью. Если какая-то часть приложения имеет проблемы с производительностью, масштабируемостью или ненадежна, имеет смысл превратить ее в службу.

■ Позволяет извлекать некоторые другие службы: Иногда извлечение одной службы упрощает извлечение другой службы из-за зависимостей между модулями. Вы можете использовать эти критерии, чтобы добавить задачи рефакторинга в «бэклог» вашего приложения и ранжировать их по ожидаемой выгоде. Преимущество этого подхода заключается в том, что он является более стратегическим и в большей степени соответствует потребностям бизнеса. При планировании спринта вы можете определить, что более ценно для реализации функции или извлечения услуги.

Эта статья взята из статьи «Шаблоны проектирования микросервисной архитектуры», опубликованной с разрешения издателя.

Дальнейшее чтение:

《微服务架构设计模式》

Автор: [Америка] Крис Ричардсон (Chris Richardson) Переводчик: Ю Юн 

Рекомендуемые слова:

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

Охватывая 44 режима проектирования архитектуры, система решает проблемы разделения услуг, управления транзакциями, запросов и межсервисной связи и т. д. Технический директор Ybao Payment Чен Бин, соучредитель PolarisTech Цай Шу, генеральный директор Caiyun Technology Чжан Синь и многие другие эксперты настоятельно рекомендуется.

Раздайте всем по три копии «Шаблонов проектирования микросервисной архитектуры».

Такая хорошая книга должна быть куплена, чтобы учиться.25-го числа у меня здесь есть несколько купонов, которые будут опубликованы в статьях.Вы можете использовать купоны для покупки, что очень выгодно.

В то же время мы также сначала дадим вам 3 копии, идентифицируем QR-код ниже и ответим в диалоговом окне, чтобы получить QR-код для лотереи для участия в лотерее. Время окончания — 8:00 27-го числа.