microFrontend

Архитектура микро-фронтенда: замена монолита изнутри

Как модернизировать устаревшее приложение с помощью технологии микро-фронтенда.

Эта статья является частью серии, посвященной приложениям микро-фронтенда и методам управления ими.

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

Проблема

Давайте предположим, что существует монолитная база кода. В этом монолите используется один из механизмов бэкэнд шаблонов (например, EJS или ERB), jQuery, и он не имеет реальных соображений относительно внешнего интерфейса - или, что еще хуже, он возник в то время, когда не существовали SPA. Возможно, у него есть какой-то pipeline, как у Ruby on Rails. В этом случае у вас могут быть внутренние переменные внутри файлов javascript - например, файлов .js.erb или фрагментов AEM.

Вы хотите прекратить писать код UI внутри этого монолита и перейти к более ориентированной JavaScript экосистеме, но как?

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

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

Однако разделение монолита может быть сложным, в первую очередь, когда новые API для поддержки приложения JavaScript должны пройти планирование или разработку для поддержки UI.

Итеративная разработка веб-интерфейса, реализация микро-фронтенда (MFE) и командная автономия блокируются в ожидании разработки или завершения необходимых API-интерфейсов и переходом в цикл выпуска. ЛОЖЬ, вы можете отделить фронтенд параллельно от бэкенда.

1

Вот одно из решений, позволяющее отделить интерфейс и перенести его на автономный MFE с SSR. Этот метод позволяет команде сделать это, не дожидаясь, пока бэкэнд-API будут абстрагированы и разделены на микросервисы или даже на раздельные API-интерфейсы в пределах монолита. Замените монолит изнутри.

Блокировщики

Микро-фронтенд обычно имеет эти две зависимости.

1) Аутентификация

2) Данные для подачи приложения, как в браузере, так и во время рендеринга на стороне сервера (SSR)

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

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

Используйте Монолит как движок шаблонов

Существует несколько различных архитектурных спецификаций для дизайна платформы MFE. Эта статья будет посвящена адаптированной спецификации, которая популярна среди базовых микросервисов, - LOSA (Lots Of Small Applications - множество небольших приложений). Эта архитектура является хорошим вариантом для миграции “наизнанку”.

2

LOSA-приложения (микро-фронтенд в целом) - это автономные сервисы Node.js, способные отображать на стороне сервера часть или фрагмент веб-страницы. Страница может состоять из нескольких сервисов LOSA. Эти приложения / микро-фронтенд создаются и развертываются в контейнере, который является независимым и работает автономно.

3

Одна и та же веб-страница, составленная тремя различными способами, демонстрирует путь постепенной миграции. Начиная со страницы, представленной в виде монолита, переходя к микро-фронтенду LOSA и, наконец, заканчивая вертикалью микро-фронтенда, полностью заменяя монолит. Изображение принадлежит Роберту Аркрайту.

Монолит остается ответственным за обработку объекта HTTP-запроса и отправку окончательного ответа клиенту. Микро-интерфейсы могут оставаться за межсетевым экраном внутри кластера - они доступны только для устаревшей системы до тех пор, пока API и аутентификация пользователя не могут быть отделены (или, по крайней мере, превращены в конечную точку API). Вам не потребуется много изменений, чтобы подготовить эти интерфейсы к их “существованию” после монолита.

Поток рендеринга

Ниже приведен смоделированный пример того, что запрос / ответ может в конечном итоге напоминать.

Сначала делается запрос:


GET/POST 'https://MFEwebsite.com/parts/header?format=json

4

Рендеринг страницы может потребовать разнообразных данных, любая «пропущенная» информация, которую еще нельзя запросить от разъединенной конечной точки, может быть отправлена в MFE (микро-фронтенд) в качестве реквизита во время запроса. Вот что делает MFE, когда выполняется запрос, запрос передается через промежуточное программное обеспечение, которое отвечает за рендеринг приложения React, выполняется запрос к любым необходимым API-интерфейсам, которые разъединены, и его ответ отправляется обратно в качестве реквизитов. , Эти реквизиты будут составлять window.INITIAL_STATE.

Код

Если вам нужно вдохновение как реализовать некоторые из этих шаблонных функций или фильтров, тогда стоит посмотреть на Hypernova. Я не использую Hypernova, f всегда выбираю свою собственную реализацию. Я реализовываю подобные механизмы в Rails, Node и PHP. Из-за запатентованной природы различных платформ, я буду использовать примеры Hypernova, чтобы передать элементарную концепцию. Вот как url запроса рендеринга MFE будет выглядеть:

Запрос из другой системы, в данном случае монолит:

GET/POST 'https://MFEwebsite.com/parts/header?format=json

{

html: '
<div> ... </div>',
css: '
/static/header.3042u3298423.css',
js: '
/static/header.idhf93hf23iu.js',

initial_state: {items:[...]}

}


Промежуточное программное обеспечение, которое обрабатывает ответ:

export function exampleRenderAPIware(req, res) {
const renderedMarkup = renderHTMLpage(
req,
this.index,
intial_state,
);
asyncRender.then(() => {
const responseObject = {
html: renderedMarkup,
initial_state,
js: jsResource,
css: cssResource,
};
res.status(200).end(JSON.stringify(responseObject));
});
}
 

Контроллеры, выполняющие эти первоначальные запросы POST, должны обрабатывать ответы, размещая JS и CSS в нужных местах и, наконец, отображать приложение React в соответствующем месте в устаревшем шаблоне. Вот пример того, как этот устаревший шаблон выглядит сейчас. Ресурсы, обычно обрабатываемые другим контроллером в вашем монолите, будут отвечать за внедрение этих скриптов и стилей в то, что осталось в устаревшем заголовке и нижней части тега body. Помните, что монолит по-прежнему служит движком макета. Мы заменяем детали и добавляем новые функции в режиме React SSR. В конце концов, эти приложения LOSA могут быть объединены под одним MFE или с помощью черной магии Webpack, которую я разрабатываю как webpack-external-import.

