Pull to refresh

Comments 68

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

Почему? То есть маленькая команда не может сделать сложный продукт, которому было бы неплохо получить возможность выкатывать части независимо?

Потому. Сильное усложнение того что можно за 15 минут обсудить в курилке и сделать самым простым способом.

А если вам нужно чтобы отдельные компоненты масштабировались независимо, но вы умещаетесь за одним столом, вам все равно нужно писать монолит?

Думаю, что вся проблема в понимании термина «монолит». Монолит — это не когда у тебя только один сервер с только одним аппликейшеном на нем. Монолит также может быть построен на сервисной архитектуре. Микросервисная архитектура — является частным примером сервисной. Так вот речь о том, что именно микросервисная архитектура не подходит для маленьких команд. Бенефитов меньше.
Эта архитектура предполагает достаточно мелкое деление, которое не разумно для маленьких команд в связи с увеличивающейся стоимостью поддержки инфраструктуры.
При этом сервисную (макросервисную) архитектуру никто использовать не запрещает по необходимости. Тут всегда вопрос компромисса между стоимостью поддеркжи, производительностью и масштабируемостью.
Компоненты != Микросервисы

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

А сложный в плане «сложный» — да, может, но это не имеет отношения к любому механизму обеспечения модульности.
Просто для справки — в Netcracker работает более 3000 разработчиков. Среди них, думаю, не менее 100 чистых фронтендеров.
С вашими объемами все понятно. Я для читателей написал.

Попытка сделать микросервисы в командах по 5-10 человек приводит к такому ужасу… Надо предупредить.
Вы бы такие вещи писали бы как варнинг в начале статье, это очень бы помогло миру.
По настроению в статье показалось, что «делаем, потому что можем», но, возможно, просто показалось. А что с производительностью? Я имею в виду насколько медленнее приложение, обросшее оверхедом и внутренними взаимодействиями, стало работать в браузере? В случае с бэком, это все скрыто множеством серверов, а тут это все вываливается клиенту в браузер.
Спасибо большое за коммент!
Делаем, потому, что размеры решения очень большие и количество команд разработки растет как на дрожжах. Так что скорее показалось.

По производительности — импакт, безусловно есть. Но мы применяем много разных техник оптимизации, чтобы свести их к допустимому минимуму. В некоторых случаях получается даже быстрее чем если бы каждый из компонентов (разработанных разными командами) ходил за одними и теми же данными самостоятельно (сервис локализации, авторизации, интернационализации, конфигурации, дизайн токенов и пр.)
размеры решения очень большие и количество команд разработки растет как на дрожжах
Паттерн «решение организационных проблем техническими способами». :)
Ну не скажи. Мы наоборот решили организационную проблему в бекенде (сильная связанность, боттл нек команды, отсутствие ответственного за функционал) методом деления на микросервисы. А дальше уже решили ту же самую проблему во фронтенде. Технический способ давно известен.
Жаль, что не освещены подробности взаимодействия фрагментов между собой. Например, нам нужно вызвать «метод» (у фрагментов есть внешнее API?) из другого фрагмента, дождаться его асинхронного выполнения и получить результат. Или как решается к примеру проблема, когда сообщение публикуется, но не все подписчики еще инициализировались.
Никаких попыток прямого взаимодействия с другими фрагментами быть не должно.
Также для общения фрагментов между собой есть шина, построенная на Observable и rxjs.

У фрагмента нет публичного API. Фрагмент != компонент. Это не читсая вью — это прямо самостоятельный кусок UI, который самодостаточен и ему не нужно ничего из вне. Поэтому таким фрагментам очень редко нужно взаимодействие с внешним миром. Если оно нужно — делаем через EventBus.
Или как решается к примеру проблема, когда сообщение публикуется, но не все подписчики еще инициализировались.

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

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

