В этой статье я рассказываю, как мне удалось сделать весьма не плохую архитектурное решение с применением Microfrontend и Feature Sliced Design. Вкратце что из себя представляет обе эти архитектуры.
Microfrontend - пришедшее из мира микросервисов, где каждый сервис условно работает автономно и имеет не сильную связь с другими сервисами. Микрофронтенд применяется что-то из этого рода архитектур, где контейнер (App) условно разбит на несколько модулей, и каждый модуль может быть написан на разных фреймворков и быть подключен к единому контейнеру.
Feature Sliced Design (FSD) - достаточно новая архитектура я считаю, но ее преимущество в том эта разбиение файлов на отдельные слои где каждый слой не может быть зависим от других слоев, этих слоев не так много и их довольно легко запомнить (app, pages, widgets, features, entities, shared). Эта модель позволяет спроектировать интерфейс как композицию различных компонентов, не связанных напрямую друг с другом, и, тем самым, добиться низкой связанности компонентов интерфейса.
Почему же стоит задуматься над решением применить их обе в проекте, ведь ничего нам не мешает их использовать в слиянии, микрофронтенд не требует дополнительных настроек на сервере, ведь все развертывается и запускается в одном проекте, а FSD имеет достаточно удобную структуру файлов, применение этих двух архитектур будет must-have'ом для проекта с высокой нагрузкой, если конечно вы не пишите проект с простеньким функционалом.
Принципы микрофронтенда и FSD
Разделение по функциональности
Автономность
Слабая связь
Гибкость и легкая масштабируемость
Границы контекста
Высокая атомарность
Границы контекста
Для примера я разделил проект на 2 модуля кроме основного контейнера, в будущем можно будет расширить проект до нужных вам модулей. Для инициализации модуля используется пакет create-mf-app.
/container
- наше приложение, куда пользователь будет попадать, из себя он будет представлять довольно типичную структуру для React App, но в дальнейшем мы расширим ее файловую структуру.
/service-api
- модуль API
, тут будут раскиданы работы с интерфейсами, в себе он будет иметь один пакет работа с запросами.
/service-shared-components
- модуль для shared компонентов или uikit
Первичная инициализация:
npx create-mf-app
Вам предложат выборочную установку:
Application - приложение, тем самым вы создаете модуль с исходным фреймворком который вы укажите из пакета, в моем случае эта React.
После всех этих манипуляций, связь между модулем обеспечивает пакет и webpack'а ModuleFeredationPlugin
. В конструктор он принимает объект, разберем что он может из себя представлять.
name
- имя модуля.
filename
- удаленный entry файл куда будет размещен наш код при запуске модуля.
remotes
- подключение удаленных модулей, обычно он указывается домашним приложением, заметьте что для обращение я использую @
, чтобы typescript не ругался. для подключение указываются префикс имени модуля и путь по которому он будет запущен.
exposes
- инициализация или путь модуля для развертывания домашним приложением.
shared
- инициализация зависимости пакетов, дабы избежать перелинкования версий зависимостей.
"@uikit": "uikit@http://localhost:8081/remoteEntry.js",
"@api": "api@http://localhost:8082/remoteEntry.js",
После перезапуска модуля вы увидите следующую картину:
Этот файл удаленной записи, remoteEntry.js
, представляет собой файл манифеста всех модулей, которые предоставляются приложением.
После того как подключили все модули к единому контейнеру, можно будет рассмотреть вариант расширить файловую структуру.
features - эта фичи, которые явно работают по определенной бизнес логики, там будут происходить разные манипуляции с запросами, работа со стором.
widgets - самостоятельный интерфейс, который присущен бизнесу в себе он может включать разные фичи, сущности и shared компоненты.
Заметьте что у меня отсутствует слой entity, так как его я буду брать из сервиса service-api
.
@service-api/posts.api
в service-api
имеется интерфейс для получение постов, в качестве теста я взял за основу jsonplaceholder.
@container/features/posts/getPosts
В домашнем приложение я инициализирую usecase, который будет возвращать мне список PostEntity, в данном случае я просто использую интерфейс postsApi, так же вы можете работать со стором делать разные манипуляции с селекторами.
@container/App.tsx
в файле App.tsx инициализируется наша фича и виджет PostCard, делается первичный запрос на получение постов с последующим сохранением и отображением.
Заключение
В этой статье мы рассмотрели концепцию микрофронтендов на примерах, обсудив их преимущества перед монолитными фронтенд‑приложениями и другими доступными настройками. Микрофронтенды предлагают отличные функции и просты в использовании.
С помощью create‑mf‑app мы реализовали архитектуру микроинтерфейса так же легко, как и с помощью приложения Create React. Лично мне нравится стиль микроинтерфейса, потому что его легко поддерживать в командах. Кроме того, создание внешнего интерфейса и безопасность управляются довольно элегантно.
Надеюсь, вам понравилась эта статья, и не забудьте оставить комментарий, если у вас есть какие‑либо вопросы. Удачного кодирования!
mikaakim
Спасибо за статью.
Плюсы/минусы подхода есть?
Как ведет себя структура кода и его размер в более сложных приложениях, чем пример из статьи?
Где микрофронтенд будет плох и будет ииеть оверхэд?
Pecheneg2015
Могу поделиться своими наблюдениями про mfe и fsd.
mfe хороши, но не панацея. При использовании mfe сразу встаёт несколько вопросов:
1) Как поддерживать консистентность зависимостей? Для себя принял, что mfe хорошо дружат с nx. При использовании nx сразу упрощается управление зависимостями, деплой, запуск в dev режиме(запуск был актуальным вопросом т.к. количество mfe в проекте было более 20).
2) Шаринг зависимостей. Тут всё очень неоднозначно т.к. при шаринге какой-либо библиотеки мы сразу теряем treeshaking т.к. сборщик не может угадать какие части библиотеки понадобятся. Опять же, в данном случае мне помог nx с его упором на создание библиотек из коробки.
3) При работе с большим количеством mfe автоматически встаёт вопрос динамической подгрузки mfe. Если не решить данный вопрос, то вы автоматически будете дёргай тот самый злосчастный remoteEntry.js для каждого mfe.
Также хотел отметить, что автор плохо понимает что получает на выходе. Особенно это хорошо видно в следующем фрагменте:
"@uikit": "uikit@http://localhost:8081/remoteEntry.js",
"@api": "api@http://localhost:8082/remoteEntry.js",
Китовая библиотека компонентов и библиотека для общения с апи не являются самостоятельными единицами. Данные модули должны быть расшарены как общие зависимости через хост приложение.
Про fsd. Подход имеет место быть, но стоит держать в уме, что просто взять и перенести средний/большой проект на fsd не выйдет без специалиста, который хорошо знаком с данным подходом. Сталкивался с ситуацией когда на проекте fsd и всё круто, но в какой-то момент людям приходит понимание, что большая часть разработчиков распихивает код по слоям рандомно и проект превращается в кашу. Поэтому первое время потребуется строгое ревью.