Как фронтенд-разработчик, вы все эти годы разрабатывали монолиты, хотя уже знали, что это плохая практика. Вы разделили свой код на компоненты, использовали require или import и определили пакеты npm в вашем package.json или подключили дополнительные репозитории git в свой проект, но в итоге вы создали монолит . Пора это изменить.

Почему ваш код - монолит?

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

Что такое микросервисы на самом деле?

В целом и наиболее упрощенное объяснение микросервисов заключается в том, что это метод разработки, который позволяет разработчикам выполнять независимые развертывания для разных частей платформы, не причиняя вреда другим частям. Возможность независимого развертывания позволяет им создавать изолированные или слабосвязанные службы. Чтобы поставить эту архитектуру на более стабильную основу, необходимо следовать нескольким наборам правил, которые можно резюмировать следующим образом: Каждая служба должна иметь только одну задачу, и она должна быть небольшой. Так что команда, отвечающая за эту услугу, должна быть небольшой. Что касается размера команды и проекта, одно из самых крутых объяснений в Интернете было сделано Джеймсом Льюисом и Мартином Фаулером, как показано ниже:

В наших беседах со специалистами по микросервисам мы видим широкий спектр услуг. Сообщается, что самые большие размеры соответствуют представлению Amazon о команде двух пицц (т. Е. Всю команду можно накормить двумя пиццами), то есть не более дюжины человек. В меньшем масштабе мы видели установки, в которых группа из полдюжины человек поддерживала полдюжины сервисов.

Я создал простой набросок, чтобы дать наглядное объяснение монолиту и микросервисам:

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

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

Решение проблемы узких мест в микросервисах: Micro Frontends 🎉

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

Идея Micro Frontends состоит в том, чтобы рассматривать веб-сайт или веб-приложение как набор функций, которые принадлежат независимым командам. У каждой команды есть отдельная сфера деятельности или миссия, которую она волнует и на которой специализируется. Команда кросс-функциональна и развивает свои функции сквозной, от базы данных до пользовательского интерфейса. (Micro-fontend.org)

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

Общая структура и некоторая терминология

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

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

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

Проблемы, которые нужно решить

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

Как создать единый и последовательный интерфейс, когда у нас есть полностью независимые автономные микроприложения?

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

Другой идеей может быть совместное использование пользовательских переменных CSS на уровне: root. Преимуществом этого решения будет глобальная настраиваемая тема между приложениями.

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

Как убедиться, что одна команда не отменяет CSS, написанный другой командой?

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

Другое решение - сделать каждое микроприложение настраиваемым веб-компонентом. Преимущество этого решения заключается в том, что область видимости выполняется браузером, но за это приходится платить: практически невозможно выполнить рендеринг на стороне сервера с помощью теневой модели DOM. Кроме того, пока нет 100% поддержки браузера для пользовательских элементов, особенно если вам нужно поддерживать IE.

Как мы должны делиться глобальной информацией между микроприложениями?

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

В качестве альтернативы можно использовать любую общую реализацию pub-sub или наблюдаемую реализацию T39. Если нам нужен более сложный обработчик глобального состояния, мы можем реализовать совместно используемый миниатюрный Redux, таким образом мы можем достичь более реактивной архитектуры.

Если все микроприложения являются автономными приложениями, как нам выполнять маршрутизацию на стороне клиента?

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

Мой текущий прагматический подход заключается в создании общего клиентского маршрутизатора, который отвечает только за маршруты верхнего уровня, а все остальное принадлежит соответствующему микроприложению. Допустим, у нас есть определение маршрута / content /: id. Общий маршрутизатор будет разрешать / контентную часть, и разрешенный маршрут будет передан в ContentMicroApp. ContentMicroApp - это автономный сервер, и он будет вызываться только с /: id.

У нас обязательно должен быть рендеринг на стороне сервера, но возможно ли это с помощью микро-интерфейсов?

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

Интеграция с устаревшей средой жизненно необходима! Но как?

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

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

Следующим шагом будет наше первое движение постепенного вторжения: мы немного откусим от LegacyMicroApp, удалив основную навигацию и заменив ее зависимостью. Эта зависимость будет микроприложением, реализованным с использованием новой блестящей технологии: NavigationMicroApp.

Теперь слой сшивки будет разрешать каждый маршрут как LegacyMicroApp, и он будет разрешать зависимость как NavigationMicroApp и обслуживать их, объединяя эти два.

Следующим шагом будет нижний колонтитул, следуя тому же шаблону с основной навигацией.