Я не сказал, что взаимодействие полностью исключается. Мы исходим из того, что прямое взаимодействие скорее говорит о неверном делении.
Еще замечание по поводу яндекса. Так и есть, они строят страницу из кусочков, поставляемых разными микросервисами. Вот только они пошли чуть дальше — у них есть серверный рендеринг и сборка страницы происходит риал тайм. Подобный подход есть и у других компаний. Есть даже опенсорс решение на посмотреть Project Mosaic
Добрый день! А вы не рассматривали новейшие техники взаимодействия между микросервисами, скажем посредством GraphQL или даже GRPC? Фрагментирование GraphQL medium.com/graphql-mastery/graphql-fragments-and-how-to-use-them-8ee30b44f59e и www.apollographql.com/docs/react/advanced/fragments.html
Здравствуйте! Немного ортогональный вопрос теме статьи :) В целом, не вижу никакого смысла в использовании GraphQL для общения между микросервисами. Но скажу, что мы активно его используем для взаимодейсвия между фронтом и беком.
Те самые фрагменты GraphQL, на которые вы дали ссылку — это совершенно не те фрагменты, о которых я разговаривал. Это просто дополнительные возможности языка запросов в GraphQL.
UFO just landed and posted this here
Нет, не совсем. Web Components — это способ изоляции частей UI и это крутой подход, чтобы не заморачиваться не конфликты стилей, конфликты библиотек и прочее. Как только мы сможем его использовать в полную силу — обязательно наши фрагменты будут строиться на них. Пока же это все только планы и разрозненные имплементации в разных браузерах.
Angular Elements позволяют создавать Web Components, использующие всю мощь ангуляра.
Да, НО… Это не решает поставленные задачи. В браузерах, не реализующих полноценную поддержку Web Components, все равно не возможно добиться изоляции. А значит и их использование не оправданно. Надо сказать, что в некоторых случаях, если фрагмент пишется на Angular, Angular Elements используются для создания фрагмента.
Упомянутая mui-platform доступна в Github? Где можно ознакомиться с техническими подробностями библиотеки? Если нет, планируете ли выложить в общий доступ?
Нет, к сожалению, сейчас она не доступна в общем доступе и к распространению внутри открытого сообщества пока не планируется. С другой стороны, самое важное, что в ней есть — идея, которую я тут и раскрыл.
каждый фронтендный микросервис… может быть написан на любых технологиях

хорошо сказано, это также снижает риски неправильно подобранного фреймворка
Это да, но также и добавляет сложности в поддержке. Т.к. у нас команды действительно независимые и приложения, которые они пишут, достаточно независимы, для нас актуален выбор нужного фреймворка для задачи, решаемой приложением.
На мой взгляд это называется из «мухи — слона». Столько излишних абстракций, сложностей, костылей, правил для команд, правил для правил… Модули на 100% реализуют микросервисный подход во фронте.

Для каждого модуля лучше подходят свои технологии

два приложения, написанных на разных фреймворках, на одной странице

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

компиляцию в единый бандл

Ни в коем случае, всё в модулях!

Кто-то не готов перейти на обновленный API зависимости

Вам всё равно придется при изменении глобальной зависимости (от которой зависят все фрагменты, а этих сервисов даже в статье перечислено много) править код во многих фрагментах, а так как они ещё и на разных технологиях, то это затронет все команды.

Содержимое каждой закладки мы можем сделать отдельным фрагментом

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

Именно так. Только вы это называете проектом, а мы — микросервисом. И да, микросервис написан на едином фреймворке и поддерживается единой командой.

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

Тут вы и правы и не правы. От глобальных зависимостей никуда не деться, хотя свести их к минимуму — вполне возможно. (Кстати, предлагаемый вами, модульный подход увеличивает dependency hell.) Другое дело, что мы об этом очень хорошо подумали заранее и добавили версионирование этой зависимости. Версионируется не только сам артифакт, но и рантайм зависимость — даже если в барузере подгружена новая версия общей зависимости, ваш код со старой будет продолжать работать.

Ни в коем случае, всё в модулях!

Модули потом все равно в единый бандл компилируются. Или вы имеете в виду, что каждый модуль компилируется в js отдельно и подгружается в браузер независимо? Тогда мы перейдем к обсуждению проблем — асинхронности подгрузки модулей, dependency management, производительность.
Значит выбран неподходящий для вашей задачи фреймфорк, но уж точно не позволять в одном проекте писать на разных.

Тут вы не уловили мою мысль. Наш проект — это сложная система, состоящая из выделенных частей (микросервисов). Разными фреймворками могут пользоваться разные части. Но понятно, что каждая часть обычно написана на едином фреймворке. В некоторых экзотических случаях могут использоваться специализированные фреймворки для решения конкретных задач в конкретном месте одной части. Но это уже выбор каждой части в отдельности.
Модули на 100% реализуют микросервисный подход во фронте

