Привет! С вами снова Максим, и это заключительная часть трилогии о переезде на MFE. В первой части мы говорили о том, как пришли к распилу, во второй — что подтолкнуло нас к микрофронтам, и вот настала очередь фолбэков. В прошлой части мы добавляли конфигурацию нашего приложения и перенесли ее из локального хранения на отдельный сервис. Но что делать, если сервис, раздающий эту конфигурацию, будет недоступен? Первый фолбэк — это конфигурация. В случае недоступности раздающего сервиса нам нужно из какого-то кэша отдать конфиг, даже если кэш будет протухшим. Иначе конечный пользователь столкнется с проблемой. Такую ситуацию проще проиллюстрировать: Проблему, описанную выше, решает CDN. Если в нашем nginx нет кэша, он точно есть у CDN, но мы дополнительно сохраняем у себя в локальном кэше nginx. С конфигом все понятно: код мы здесь даже не писали и не притронулись к нему. Внутри приложения нам нужны фолбэки на загрузку микрофронта или хотя бы фолбэки переходов между страницами внутри приложений. Module Federation имеет мало отношения и к микрофронтам, просто фолбэки нужны всегда, если вы боретесь за доступность ваших приложений. Здесь мы добавили их именно в таком контексте.  Еще переходы между приложениями могут затрагивать плановые работы на ремоутах: релизим какой-то функционал и он может привести к down time. Поэтому нужно повесить плашечку, то есть отследить, живо ли приложение, пускать ли на него трафик.  Любому роутеру нужно добавить обработчик на редирект. У нас в Ангуляре есть Router Events, мы ловим среди событий ошибочное и либо показываем нотификацию, либо навигируем отдельную ошибку, если понимаем что микрофронт полностью недоступен. Но главное — мы говорим с пользователем: не показываем ему белый экран, а сообщаем, что есть проблема, но можно пользоваться остальными приложениями, все будет хорошо, а это мы починим. Health check — это проба, которая говорит, живо наше приложение в данный момент или нет На фронте health check используется редко, а в мире бэкенда — достаточно часто.  Поэтому добавляем на nginx проверку maintenance mode. Если мы релизим приложение и знаем, что будет down time, то переключаем галочку maintenance mode, чтобы перед этим down time увести трафик из приложения.  Многие проекты запущены не в одном экземпляре, а минимум в двух-трех для поддержания стабильности. Вывели один экземпляр через maintenance mode, трафик через него перестал приходить и релизим без аффекта на клиентов. Если maintenance mode не включен, просто проверяем живость сервиса через health check. Сервис мертв — 503, мы не можем принимать трафик. Живой — 200, ок, все хорошо. Описание нужно где-то хранить, для этого есть великолепный конфиг, который мы можем расширить:  В конфиг добавляем новый объект с указанием, куда ходить по конкретному микрофронту, какая максимальная задержка должна быть. Думаю, не стоит ждать ответа health check дольше 60 секунд. Если сервис вам не ответил за 5 секунд — кажется, уже есть проблемы, можно его глушить. В нашем примере мы проверяем живость приложения раз в 10 секунд, с ожиданием ответа в течение 5 секунд. Со стороны приложения наш хост может вычитать этот конфиг, запросить health check у приложения и обработать ответ.  В тот момент, когда мы создаем алерт и понимаем, что приложение недоступно, можем обработать эту ситуацию. Например, убрать недоступное приложение из навигации или повесить плашку, что сейчас ведутся технические работы, — чтобы пользователи видели предупреждение о возможных проблемах.Кейс простой и удобный. Но нужно смотреть, хотите ли вы показывать пользователю, что у вас работы или же сбой. Или вы принимаете этот риск, что они пойдут туда, попадут, что-нибудь сломают или сломаются. Однажды к нам пришла очень добрая тетя-продакт, позвала всю команду фронтенда и сказала: «Ребята, у меня есть задача, от которой вы будете просто без ума». У нас есть два проекта: личный кабинет и админка. Думаю, у многих имеются такие приложения. И там, и там у нас микрофронтенды. Появилась задача, по которой админ должен при определенной роли в админке видеть пользовательские операции и что-то с ними делать: возвращать, подтверждать и многое-многое другое. Но часть функциональности они не должны видеть, то есть нужны определенные роли. Сели думать, как будет выглядеть наше приложение. Как можно решить эту задачу? Ctrl+C, Ctrl+V — перенесли, допилили. Но нам нужно фактически зеркалирование. Если мы внесли изменения в личный кабинет, оно должно автоматически отразиться в админке. Если же мы Ctrl+C, Ctrl+V — появляется второй проект, в котором нужно не забыть это допилить.  После этой истории у нас появляется ситуация: есть хост, внутри которого ремоут и внутри ремоута еще один ремоут. В загрузчик конфига добавляем историю с сохранением конфига в session storage. У нас используется специальный ключ configuration storage key. И это отсылочка к той первой истории, когда мы делили на локальные npm-библиотеки все, что должно быть вынесено общее:  Появилась NPM-библиотека для токенов, конфигов и для состояния наших приложений. О ней я чуть позже расскажу. Мы решили использовать именно микрофронтендовый подход не только в одном приложении, а зациклить все хосты, которые у нас есть, на определенный базис требований, которым они должны соответствовать.  Пишем еще один загрузчик: Мы фактически делаем обертку на функцию, которую нам поставляет Module Federation внутри проекта. По нашему storage key внутри другого приложения в другой библиотеке находим конфиг, подгружаем его:  Здесь мы не можем использовать ангуляровский DI, потому что используется это все в момент, когда до DI еще дело не дошло. Поэтому берем из конфига, парсим и находим нужное приложение:  У нас везде есть список приложений — это enum, который мы передаем. Если нашли — отлично, работаем дальше. Мы вызываем Module Federation функцию и подгружаем наше приложение либо выдаем ошибку:  В целом функция «загрузчик» отлично работает, и мы идем по стандартному для фреймворка пути — без каких-либо костылей и велосипедов, то есть у нас есть массив объектов, в котором описаны наши пути. В одном из этих дочерних приложений указываем, что появляется новый путь. Как только пользователь перейдет по нему, произойдет вызов функции, которая все сделает за тебя:  Функция сходит на отдельный хост, заберет оттуда название ремоута, его файлики и прогрузит наш микрофронт там, где нужно.  У нас все получилось, все работает.   Почему мы взяли страницу личного кабинета, вставили ее в админку, и оно не сломалось?  Все потому, что у нас есть шина данных. Все приложения, которые мы разрабатываем, должны иметь у себя свой фича state, в котором они хранят только свою бизнес-логику и данные. И они знают о том, что они куда-то встраиваются, и у этого есть рутовое состояние. Рутовое состояние между всеми приложениями сделано двумя способами: 1. Паблишим npm-библиотеку: подключаем, указываем, какой рутовый state взять из этой библиотеки. 2. Единые DTO для описания типов состояния.  Наше хост-приложение при старте будет исполнять роль продюсера — возьмет из библиотеки поток и обозначит, что в этот поток загружаются все события: по загрузке пользователя, по загрузке конфига при изменении языка. Ремоут-приложение знает, что есть очередь, на которую стоит подписаться, и каких событий он ждет. Поэтому, когда события исполнятся, он их обработает. Выглядит это примерно так: Хост-приложение де-факто соответствует нашим стандартам, и мы всегда знаем, что личный кабинет и админка испускают одинаковые events, у них одно и то же состояние данных. Да, они могут локально их расширить уже конкретно под себя, но базис у них есть.  Любое приложение знает, что пользователь имеет определенный тип, и устанавливается в определенный атрибут. Приложения знают, из какого атрибута состоит пользователь, где он лежит, и могут брать нужные им для работы параметры. И если вдруг случится логаут, релогин — через эту шину они поймут, что нужно либо у себя почистить, если они вдруг сохранили себе этого пользователя, либо что-то поменять, перерисоваться. И еще одна важная вещь: единый state, то есть рутовое состояние нашего приложения, которое мы фактически пошарили между всеми. Возможно, пошарили не совсем красиво и можно использовать dedicated-воркеры, сервис-воркеры и многое другое, чтобы этот конфиг откуда-то подтягивать. Но npm быстро, дешево и сердито решает нашу задачу. Все приложения знают единый набор селекторов, событий и вот этого всего. Если мое приложение просит информацию по пользователю через селектор, в любом хост-приложении будет информация по пользователю, чтобы этот селектор отработал. У нас нет проблем с подключением тех или иных страниц в разные приложения. Поэтому любую функциональность личного кабинета можно без каких-либо проблем добавить в наше приложение через добавление в конфиг и подключение где-то в роутере.  Все это у нас — библиотека и выглядит примерно так: Все работает дешево и сердито, то есть нашу задачу оно решает. Поэтому можно интерфейс админа подключить в личный кабинет. Но он не заведется, потому что там есть ролевые модели и вот это все. И личный кабинет можно просто перенести внутрь админки, которая у нас есть. Микрофронты — Module Federation или какое-то свое решение — это универсальное средство для решения проблем. Какую бы задачу мы ни решали, можно посмотреть, как лучше сделать: монолитно или с применением микрофронтовых решений.  Но микрофронты — это все-таки не про фреймворк, а про мышление: как декомпозировать приложение так, чтобы была модульность, легкость, все работало быстро и было расшарено между командами. Можно использовать модульность в монолите: снаружи монолитный, а внутри — красивый модульный проект. И можно спокойно этот проект перевести на микрофронт. Но в работе с любым приложением, будь то монолит или микрофронт, микросервисные бэки или еще что-то, главное — фантазия. Экспериментируйте. Если кажется странной идея пошарить state, запихнуть это все еще куда-то — попробуйте. Потому что только попробовав вы поймете, тяжело вам это дается или легко, какие плюсы и минусы вы от этого получите. Поэтому не бойтесь экспериментировать.   Фронтенд архитектор