А затем мы продолжим брать такие же небольшие кусочки из LegacyMicroApp, пока от него ничего не останется.

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

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

В соответствии с приведенными выше вопросами и возможными решениями я могу обобщить все по следующим темам:

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

  • Оркестровка
  • Маршрутизация
  • Изоляция микроприложений
  • Связь приложения с приложением
  • Согласованность между пользовательскими интерфейсами микроприложений

На стороне сервера

  • Серверный рендеринг
  • Маршрутизация
  • Управление зависимостями

Гибкая и мощная, но простая архитектура

Так что ждать всю эту статью было очень тяжело! Наконец-то начали проявляться базовые элементы и требования архитектуры микро-фронтендов!

Руководствуясь этими требованиями и проблемами, я начал разрабатывать решение, которое я назвал microfe. 😎 Здесь я опишу архитектурную цель этого проекта, подчеркнув его основные компоненты в абстрактной манере.

Начать работу на стороне клиента легко, и у нее есть три отдельные структуры магистрали: AppsManager, Loader, Router и одна дополнительная MicroAppStore.

AppsManager

AppsManager - это ядро ​​оркестровки клиентских микроприложений. Основная функция AppsManager - создание дерева зависимостей. Когда все зависимости микроприложения разрешены, оно создает экземпляр микроприложения.

Загрузчик

Другой важной частью оркестровки клиентских микроприложений является загрузчик. Ответственность за загрузчик - получение неразрешенных микроприложений со стороны сервера.

Маршрутизатор

Чтобы решить проблему маршрутизации на стороне клиента, я ввел маршрутизатор в microfe. В отличие от обычных клиентских маршрутизаторов, маршрутизатор microfe имеет ограниченные функции. Он разрешает не страницы, а микроприложения. Допустим, у нас есть URL / content / detail / 13 и ContentMicroApp. В этом случае маршрутизатор microfe разрешит URL до / content / * и вызовет часть URL ContentMicroApp / detail / 13.

MicroAppStore

Чтобы решить проблему взаимодействия микроприложений и микроприложений на стороне клиента, я ввел MicroAppStore в microfe. Он имеет аналогичные функции библиотеки Redux с отличием: он устойчив к асинхронным изменениям структуры данных и объявлениям редукторов.

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

MicroAppServer

Минимальные функциональные возможности MicroAppServer можно резюмировать как init и serve.

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

StitchingServer

StitchingServer предоставляет конечную точку register для MicroAppServers. Когда MicroAppServer регистрируется на StichingServer, StichingServer записывает объявление MicroAppServer.

Позже StitchingServer использует объявление для разрешения MicroAppServers из запрошенного URL-адреса.

После разрешения MicroAppServer и всех его зависимостей все относительные пути в CSS, JS и HTML будут иметь префикс связанных общедоступных URL-адресов MicroAppServer. Одним из дополнительных шагов является добавление к селекторам CSS уникального идентификатора MicroAppServer для предотвращения конфликтов между микроприложениями на стороне клиента.

Затем на сцену выходит основная ответственность StitchingServer: создание и возврат цельной HTML-страницы из всех собранных частей.

Взгляд на другие существующие реализации

Еще до того, как к 2016 году он был назван микро-интерфейсом, многие крупные компании пытались решить аналогичные проблемы, такие как Facebook с его BigPipe. В настоящее время идея набирает обороты. Компании разных размеров заинтересованы в предмете и вкладывают в это время и деньги. Например, Zalando открыла исходный код своего решения, которое называется Project Mosaic. Я могу сказать, что microfe и Project Mosaic придерживаются схожих подходов с некоторыми существенными различиями. В то время как microfe поддерживает полные децентрализованные определения маршрутов, чтобы обеспечить большую независимость для каждого микроприложения, Project Mosaic предпочитает централизованное определение маршрута и определения макета для каждого маршрута. Таким образом, Project Mosaic позволяет легко A / B-тестирование и динамическое создание макета на лету.

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

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

Последние мысли

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

Рассмотрите возможность участия

Я интенсивно экспериментирую с микро-интерфейсами с благородной целью: создать среду для микро-интерфейса, которая может решить большинство проблем без ущерба для производительности, простоты разработки и тестирования. Если у вас есть какие-то яркие идеи, не стесняйтесь посещать мои репозитории, открывать вопросы или обращаться ко мне через комментарии ниже или Twitter DM. Я буду рядом, чтобы помочь тебе! 🙂