Если мы с вами одно и то же подразумеваем под микросервисном подходом (читай начало статьи), то раскройте, пожалуйста, мысль с модульным подходом. Будет интересно.
Именно так. Только вы это называете проектом, а мы — микросервисом. И да, микросервис написан на едином фреймворке и поддерживается единой командой.


почему бы не разнести «микросервисы» одного проекта на разные урл-ы (серверный роутинг), каждая страничка которого собирает свой отдельный бандл.
Таким образом, каждая страница — это отдельный spa и может писаться хоть на реакт-е, хоть на вью, хоть просто классический html с серверным рендерингом. Подумаешь, будет перезагрузка между «микросервисами» — но на то они и микросервисы, раз уж выделили их отдельно. Под этот вариант идеально подпадают закладки в каком-нибудь сайдбаре или те же табы.

Делал так в одном легаси, на одной из страниц которого были «богатые» ui требования, которые снежным комом наращивали костыли. В итоге плюнул, и впилил туда div с реактом)
В целом, и такой подход используется — есть фрагмент «общая шапка», в котором ссылки на разные микросервсы, в каждый из которых этот фрагмент вставлен. Переходы между микросервисами осуществляются с перезагрузкой.

Но как быть с примером из статьи? Есть корзина, владелец которой один микросервис, а отобразить надо в другом (при том с сохранением состояния при переходе)
мы об этом очень хорошо подумали заранее и добавили версионирование этой зависимости

О, это круто, тогда ок.

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

Наш проект — это сложная система, состоящая из выделенных частей (микросервисов)

Значит ваш проект уникален и требует именно такого подхода, поэтому согласен с вами.

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

Я бы все-таки не совсем такой вывод сделал — это специфика микросервисного подхода для крупных проектов. Где UI — это не один портал или один рабочий стол, а целый набор разнородных инструментов, которые так или иначе друг с другом связаны по бизнес процессам. Наша компания тут не уникальна. Гугл, например — каждый сервис может выбрать свой фреймворк для написания (некоторые вообще свои рожают). Мейл — то же самое. Рамблер. Яндекс все-таки пытается стек держать консистентным, но в принципе тоже разные фреймворки попадаются.

Справедливости ради — пока мы выбрали в качестве стандарта де-факто Angular 2+ (6 прямо сейчас) и выбор другого фреймворка для конкретного микросервиса должен быть очень мотивирован.

И еще, когда мы переходили с Angular 1 на Angular 2+ — мы почувствовали все бенефиты подхода, когда микросервисный фронт может одновременно работать на нескольких технологиях.

Мы пошли другим путем. Все под-приложения завернуты либо в веб-компонент, либо в IFRAME если это 3rd-party и там нужна особая безопасность и изолированность.

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

Так вот вся соль в том, что в этой статье я описал именно решение задач, которые встают в момент, когда мы говорим — каждый микросервис отвечает за те куски экрана, которые к нему относятся. А уже как сам микросервис обеспечит изоляцию — это его выбор. Кто-то пытается идти с веб-компонентами — мы не советуем, т.к. спецификация поддерживается очень слабо в самых важных местах. Кто-то заморачивается на специфичность стилей и поставке бандла без внешних зависимостей. Кто-то действительно использует IFrame, если это интеграция с 3rd party. Важен общий подход — если есть кусок UI микросервиса, то только команда этого микросервиса должна за него отвечать.

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


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


Авторизация внутри IFRAME — обычный 3-legged OAuth с Implicit Grant и prompt=none, т.е. тихий редирект, поскольку и хост и приложение ходят на один и тот же login.example.com. Веб-компоненты используют SDK, у которого авторизация глобальна.


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


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

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


Пока получается, что вы прошли тем же самым путём. Чем вдохновлялись?

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


У нас два реестра ;) один — известные компоненты, грубо говоря хардкондый список, мы их сами пишем и к ним доверие (web components) и их можно монтировать руками + реестр установленных у юзера приложений, хранящийся на бакенде, там и наши и сторонние могут быть, это IFRAME/веб-компоненты, интерфейс одинаковый в данном случае, хосту все равно что вставлять на основе урла.


Вдохновлялся… как то само родилось. Поглядывал на приложения VK и FB, на всякие CRM типа ZenDesk, Salesforce и тд., там кто во что горазд извращения, я решил, что надо творчески переосмыслить и пойти простым и понятным путем.


Нам еще следующий шаг предстоит, надо как-то давать приложениям возможность влезать в другие приложения, парсить DOM, добавлять контролы (ну например в список юзеров кнопку с действием) и тд и все это безопасно надо делать, но это совсем другая история.