Как насчет перехода от данных шаблона к новому API?

Что можно ожидать от миграции при переходе от нового API к сети?

Когда монолит передает данные в MFE, Express.js получает доступ к этой информации из тела HTTP-запроса. Теперь express нужно будет асинхронно извлекать из API. Форматы данных могли измениться, но React по-прежнему получает props`ы.

Производительность

По сравнению со старыми монолитами, новая архитектура LOSA (множество небольших приложений) была недостаточно производительной, занимая 400–600ms для рендеринга части страницы. Мы использовали структуры Async Worker, а это означает, что вместо одного приложения SSR мы могли бы запросить несколько служб для SSR разных частей приложения. Из-за этого было очень сложно перевести производство в автономный режим, поскольку «сбой производства» означал потерю бокового сайдбара или футера в течение 10 минут, пока она не была исправлена. Разделение концернов во всей красе.

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

5

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

Поскольку view data были опубликованы во внешнем сервисе реакта, ответ на этот POST, который содержит разметку, затем передается внутреннему шаблонизатору вместе с таблицей стилей, начальным состоянием и URL-адресами CSS. Механизм шаблонизирования теперь просто отображает ответ на запрос POST, таким образом отделяя ваше представление или его часть от устаревшего монолита.

React Render Time

React был медленным! SSR не работает быстро, поэтому наше новое react-решение LOSA-архитектуры не было достаточно производительным, чтобы быть жизнеспособным. Наше решение: Фрагмент кэширования внутри React.

6

• Желтый: нет кэширование фрагментов React - сквозное (+ - 400 мс)

• Темно-фиолетовый: с кэшированием фрагментов React - сквозное (+ -150мс)

• Оранжевый: полностью оптимизированная архитектура (+ -20 мс)

• Зеленый (внизу точек данных): собственный кеш фрагмента из backend

Мне нужно будет написать еще одну статью, чтобы подробно описать весь процесс создания полностью оптимизированного сервера c React. Данные Graphana показывают, что мы по крайней мере удвоили нашу производительность рендеринга, время циклического перебора все еще было очень медленным. В то время как внутри, React был в состоянии выполнять очень быстро - сквозное время было не такими эффективными, как мы надеялись. По крайней мере, 150 мс. Как вы можете видеть, и как я расскажу в следующей статье, я могу конкурировать с фрагментным внутренним кэшированием.

Время рендеринга против круговой системы времени

Время рендеринга является частью проблемы, даже после реализации фрагментного кэширования внутри React. Я был разочарован, увидев, что время внутреннего рендеринга в Node.js быстро росло (около 20 мс). Всё “путешествие” от начала до конца все еще занимало 140–200 мс.

В чем же проблема?

1. Размер JSON, в частности начальное состояние приложения. Отправьте минимальное состояние, необходимое для отображения страницы. Не отправляйте слишком много данных в начальный рендерер. Отправьте достаточно состояния, чтобы React мог повторно ввести его и, возможно, какое-то дополнительное состояние для немедленного создания интерактивных компонентов.

2. Количество отображаемых DOM-узлов - прекратите упаковывать ваш код в бесполезные div-ы, просто чтобы поместить в него класс. Воспользуйтесь преимуществами семантической природы HTML и каскадных эффектов CSS. В итоге вы пишете намного меньше разметки, тем самым создавая намного меньше функций React.createComponent.

3. Сборка мусора - подробности будут опубликованы в следующей серии статей.

4. Только так же быстро, как и провайдеры данных. - Используйте кэши Redis на среднем уровне. Если вы бросите мне аргумент «аннулирование кэша сложно», посмотрите на источник событий. Еще лучше решить проблему с CQRS и асинхронными рабочими при записи и чтении.

5. Затраты HTTP между монолитом и MFE - gRPC, CQRS, UDP, Protobuf. Эта связь между монолитом и вашим MFE должна происходить внутри сети Kubernetes. POST медленный, но работает. Когда вы столкнетесь с проблемой, обработайте ее соответствующим образом.

Как я превзошел бэкенд рендеринга

Шаблонизация, кэширование фрагментов и gRPC / CQRS, удаление bloat из исходного состояния JSON. Реагирует медленно (медленнее) на сервере. Это легко забыть, нет абстракции быстрее, только медленнее.

Как насчет масштаба?

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

1) Оплата дорогих сторонних услуг, чтобы разделить нагрузку

2) Оплата за контейнеры побольше

3) Упущенный доход из-за плохой работы

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

5) Разработчики могут двигаться быстрее в средах с низким уровнем риска, бизнес может предлагать новые идеи для рынка и откатывать проблемные области - команда, которая может двигаться быстро и эффективно, является экономически эффективным фактором, способствующим развитию бизнеса.

Результат

Трафик: 10 миллионов (рендеров) / день

Распределение ресурсов:

• Экземпляры: 5

• RAM: 100mi (100 мегабайт оперативной памяти)

• Процессор: 100 (одноядерный)

• Максимальный порог использования процессора: 65%

• Время ответа: 20–25 мс

• Сложность DOM: высокая

• Времени ответа сократилось на 95%.

7

Зеленый: время рендеринга в бекенд

Синий: React с фрагментным кэшированием и оптимизацией состояния.

Мое однопоточное приложение Javascript было быстрее, чем многопоточная бэкэнд-система с полноценным фрагментным кешем.