Я разрабатываю похожую архитектуру. Взаимодействие микросервисов, встраивание UI.

Есть идея реализовать что-то наподобие распределенного Redux. Каждое приложение может обновить store посредством dispatch какой нибудь мессаги (физически это может быть api, message queue и т.п.). Каждый микросервис выбирает себе канал общения со store, реактивный типа веб сокета, pull based через очереди и т.п. Когда в store приходит сообщение от одного микросервиса, он рассылает всем подписанным другим по выбранным каналам.

Вторая идея, которая меня напрягает в реализациях у других, это понятие фичи. Я не понимаю, почему разработчики ограничиваются css/js при загрузке страницы. Хотелось бы нечто подобного на MEF/Prism в .NET с динамической подгрузкой модулей. On demand, когда запрошена какая-то фича из модуля. И фича может быть чем угодно, UI компонент, API wrapper и т.п.

Если поразмыслить, приложение можно построить как граф (DAG) фич. Подграф фич, сам может являться фичей. Какие-то подграфы мы собираем вебпаком в бандлы (модули с фичами). Сами решаем какие в зависимости от размера, и т.п.

Если реализовать управление зависимостями фич, их life cycle (конфиг, активация, деактивация) и варианты стыковки (две фичи не обязательно могут зависеть друг от друга, но отлично могут жить вместе), у меня в голове рисуется картинка, где собирание приложения сводится к набору нужных фич на всех уровнях.
Очень интересно! Но есть ощущение, что понятие фичи в вашем рассказе размыто. Создалось ощущение, что вы фичей как раз называете микросервис. У нас фича — это некая функция микросервиса. И да, она может состоять из чего угодно (чаще всего из FE API, js+css), фключаться и отключаться по запросу. Вот только в нашем понимании, каждый фрагмент может состоять в свою очередь из фич, которые очень даже взаимосвязаны. С другой стороны, взаимосвязанность фрагментов мы не приветствуем. Их цель — разделение ответственности и прочие плюшки микросервисной архитектуры.
Как раз-таки размытость понятия фичи тут помогает. Делаю POC по этому поводу, React/Redux SPA. У каждой фичи есть тип. Например корневая фича имеет тип «react-redux-app». Тип фичи описывает, какие дочерние фичи требуются или могут использоваться. Например, рутовая фича требует фичу типа ui-template (min: 1 — фича UI контролов), может использовать фичи redux-module-batch (min: 0). Последняя может использовать фичи типа redux-module (min: 0).

В общем имеем граф фич. Для упрощения, можно положить что фича имеет фукцию активации, асинхронную, возвращающую Promise.

Теперь разный функционал описывается как F(dag of features). Под F скрывается выбор нужных типов фич для исполнения, обход и активация выбранных фич в топологическом порядке.

Например, app bootstrapper функция F активирует все фичи.
Генерация webpack конфига использует фичи типа redux-module-batch и обходит их отдельно из каждой как из корня.
Планирую добавить новый тип фичи для Gulp таска. Gult -T будет обходить граф и находить все таски.

Сейчас прихожу к мысли, что одного типа немного не хватает. Например, есть фича типа react-dev-tools, хотелось бы ее активировать только в девелопменте. То есть, активационная функция F должна скипать такие фичи для продакшна. Думаю добавить произвольный набор аттрибутов к каждой фиче и механизм зависимостей проапгрейдить с их использованием.

Простите, если все слишком высокопарно. Меня иногда поглощают философские вопросы. Удивляюсь, почему интерфейсами для пакетов являются классы, константы и т.п., а не функции активации с конфигом. А уже внутри можно использовать что угодно. Можно сказать, типа: «пиши, что тебе надобно, используя интерфейс классов и т.п». Но если представить, что помимо классов есть уже реализованные фичи, покрывающие наиболее используемые use кейсы, все было бы гораздо проще.

И вообще, DSL описания графа фич может быть кросплатфоменным. И если реализации фич есть на разных языках, мы автоматом получаем кросплатформенное приложение.
Вы описываете все равно подход со статической линковкой компонентов. Моя же задача сделать так, чтобы если микросервис А использует часть из микросервиса Б, то после обновления микросервиса Б, микросервис А тут же получил обновления без пересборки и перезаливки докер образа. Это и есть принципиальный бенефит микросервисного фронта — только микросервис Б отвечает за то как будут выглядеть и работать принадлежащие ему куски.
Удивляюсь, почему интерфейсами для пакетов являются классы, константы и т.п., а не функции активации с конфигом.

Давно уже придумали экспортировать default функцию-фабрику. debug в node.js использует как раз такой подход.

Отличная статья, спасибо! А каков примерный общий объём «приращения» кода в % после внедрения такого подхода?
Именно кода? Практически никакого. Команды продолжают разрабатывать на своих любимых фреймворках. Иногда даже идет сокращение кода, когда не приходится писать для своего нового фреймворка очередную реализацию авторизации или локализации.
Внутри что-то шевелится, пытаясь как-то намекнуть, что в микросервисах одна из самых главных вещей это все-таки масштабируемость и fault-tolerance, а в однопоточном фронте это несуществующая проблема, но это холивор и к делу не относится.

А вопрос вот такой — в микросервисах в мире backend есть достаточно обычная ситуация:
1. Есть три микросервиса: А, В и С
2. Микросервис А меняет контракт одной своей точки входа окончательно и бесповоротно.
3. Сервис В сидит в соседней комнате и поддерживают новый контракт.
4. Сервис С сидит в другой стране и не успел поддержать новый контракт.
5. При деплое разворачивается две версии микросервиса А: новая и легаси, а с помощью service discovery сервисы В и С обращаются к разным инстансам этого микросервиса.
6. Утром просыпается команда С и обновляет версию, после деплоя которой легаси-версия сервиса А прибивается.

У вас не возникает такой проблемы, особенно с единой шиной взаимодействия всех микросервисов? Как вы решаете проблему версионности?
Да, вы правы, проблема версионности тут встает в полный рост. Тот подход, который вы описали для мира бекенда, называется canary deployment. Но это не единственный способ решить поставленную задачу. Также можно сделать, чтобы микросерис А версионировал свой API. Т.е. любой эндпоинт микросервиса А будет иметь версию. Например, /microserviceA/api/v1/endpointA. Микросервис должен гарантировать, что при любом изменении контракта этого эндпоинта, он получит новую версию, но старая продолжит работать по-старому.

Вся эта проблема возникает по причине так называемой runtime зависимости. Т.е. зависимость, которую невозможно проверить во время компиляции.
То же самое происходит и с фрагментами и с платформой, которая их менеджит. Приведу пример решения этой проблемы в платформе. У нас есть артефакт, который подключают к себе на страницу все микросервисы — он никак не версионируется (в URL нет ничего, говорящего о версии), но внутри него любой метод версионирован по принципу версионирования API микросервисов (methodA_v1, methodA_v2...). Кроме того, мы поставляем статическую зависимость (для каждого фреймворка свою), которая по сути является оберткой для вызова тех самых методов с версией. Она версионируется и вызывает соответствующую версию метода. Следовательно, не важно с какой версией обертки собран фрагмент, он сможет работать независимо от того, есть ли уже более новая версия зависимости runtime.

Надеюсь, что не очень сумбурно :)
Нет, это не сумбурно и понятно — проблему версионности уместнее всего решать, собственно, версионностью.
Я думал о ситуации, когда новая версия категорически отметает обратную совместимость. Такое ведь тоже бывает? Никому не нравится видеть у себя в коде кучу устаревших контрактов, даже если эти контракты в отдельной сборке (субъективненько).

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

Насчет fault-tolerance. Допустим, один микросервис упал, а другой в это время к нему обращается с целью например что-то обновить в себе. Единая шина умеет в MQ? Если нет, мне кажется, это было бы очень круто, научить ее отправлять в «свежеподнятый» компонент все неполученные им в момент простоя сообщения.
Я думал о ситуации, когда новая версия категорически отметает обратную совместимость.

Такое безусловно может быть. В этом случае и тут можно использовать canary deployment. На данный момент наша разработка этого не поддерживает, но не вижу ничего сложного в реализации.
это было бы очень круто, научить ее отправлять в «свежеподнятый» компонент все неполученные им в момент простоя сообщения

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

1. Backend-case: есть микросервис, который общается с SMTP-сервером. Есть другой микросервис, который должен зарегистрировать пользователя, в числе прочего отправив письмо ему на мыло. Если в настоящий момент микросервис отправки писем не работает, то мы либо не сможем создать пользователя совсем, либо создадим пользователя и не отправим ему письмо. Первый вариант не совсем fault-tolerant, второй вариант вообще неприемлим. Если пользоваться MQ, то можно отправить письмо в момент подъема сервиса/SMTP.

2. Frontend-case: допустим, у меня есть сайт с TODOList-ом, который я написал, чтобы продемонстрировать работу вашего подхода. В нем два микросервиса — непосредственно список и счетчик завершенных задач, первый написан на реакте, а второй на vuejs. Никакого сервера вообще не имеется, и я публикую события в духе «создано новое туду» или «туду сделано». Но в какой-то момент микросервис счетчика упал окончательно и бесповоротно. К счастью, он сохранил свое состояние в локалстораже и при подъеме сможет себя из него восстановить. Но вот дела, пока он лежал без чувств, другой микросервис отправил вникуда пяток сообщений «новое туду», и юзер рискует увидеть несоответствие списка счетчику.

Бекенд кейс валидный, но к топику не относится, т.к. работа идёт с персистант состоянием. Тут уже давно придуманы RabbitMQ и всякие Kafka.


А вот фронтенд кейс не очень хороший. У вас есть два микросервис, которые работают с общим состоянием, но при этом состояние это хранят в разных местах. Это сразу же архитектурный промах. В моем понимании эти два фрагмента должны иметь общий сторадж (в вашем случае локалсторадж) и обмениваться эвентами только для того, чтобы сообщить, что данные надо обновить. Далее все решится само.
Есть другие кейсы, где это действительно нужно? Т.к. реализовать персистант шину на клиенте не сложно. Хоть и не понятно зачем.

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

Тоже люблю философствовать, поэтому позвольте прокомментировать. На мой взгляд, вы перечислили не одну вещь, а две разных :) И забыли третью очень важную — независимость (в том числе независимость обновлений). Вот именно это вполне достижимо и приносит очень хорошие плоды. А в целом, масштабируемость для фронта — это пока что-то непонятное. Но вот fault-tolerance вполне себе проявляется в нашем подходе. Например, если один из микросервисов упадет или не поднимется в облаке, любой другой, кто хочет показать в себе его кусок, просто ничего не покажет на этом месте. И пользователь сможет продолжать выполнять задачи присущие только не упавшему микросервису. Вуаля :)
UFO just landed and posted this here

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

Спасибо за статью. Вставлю свои 5 копеек. Я так понимаю у Вас в компании используется горизонтальный принцип построения команд — разделение на бэкенд и фронтенд. Я участвовал в переходе от горизонтального принципа управления проектом к вертикальному, и это привело к значительному росту производительности. У нас было две команды 20 бэкенд и 10 фронт разработчиков и мы не справлялись, планировали нанимать еще 20 человек. После реорганизации по вертикальному принципу — получилось 8 команд, у каждой свои цели и задачи (по несколько микросервисов во владении). Постановка задач и реализация стала проходить намного быстрее и качественнее. Сейчас об увеличении штата уже никто не думает, каждая команда раскидала за пару месяцев задачи которые ставились на полгода.

Как раз не так :) Команды у нас поделены вертикально. Именно поэтому очень важно поделить и фронт на микросервисы и сделать его куски действительно независимыми. Иначе мы могли бы жить с одним жирным SPA князь кактус.

Все очень просто если следовать компонентной структуре. А с перформансом все вообще будет зашибись если использовать один Фреймворк ;) или не использовать вообще


Описание:


  1. К-во команд равно к-во микросервисов +1 (product team)
  2. Каждая команда пишет 1 или набор web components (я о стандрарте который поддерживают большинство современных броузеров); под капом что угодно, например Stenciljs или тяжеловесы как React и Angular, как и любой компонент они ожидают параметры и викидывают какие-то ивенты
  3. Product team объединяет компоненты написанные другими командами в одно целое (в зависимости от простоты внедрения зависимостей команда может состоять из джунов или заменена на автомейшн build)
  4. Продукт пишется на любом доступном фреймворке или без него. Веб компоненты получают все необходимые данные, продуктовая команда решает что делать если сервисный компонент выбросил ошибку.

Понятие «микросервисы» не применимо на фронтенде, среда то одна ;)


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

Спасибо за комментарий.

1. Стандарт WebComponents еще не принят полностью. Остаются очень важные (ключевые) части, по которым нет согласия. В браузерах поддерживается по факту только Custom Tag. А вот важнейший Shadow Dom не поддерживается много где (https://caniuse.com/#feat=shadowdomv1), а где поддерживается — есть отличия в поведении. Только не говорите мне о полифилах!!! Если браузер не может изолировать два дом дерева друг от друга, то никакой полифил этого сделать не сможет.
3. «К-во команд равно к-во микросервисов +1 (product team)» — в этом случае вы приходите к моей архитектуре. Только мне еще и продакт тим не нужна. У нас один микросервис — это, например, админка, црм, каталог, цмс.
2. Product Team, как в вашем плане, собирающий интерфейс из веб компонентов станет бутылочным горлышком. Ему придется понимать как работает каждый компонент в отдельности. Ведь именно от этой команды будут зависеть 70% результата.
3. Использовать единый фреймворк — да! Это круто. Но есть узкие задачи. Есть команды с разной историей и опытом.
4. «Понятие «микросервисы» не применимо на фронтенде, среда то одна ;)» — ошибаетесь. Я так же могу сказать, что понятие микросервисов не применимо на бекенде — система, на которой они крутятся, одна же! Самое важное не то, что эти фрагменты в конце концов оказываются на одной странице, а в том, что мы даем возможность делать куски этого финального интерфейса достаточно независимыми и действительно независимыми командами. А значит подход микросервисный.
  1. Вам нужен shadow DOM чтобы писать на любом из современных фреймворков?) зачем тогда он Вам нужен чтобы писать на веб компонентах? А бандл будет намного меньше и шустрее. Посмотрите в сторону web components compilers (один из них https://stenciljs.com)
  2. Как уже писал это или команда джунов или автомейшн билд. В зависимости от сложности взаимодействия. В Вашем случае это билд
  3. Не нужно им ничего понимать. Им нужно знать что дать на вход и нужно ли что-то ожидать на выходе. Инкапсуляция и все дела :) на самом деле конечный продукт будет собираться очень быстро и очень просто.
  4. Возможно Вы правы. Но все же, на фронте один Локал сторадж, window, document и ещё куча всего. На backend такого нет, или же не должно быть.
Самое важное не то, что эти фрагменты в конце концов оказываются на одной странице, а в том, что мы даем возможность делать куски этого финального интерфейса достаточно независимыми и действительно независимыми командами. А значит подход микросервисный

Всё таки это модульный подход. Можете считать ещё плагинным подходом. Но уж точно никак не микросервисным — иначе любая современная IDE будет "микросервисной"

serjoga
1. Мне он не нужен, чтобы писать на разных фреймворках, но он мне критически необходим для организации изоляции фрагментов на странице. И да, как только веб компоненты начнут нам приносить еще какую-то пользу кроме возможности объявить кастомный тег, мы обязательно используем их во фрагментах. Но фишка в том, что фрагменту не важно как он написан. С использованием web components или нет. Работать все равно будет хорошо и целей своих достигнет.
2. Тут слишком глубоко :) Не понял.
3. Ну это очень спорные утверждения. Это получается, что если дать любому джуну material-ui, он любой интерфейс за день замонстрячит?
4. Все верно говорите, есть шаренные хранилища в браузере. Также как и куча шаренных хранилищ на бекенде — БД, кеш, CDN, сервер конфигурации, idm и много всего другого.

Borz
Я понимаю уровень фрустрации, который вношу подобным названием, но все-таки я вижу принципиальную разницу между подходами. IDE может быть написана разными командами, даже достаточно независимыми, но все равно она билдится в некий единый бандл. И как только эта компиляция прошла — все, владелец модуля не может обновить только свой кусок. Также на лицо остальные минусы модульного подхода типа завязки на определенную технологию и невозможность использования узко специализированной.

а как же плагины к IDE, расширяющие функциональность и интерфейс?

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

Но безусловно, некое сходство можно найти между плагинами и микросервисным фронтендом.

Если уж совсем на чистоту, то сам по себе микросервисный подход на бекенде тоже можно методами последовательных приближений сравнить с OSGI, которым пользовались довольно безуспешно в монолитах.

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


Как такое реализовать в вашей парадигме?

Очень интересный вопрос. В моей парадигме хост страница отвечает за позиционирование блока, если он не абсолютно спозиционирован и сам блок, если абсолютно. Более того, в моей парадигме и кнопка и сама форма — суть один фрагмент (не сервис!)). Т.е. поведение кнопки и, соответственно, позиционирование формы всегда будет соответствовать настройкам (модели), которую вы передадите при инициализации фрагмента.
Sign up to leave a comment.