Спасибо за статью! Я буду благодарен, если вы поделитесь своим мнением по следующему вопросу.

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

Так ли это важно, на каком уровне выполнена декомпозиция на отдельные независимо работающие части кода?
Я отмечу, что это перевод, может быть вы не там спрашиваете.

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

микросервисы — это про горизонтальное масштабирование. Монолит плохо и нерационально масштабируется.

То что описано в статье по вашему монолит?

я лишь ответил на коммент — где происходит декомпозиция важно, т.к. цель микросервисов — достичь хорошего масштабирования. Декомпозиция внутри приложения на это никак не влияет.
А в статье что-то странное, оценивать не буду.

В большинстве случаев декомпозиция в монолите выполнена намного хуже, чем в микросервисах. Типичный пример — у каждого stateful микросервиса обязательно собственная БД. В монолитах использование десятка разных БД (хотя бы на уровне разных логинов и database в одном MySQL) практикуется… практически никогда. Помимо изоляции данных, далеко не каждый язык программирования позволяет действительно надёжно изолировать разные части монолита друг от друга, не на уровне соглашений между разработчиками, а с жёстким контролем средствами языка (напр. в Go поддержка internal пакетов появилась всего 3 года назад).


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


Так что в теории да, можно делать микросервисы внутри монолита (встроенные микросервисы), но на практике это пока редко встречается.

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

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

Разве декомпозиция напрямую зависит от выбора архитектурного паттерна монолит/SOA?
Значит ли это, что мир монолитных приложений погряз в проблемах композиции?

В монолитах использование десятка разных БД (хотя бы на уровне разных логинов и database в одном MySQL) практикуется… практически никогда.

На какие практики или исследования вы опираетесь?
Исходя из моего опыта продуктовой разработки ваше утверждение ложно.

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

Поясните, пожалуйста, почему SOLID и GRASP не подходят, а требуются какие-то жесткие средства языка.
Обладают ли этими жесткими средствами C++/C/C#/JAVA?

в Go поддержка internal пакетов появилась всего 3 года назад

Можете пояснить ваш use-case использования internal packages в контексте замены SOA на монолит?

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


Есть какие-то исследования, что все ПО написанное до второго бума SOA «не работает»?
Chromium/V8 тоже не работают?

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


Разве декомпозиция напрямую зависит от выбора архитектурного паттерна монолит/SOA?

В теории — нет. Но на практике эта зависимость однозначно наблюдается. Проблема в том, что если у разработчиков есть возможность (а в монолите — она обычно есть) нарушить архитектуру и сделать что-то неправильно, создать какие-то связи между компонентами, которых быть не должно — они сделают это, причём скорее рано, нежели поздно. Из-за отсутствия квалификации, понимания, или просто времени на то, чтобы сделать "правильно". В SOA намного сложнее и дольше создать такие некорректные связи, потому что для того, чтобы добраться до недоступного через API функционала стороннего сервиса из другого сервиса сначала надо изменить и выкатить первый сервис, добавив в него дополнительное API. Эта дополнительная сложность создаёт препятствие, которое довольно эффективно мешает портить текущую архитектуру. Конечно, при наличии должной степени упёртости можно и это преодолеть, но обычно архитектуру портят не из желания сделать гадость, а из желания сделать проще/быстрее — и вот в SOA делать такие вещи уже ни разу не проще и не быстрее, что очень помогает.


Значит ли это, что мир монолитных приложений погряз в проблемах композиции?

Не то, чтобы буквально погряз… но да, архитектурный паттерн big ball of mud определённо появился в монолитах, и, насколько мне известно, в микросервисах пока не встречался.


На какие практики или исследования вы опираетесь?
Исходя из моего опыта продуктовой разработки ваше утверждение ложно.

Лично я прямо сейчас пишу что-то вроде монолита/встроенных микросервисов, и у меня в одном приложении несколько изолированных логинов и database в MySQL. Тем не менее, где либо кроме своих собственных проектов я этого подхода не видел, ни в компаниях, ни в опенсорс-проектах. Так что хотелось бы узнать про Ваш опыт подробнее — где (кроме собственных проектов) Вы такое видели, и как часто это встречается.


Поясните, пожалуйста, почему SOLID и GRASP не подходят, а требуются какие-то жесткие средства языка.

Потому, что наличие в монолите какой-то библиотеки/класса с публичным интерфейсом ещё не означает, что ими можно пользоваться из любой части монолита.


Обладают ли этими жесткими средствами C++/C/C#/JAVA?

Я не эксперт в этих языках, так что с гарантией не скажу. Насколько мне известно — скорее нет.


Можете пояснить ваш use-case использования internal packages в контексте замены SOA на монолит?

Пакеты в подкаталоге internal/ в Go не доступны никому в родительских/соседних каталогах, что позволяет гарантировать что вызывать эти библиотеки/классы из другой части монолита не получится, если только не открыть к ним явно доступ через API пакета находящегося в том же каталоге, что и internal. Этот подход вполне соответствует стилю работы с микросервисами — не важно, какой код доступен внутри микросервиса, снаружи есть доступ только к тому, к чему явно предоставили API.


Есть какие-то исследования, что все ПО написанное до второго бума SOA «не работает»?
Chromium/V8 тоже не работают?

Не надо передёргивать, я лично таких утверждений не делал. Если хотите услышать утверждения, с которым можно всласть поспорить — пожалуйста:


  • до определённой границы сложности монолит писать проще микросервисов, после этой границы проще писать микросервисы
  • микросервисы добавляют заметное количество дополнительной сложности из-за сетевого взаимодействия, но эта сложность константная во всех проектах и не увеличивается с развитием проекта, в отличие от ситуации с большинством монолитов, что и является причиной предыдущего утверждения
  • большинству проектов не нужны возможности писать микросервисы на разных ЯП и запускать их на разных серверах, нужна возможность быстро развивать большой проект, в идеале — монолит… кандидатом для решения этой задачи являются "встроенные микросервисы", которые позволяют получить идентичный микросервисам уровень изоляции между командами разработчиков и кодом внутри монолита, избегая дополнительной сложности из-за сетевого взаимодействия… но чтобы это работало крайне желательно иметь поддержку на уровне инструментов — хоть языка, хоть линтера, хоть pre-commit хуков, но чтобы работало автоматически и мешало из кода одного модуля лазить в данные или код другого модуля мимо его публичного API

По моим наблюдениям, микросервисы требуют заметно большей квалификации от архитектора и меньшей квалификации от разработчиков, чем небольшие монолиты… но большие качественные монолиты, которые не тонут в растущей сложности и не превращаются в большой комок грязи, требуют такой же (а может и большей) квалификации архитектора как микросервисы плюс намного большей квалификации разработчиков, чем микросервисы. Иными словами, без хорошего архитектора к микросервисам подступаться бессмысленно. А с хорошим архитектором и средней командой разработчиков можно осилить микросервисы, но не большой и сложный монолит. Ещё иными словами: нормально написать большой монолит тупо дороже, чем то же самое на микросервисах. Идея со встроенными микросервисами в том, что они должны оказаться заметно дешевле обычных микросервисов, ценой потери незначительных для большинства проектов возможностей (разные ЯП в одном проекте, запуск разных частей проекта на разных серверах).

Не то, чтобы буквально погряз… но да, архитектурный паттерн big ball of mud определённо появился в монолитах, и, насколько мне известно, в микросервисах пока не встречался.

В микросервисах он мигрировал на следующий уровень абстракции :-) и может быть найден в скриптах деплоя и конфигах.

Есть такое. Но там small ball of mud :) — сколько там тех конфигов и скриптов, по сравнению с общим объёмом проекта.

можно делать микросервисы внутри монолита
Только… в таком случае они будут называться Bounded Context.
Прошу прощения, вопрос Вы не мне адресовали, но моё личное мнение такое: когда разделение идёт на высоких уровнях, то возникают дополнительные накладные расходы на взаимодействие, в случае с контейнерами это вообще происходит по сети, что очень медленно. Даже если в исходном коде все данные будут передаваться по значениям а не ссылкам (через копирование), это всё равно получится быстрее в разы.
Производительность складывается из latency + throughput.

Какой-бы не был супер-монолит, с передачей всего по ссылкам и прочими ультра-оптимизациями, настанет момент, когда одного сервера перестанет хватать. Но всегда есть функциональность, которую нельзя просто накопипастить по серверам без взаимодействия между ними (например, регистрация аккаунта с проверкой уже занятых логинов), или ведение сессий пользователя. Появится необходимость специализации серверов, и это ведёт к сервисам.
Ну, по сути, более-менее серьёзное веб-приложение состоит по умолчанию в наше время из двух-трёх сервисов типа веб-сервер, апп-сервер (может быть реализован как модуль веб-сервера), сервер СУБД. Причём первые два обычно легко горизонтально масштабируются.

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

что там может быть такого в обычных веб-приложениях, что нагружало бы целые кластера серверов. Не сталкивался с таким.

А что для вас обычное веб-приложение? Если мы захотим вполне банальный агрегатор отелей или агрегатор авиаперелетов и будем максимально хранить кэши в памяти у нас быстро память закончится, ибо комбинаторный взрыв. Просто вы вероятно не работали в компаниях с достаточно большой аудиторией.
Я вообще в компаниях не работал, потому сразу и написал, что не сталкивался с таким. Из наиболее крупного разочек делал средних размеров кластер на докерах, и писал к нему систему управления — вертелось по яве и базе данных на каждого пользователя (схоже с авторами статьи), где-то около тысячи таких инстансов было на пике. Но вместе с тем я до сих пор считаю, что вместо пяти гигантских серверов на всю эту кухню было достаточно одного средних размеров с одним правильно построенным приложением. В итоге проект так и помер, ребята устали платить за сервера, да и постигать всю кухню докера и его багов (а их там достаточно было) тоже не всем хотелось.
Ваш пример с агрегаторами интересен… но неужели там такие гигантские объёмы, с чем не справятся современные субд?
неужели там такие гигантские объёмы

Ну давайте посчитаем, гостиниц в Москве скажем 1 тыс. (учитываем всякие частные гостиницы/аппартменты/хостелы и т.д.), вариантов размещения (общий номер, одноместный с общей ванной, одноместный с собственной ванной, двухместных, люкс и т.д.) до 20 штук (20 тыс. записей), нам нужно хранить цены с завтраком и без (40 тыс.), с возможностью отмены и без (80 тыс), на каждый день в году (30 млн), так же возможны скидки отеля при бронировании нескольких дней подряд (все возможные интервалы в течении года — много, ну скажем еще на 10 умножим (250 млн), на нужны цена так же на разное кол-во взрослых и детей в номере (еще на 10 — 500 млн), цены могут приходить как от самого отеля, так и от систем бронирования (вроде букинга) — скажем еще с 10 партнеров (5 млд. записей), каждая запись кроме цены/валюты еще (как минимум) должна хранить id на сайт партнера и время до которого она валидна. На самом деле, скорее всего параметров будет больше.

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

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

Для авиакомпаний можете сами посчитать, например сколько параметров может быть только для полета из одной Москвы.
В случае Таненбаума и Линуса речь шла совсем не о том же самом что в статье. Точнее эти два спора нельзя сравнивать в принципе из за совершенно разных задач и рисков проектов. То есть риски Ядра операционной системы не всегда и не везде можно сравнивать с рисками веб проекта. Иначе говоря всему свое место.
Спор Линуса и Таненбаума, о том, что Линус говорил, что «сложность» никуда не девается.
Просто «сложность» ядра, переносится в «сложность» взаимодействия между микроядрами.
И он прав.
В микросервисной архитектуре сложность «монолита» переносится на «сложность» взаимодействия между микросервисами.
Всё зависит от того как спроектировано это самое взаимодействие. Я-бы сказал это самая критическая точка в системе.
[Зануда mode]
Тащем-та Танненбаум с Торнвальдсом спорили по поводу микроядро vs монолит относительно разработки операционных систем. Понятия микросервисы, в те достопамятные времена, просто не существовало. Ну а сейчас такой спор просто бессмысленен, с появлением микроядер следующих поколений(L3, L4, QNX Neutrino etc).
[/Зануда mode]
Хм, у нас в команде есть похожая проблема, но там причина не в микросервисах
А в том, что очень многие программистыкодеры не понимают микросервисы, не представляют, что значит распределённая система. У каждого второго что-то «подключиться к localhost:8080» захардкожено. Потом он запускает нативно приложение — всё работает, а в контейнере не работает, и он емучается с этим несколько часов.
Микросервисы применимы не везде. Есть вещи, которые не зависят от уровня программистов. Наибольшим препятствием для использования микросервисов являются CAP-теорема и распределенные транзакции. Поэтому микросервисы применимы лишь там, где эти проблемы допускаются бизнес-логикой.
Спасибо что подделились, но, по факту, у вас не было микросервисной архитектуры. Вы ее неправильно поняли. Делить нужно было по ролям, сделать возможность масштабирования конретной роли и вся ваша боль ушла бы. Вы же раскидали по разным воркерам код и назвали это микросервисной архитектурой. От неправильного ее формирования вы и получили проблемы и с саппортом и вот эти
Дополнительная проблема заключается в том, что каждый сервис испытывает разную нагрузку. Некоторые сервисы обрабатывали несколько событий в день, а другие – тысячи в секунду.
Обожаю эти комментарии в стиле «вы просто не умеете их готовить» :) Раз говорим про роли, то давайте критерий (желательно универсальный), по которому эти роли выделять.
Желательно универсальный, сферический, серебряный…
Был монолит, худо бедно справлялся с нагрузкой, потом клиентов стало больше, офисы стали расти как грибы и отдельные операции вешали систему. Хотели переписать, но не срослось. В итоге поделили на микросервисы на основе docker. Нагрузку размазали по серверам, стало гораздо лучше.
По началу на каждый чих создавался микросервис, печать документов — сервис, внешние заявки — сервис, плохие клиенты — сервис. Сейчас их большое количество, не 140 конечно, но около 25, хорошо они хоть друг от друга не так часто зависят.
В общем если сервисов около 20, то админить и тестировать их не сложно, развернуть весь стек приложений тоже, масштабировать и тп., но вот если их больше — то начинается боль, особенно если новый человек хочет разобраться в этом.
— А какой уж там сервис отвечает за X функционал? и какой — за Y?
— Я не мог пол дня настроить окружение, блин они еще друг от друга зависят.

В общем мой совет — не перебарщивать с количеством и делить сервисы на роли.
А как же инфраструктура как программа?! :-)
Т.е. настройка и конфигурация должны настраиваться не в ручную, а быть в виде программного кода. С обязательным покрытием тестами.
Со всеми плюшками DI, тогда зависимости для развертывания dev-окружения можно mock-ировать

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

Как будто в микросервисах говнокода не бывает...


В монолите у меня есть хоткей чтобы увидеть кто еще этот код использует.

Бывает. Но сам по себе микросервис настолько маленький, что переписать его легче. При этом можно быть точно уверенным, что все остальное не упадет. ;-)
А вот это не факт. Может, все остальное и не упадет — но работать в случае ошибки точно не будет, ведь микросервис был для чего-то нужен!

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

А вот хорошая архитектура и монолит, это из области фантастики. :-)
Прекрасная история про то, как чуваки вместо пачки одинаковых библиотек сделали пачку одинаковых микросервисов, закономерно на этом огребли, и говорят про то, как хорошо быть в монолите…
Очередная команда, решившая заюзать архитектуру потому, что она модная, и разочаровавшаяся в ней потому, что не подошла под задачу/кривые руки/не осилили? Ох уж эта хайп-полезнь (

Не надо пожалуйста переводить термины, не сразу понял, что
Полезная нагрузка = payload
Конечная точка = endpoint

Наоборот, везде, где есть адекватный аналог в русском языке, надо использовать русский, а английский оставлять только для плохо переводимых терминов… Вымораживают тексты с постоянными «эндпоинтами» и «пейлоадами»…
Всё от хайпа, согласен. Решили хайпануть и напоролись. В программировании, увы, это сплошь и рядом. У нас сервис пока монилит, до тысячи задач одновременно, насколько я видел. Пока справляется как есть. Изоляция — отдельная проблема, но решаемая. Подумываю насчет разделения на несколько (не микро) сервисов. Инструмент — Delphi, проект порядка миллиона строк почти в одно лицо. Веб (без апача и аналогов), некоторые специфические протоколы, терабайты прокачиваемой через сервис информации.
Вообще странно что ТС напоролись на проблему изменений.
Такое ощущение, что DI к в команде не практикуется. :-)
Заголовок спойлера
Рассказ неосиляторов :-)


Микросервисы требуют большей дисциплины, большей команды и большей компетенции. Если чего-то из этого не хватает, то будут проблемы.
То есть у них по сути были некие модули, выполнявшие одинаковые операции: получить данные от клиента, замапить на новую структуру и отправить другому API. Они каждую отдельно реализовывали, огребая на тестировании всего этого? Вместо того, чтобы создать что-то типа базового сервиса с зависимостями от маппера и способом общения с получателем, развернув в итоге просто несколько копий этого базового сервиса: Мапперы и пересыльщиков можно тестировать отдельно, а DI-контейнеры решат проблему компоновки сервиса для каждого из направлений. Что не так в этой логике?
Думаю, дело в том, что любой серьёзный, динамически развивающийся проект постоянно меняется и может меняться очень сильно. В начале делается прототип для одной концепции, потом концепция меняется, прототип дорабатывается с минимальными затратами по времени, потом ещё, ещё и ещё. В какой-то момент получается «некоторое дерьмо», которое сложно поддерживать и развивать, напрашивается рефакторинг. Это нормальный процесс жизни проекта без чёткого понимания конечной цели.

А самая большая проблема в таких проектах, по моему опыту — это донести до менеджера, что не бывает гибкой разработки проектов (agile) без рефаторинга. Иногда надо остановиться и существенно перетряхнуть внутренности проекта, что может занимать немало времени.
Согласен.
Микросеврисная архитектура и предполагает, что проще переписать, чем изменять. :-)
Я не любитель микросервисной архитектуры, так что люто плюсую автора.

Был опыт развития проекта, построенного на микросервисной архитектуре. Окончательно меня бомбануло, когда разработчик несколько дней дорабатывал API сервиса для того, чтобы сделать подобие LEFT JOIN между двумя сервисами. И работало это на 2-3 порядка медленнее, чем запрос в базу. После было принято решение объдинить сервисы в монолит. Жалею только о том, что не сделал это сразу, как пришёл в проект, много времени было потеряно зря. Но, безусловно, это не проблема концепции, это проблема архитектуры проекта и изменчивость требований заказчика.

Сама по себе микросервисная архитектура не плохая и не хорошая, просто на хайпе её суют куда попало, в каждый «утюг», это же, блин, модно… А каждый инструмент надо использовать с умом, там где он реально помогает решать задачу.

Спасибо автору за то, что поделился опытом. Здорово, что не упёрлись рогом в концепцию, а стали искать другие решения. Может они ещё и вернутся к микросервисной архитектуре, но в другом виде…
Я не любитель микросервисной архитектуры, так что люто плюсую автора.

Был опыт развития проекта, построенного на микросервисной архитектуре. Окончательно меня бомбануло, когда разработчик несколько дней дорабатывал API сервиса для того, чтобы сделать подобие LEFT JOIN между двумя сервисами. И работало это на 2-3 порядка медленнее, чем запрос в базу. После было принято решение объдинить сервисы в монолит. Жалею только о том, что не сделал это сразу, как пришёл в проект, много времени было потеряно зря. Но, безусловно, это не проблема концепции, это проблема архитектуры проекта и изменчивость требований заказчика.


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

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

Об этом у меня написано:
Сама по себе микросервисная архитектура не плохая и не хорошая, просто на хайпе её суют куда попало, в каждый «утюг», это же, блин, модно… А каждый инструмент надо использовать с умом, там где он реально помогает решать задачу.

Почему?


Потому-что это инструментарий. Это не вопрос личных симпатий и антипатий.

В процессе обучения любой технологии первое время всегда тратим на:
— зачем эту технологию изобрели, какую проблему решали? «вот жили люди спокойно 10 лет, а потом придумали это. зачем? думаете правда им было нечего делать?»
— границы ее применимости
— когда ее стоит применять
— любое решение — это tradeoff. Чего на что? Сюда-же плюсы и минусы технологии в общем.

Сама по себе микросервисная архитектура не плохая и не хорошая, просто на хайпе её суют куда попало, в каждый «утюг», это же, блин, модно… А каждый инструмент надо использовать с умом, там где он реально помогает решать задачу.


А вот с этим соглашусь. Беда в том, что автор оригинальной статьи не только не понимает когда использовать технологию, но и не понимает ее вообще.
Потому-что это инструментарий. Это не вопрос личных симпатий и антипатий.
Но почему у меня не может быть любимой лопаты? ))))
Беда в том, что автор оригинальной статьи не только не понимает когда использовать технологию, но и не понимает ее вообще.
Мне кажется, это проблема людей вообще, мы не можем всё знать и совершаем ошибки… Автор поделился своим опытом, сообщество обсудило, а автор, думаю, вынесет из обсуждения что-то полезное для себя.

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

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

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

Беда в том, что автор оригинальной статьи не только не понимает когда использовать технологию, но и не понимает ее вообще.


Это случайно не вы Джессике Фразелли недавно объясняли как контейнеры работают?
Э-э-э???
Если нужен был LEFT JOIN и была база, то почему бы не написать микросервис, который это делает?!
Т.е. если можно (и это проще) использовать готовые микросервисы, то надо ими пользоваться.
Если проще написать «с нуля», то почему бы и не написать «с нуля»?!
Тут вопрос к «архитектору», ну или кто отвечал за общую структуру проекта.
Почему он решил, что надо делать так?!
Я так и написал, что это проблема архитектуры:
Но, безусловно, это не проблема концепции, это проблема архитектуры проекта и изменчивость требований заказчика.
При чем тут архитектура.
Как реализовать микросервис, это задача программиста.
Если проще/удобнее напрямую обратиться к БД, то почему бы и да.
Просто «архитектор» мог поставит жесткие условия, что сделать надо так и никак иначе.
ИМХО, «архитектор» должен говорить, «что» будет делать микросервис, а «как» пусть программист сам решит.
Благо микросервис должен быть таким, чтобы он был понятен любому программисту.
Соответственно переписать его может любой программист.

Если, например, вы считаете, что проще/быстрее/удобнее использовать 1 один запрос к БД. То микросервисная архитектура никоим образом это не запрещает :-)
Вполне себе запрещает. Есть микросервис, у него есть своя приватная БД. По идее никто кроме него в эту БД лазать не должен, а все остальные должны обращаться к его публичному апи. Потому что если это разрешить — начнется совсем адский ад, который обычно ставят как пример недостатков монолитов — нарушается изоляция и модульность, и все летит кувырком как только кто-то начинает менять структуру этой бд. Причем если в случае монолита можно хотя бы более-менее просто обнаружить, что в бд лазает какой-то другой участок кода, и поправить его, то в случае микросервисов этот участок кода может лежать в другом репозитории и даже на другом языке программирования.
Честно говоря, еще на заре хайпа по микросервисам, я еще слышал максиму, что каждый микросервис должен работать только с приватной БД.
Но потом, практика показала, что это не максима и вообще хранилище данных это отдельная тема.
Т.к. желательно чтобы микросеврисы были без состояние, то это стоточение где-то должно быть.
Одно из хранилищ состояния может быть, например, БД.
Куда микросервисы обращаются за данными.
Поэтому в публичном АПИ, просто создается микросервис с JOIN-ом, к которому обращаются при надобности :-)
Все правильно описали. Называется этот подход Integration Database.
Суть микросервисов именно в «как», в максимальной изоляции их друг от друга, в единственной задаче которые они решают.

Если нужно делать «джойн» данных нескольких микросервисов на уровне запросов к БД, то это, в общем случае, нарушение принципов MSA.
Суть микросервисов в максимальной изоляции бизнес-логики, но не данных!
Т.е. разделения то что не должно меняться (данные) и то что может быть изменено со временем (логика работы с данными).
Почему данные не должны меняться — потому что история. Мы должны знать что у нас было в определенный момент времени.
Поэтому данные не меняются, а добавляются.
Так что общее хранилище данных для микросервисов — это норма.
При чем это может быть не только БД, но например kafka :-)

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


Эта проблема не распространяется на кафку и аналогичные сервисы очередей потому, что данные в них изначально описываются как API — какие у нас топики, какие бывают типы событий, какие по ним даются гарантии, etc. ну и плюс там всё-таки нет свободного RW доступа к этим данным как в обычной БД.


Таким образом, максима про приватную БД у микросервисов это не какая-то глупость времён "зари микросервисного хайпа", а суровая реальность. Конечно, если у Вас данные с БД по своей природе иммутабельные (append-only), плюс формат схемы БД описан и не меняется, то по факту такая БД ничем не отличается от любого другого API — микросервисам доступны только операции которые не могут "сломать" данные в БД (кстати, чтобы это гарантировать, крайне желательно ограничить разрешённые операции для тех аккаунтов, под которыми микросервисы подключаются к БД, исключительно SELECT и INSERT, если речь об SQL) и сохраняется обратная совместимость. В этом случае никакой проблемы чтобы с этой БД работало несколько микросервисов нет. Но, мягко говоря, это не самый типичный сценарий использования БД, поэтому ту "максиму" он никак не отменяет.

Вы приводите в пример, только БД.
При изменении схемы данных, все равно будут меняться микросервисы, которые обращаются к микросервису работающему с БД.
Т.к. контракт скорее всего поменяется.

Если он не поменялся… То зачем изменение :-)

Изменения могут носить хараткер оптимизации, например денормализация схемы.
Если меняется контракт, то это изменение поведения системы.
Даже если это сделано в угоду оптимизации.
Норма для микросервисов не общее хранилище операционных данных, а отдельный сервис или сервисы агрегации операционных данных основных сервисов по типу DWH. А история не является обязательным требованием и в целом к SOA или MSA ортогональна.
mad_nazgul, Вы, к сожалению, ошибаетесь. Если интересны подробности, то Sam Newman (коллега Мартина Фаулера по ThoughtWorks) отвечает на этот вопрос в своей книге “Building Microservices. Designing Fine-Grained Systems”.
сделать подобие LEFT JOIN между двумя сервисами
Пусть он почитает книгу «NoSQL Distilled. A Brief Guide to the Emerging World of Polyglot Persistence.» by Pramod J. Sadalage, Martin Fowler. Там описано как решаются такие проблемы в распределенных системах, хотя книга и не про микросервисы.
Господа, у вас волчанка классическая проблема с архитектурой:
— Вы очень странно понимаете концепцию микросервисов и принципы деления на микросервисы и ударились в классический антипаттерн, наносервисы. Хорошо описанный, кстати.
— Архитекторы явно бросились использовать концепцию, не читая вполне доступных материалов и без понимания, что такое микросервисная архитектура, какие задачи она решает, ее ограничений и когда ее применять. Решили, что это стильно-модно-молодежно.
— Еще в начале статьи становилось понятно, что архитектора не потянули задачу и не взлетит, независимо от наличия или отсутствия микросервисов.

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

Это же перевод, так что всё Ваше беспокойство/негодование не к тем обращено

От статьи создается впечатление, что проблема там была не с микросервисами, а как поддерживать 100500 версий сервиса. Для поддержки — это, конечно, ад. Решение было в области отказа от старых версий.

Можно ли было это сделать, оставаясь в микросервисах? Наверное, да. Вопрос только в том, какую логику обновления сервисов задействовать после изменения в общих зависимостях? Автосборка всех зависимых сервисов? Отказ от деплоя или частичный деплой только прошедших тесты, сервисов? И т.д.
Основаная проблема в этом:
Тестирование и развертывание изменений в этих общих библиотеках повлияло на все наши направления. Это начало требовать значительного времени и усилий для поддержки. Внесение изменений для улучшения наших библиотек, зная что нам придется тестировать и разворачивать десятки сервисов, было рискованным предложением.

ИМХО, в случае, такого большого кол-ва микросервисов лучше не иметь общих библиотек вообще (малейшая ошибка в общей библиотеке и лягут все микросервисы сразу).

Или они должны быть сильно ограничены и сигментированы (грубо говоря, на rest клиента одна библиотека, на работу с xml другая, не обязательно в разных репозиториях, но в разных jar'никах или в чем они делают выдачу).

К своим общим библиотекам лучше относится как к любой другой opensource библиотеки — если она не нужна, не включать, если есть opensource библиотека более подходящая — брать ее, а не свою общую библиотеку.

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


Никакого развивающегося проекта не бывает без рефакторинга. И, кстати, 90% встретившихся мне менеджеров не понимают, что такое Agile/SCRUM и как он работает.

Профнепригодные архитекторы, профнепригодные менеджеры, профнепригодные девелоперы. Куда деваются люди, со знанием — загадка.

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

Где остальные — неясно, похищают и топят ли их что-ли?
Возможно сидят и тихо работают, не пишут подобные статьи :-)
По моим наблюдениям мотивация рассказать о неудачном опыте сильно превышает таковую в случае успеха.
Профнепригодные архитекторы, профнепригодные менеджеры, профнепригодные девелоперы. Куда деваются люди, со знанием — загадка.


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

Если же менеджер не был программистом, то программистам придется выпрашивать (в буквальном смысле) время на рефакторинг/тесты/песочницы.
А я не соглашусь с вами, менеджер и программист- очень разные активности. Безусловно, менеджер должен понимать тех детали, но это верно для любых профессий.
Судя по зарубежной литературе, хороший менеджер — это мировая проблема :) Вон, старик Брукс аж бестселлер написал про пули и… про то, как он развалил проект ОС/360 :)
А вообще я думаю, что хорошего менеджера практически не должно быть заметно. И это его основная цель: команда должна работать эффективно что с ним, что без. Но чтобы этого достич, нужен хороший менеджер, такая фигня :)
Я немного криво выразился, с моей точки зрения — хороший менеджер получается из хорошего программиста. Но это не означает, что хороший программист станет хорошим менеджером. Быть хорошим прогером — это необходимое условие, но точно, абсолютно точно — не достаточное.
Уверен, что есть исключения, представляющие из собой просто отличных менеджеров, которые ничерта не смыслят в низкоуровневых слоях.

Я думаю, что большинство хороших прогеров не хотят перерастать в менеджеров.

Если же менеджер не был программистом, то программистам придется выпрашивать (в буквальном смысле) время на рефакторинг/тесты/песочницы.

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

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

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

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

Попробуйте обосновать рефакторинг. Например, отделение в бэкенде слоя доступа к данным (DAL)

На самом деле не надо ничего обосновывать, равно как не надо и доводить до состояния когда на рефакторинг надо выделять неделю.


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


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


Единственная проблема вышеописанного подхода — его желательно применять с первого дня разработки. Но и в ситуации, когда большая часть проекта уже написана можно начать применять этот подход хотя бы для нового кода, это заметно поможет, хотя и не решит проблему целиком, так что выпрашивать отдельное время на рефакторинг и тесты всё-равно придётся.

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

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

Менеджмент — он разный бывает, если что. Не стоит всех огульно хаять.
Никакого развивающегося проекта не бывает без рефакторинга. И, кстати, 90% встретившихся мне менеджеров не понимают, что такое Agile/SCRUM и как он работает.
Причины рефакторинга бывают разными. Бывают причины потому что в мастер-бранч мержится некачественный код. Т.е. в команде отсутствует Collaborative Development. В таком случае Agile никак не оправдывает низкое качество кода, и даже, более того, предписывает его не трогать, пока он не мешает и не влияет на экономику разработки. А бывает рефакторинг как элемент реализации Evolutionary Design и YAGNI. Тогда — да, это Agile. Но в таком случае, это не рефакторинг, а Evolutionary Design.
А почему типовое решение для очереди не взяли, какую-нибудь Кафку, например?
Кафка это не совсем очередь, даже совсем не очередь.
Но продукт почти гениальный в простоте своей концепции :-)
Пример для перевода не удачный. Люди не умеют готовить микросервисы

Не понятны два момента:


  1. Чего плохого в общих библиотеках? Они остаются общими, пока могут такими быть. Требования меняются — меняется и код. И не надо пытаться сохранить его, закостылив в "общей" библиотеке ради общности
  2. Вы сначала перешли на отдельные очереди с раздельным управлением, испытывая проблемы с отказоустойчивостью, а потом психанули и перешли на centrifuge, опять создав проблемы с отказоустойчовостью?
    Если я правильно понял, то проблемы у вас были две — микросервисы ради микросервисов и общие библиотеки ради общих библиотек
Это перевод, автор вам не ответит
Общие библиотеки, насколько я понимаю, ради DRY и единообразности, уменьшения порога входа в микросервис.
Они бы создали проблемы с отказоустойчивостью если бы перешли на общую очередь. Но centrifuge, судя по описанию, такой общей очередью не является.

Выше VolCh ответил зачем им понадобились общие библиотеки. Но суровая правда в том, что общих библиотек, действительно, быть не должно.


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


Проблема с общими библиотеками возникает из-за того, что, поскольку это "библиотеки проекта", они не пишутся настолько аккуратно, как опенсорсные, в них нет такого тщательного соблюдения semver и обратной совместимости API, они не являются настолько библиотеками общего назначения и в них проникает бизнес-логика этого проекта, и в следствие всего этого к ним предъявляется нетипичное требование "все микросервисы обязаны использовать последнюю версию этих библиотек". В результате общие библиотеки создают жёсткую связь между всеми микросервисами и появляется точка, в которой можно все микросервисы поломать, точка, обновление которой вызывает лавину перевыкатов всех микросервисов, точка, которая создаёт больше проблем, чем решает. Вот поэтому общих библиотек проекта у микросервисов быть не должно. А общие опенсорсные — без проблем, причём одна и та же библиотека может в разных микросервисах использоваться разной версии.

Вот поэтому общих библиотек проекта у микросервисов быть не должно.

Как минимум, имеют право на жизнь, библиотеки проекта/продукта, инкапсулирующие конкретный способ использование других микросервисов, например икапсулирующие HTTP-проткол.

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


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

Я кажется понимаю о чём вы, но я о другом. Я о библиотеках, реализующих клиентов к микросервису. Есть, например, сервис работы с пользователями, предоставляющий REST API, которым пользуются все остальные сервисы. Вместе с сервисом разрабытываем библиотеку, которая будет экспортировать классы User и UserRepository и прятать от остальных сервисов факт наличия REST API и вообще факт межпроцессного взаимодействия. Клиентский сервис работает с сервисом пользователей через эту библиотеку даже не подозревая о том, что там идут вызовы по сети. Цикл мажорных и минорных релизов библиотеки совпадает с циклом релизов сервиса пользователей.

Пока использование всеми сервисами этой библиотеки вообще, и только её последней версии в частности, не является обязательным — никаких проблем. Если сервисы предоставляют стабильное и документированное API, а библиотека существует исключительно для удобства разработчиков тех сервисов, которым лень напрямую вызывать REST API — всё в порядке. Как только библиотека начинает использоваться для сокрытия факта нестабильности и/или недокументированности API, что и приводит к требованию её обязательного использования причём только последней версии, вот тогда возникает та проблема, из-за которой "общих библиотек проекта быть не должно".

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

Цель подобных библиотек как раз уменьшения количества зависимого от API сервиса кода, чтобы при изменении API сервиса достаточно было обновить только библиотеку на клиентах (естественно, если API библиотеки не изменился), а не переписывать код самих клиентов. Ну и в целом она сама может являться документацией к сервису.
Вот именно от обратно несовместимого API сервисов и проблемы. Иногда это нужно, но если такое постоянно — значит, микросервисы используются как-то неправильно.
Общие библиотеки у проекта могут быть, но это не должно быть догмой.
Т.е. их использование не должно быть обязательным.
В противном случае — да получаем монолит в библиотеках.
Дублирование кода сильно хуже, чем монолит в библиотеках.
Нету дублирования кода.
Т.к. он разнесен по разным приложениям :-)
И в каждом конкретном приложении он уникален.
А то что есть похожие куски, которые делают почти одно и то же…
То да их можно вынести в библиотеки.
Но проектирование АПИ, не совсем тривиальная вещь. :-)
На моей практике организовать работу нескольких команд, которые в итоге выдают общие библиотеки, с semver, обратной совместимостью и прочим плюшками проще, чем решение инфраструктурных проблем с микросервисами. То есть, я рекомендую с библиотек начинать. Если не получается делать нормальные библиотеки, то о нормальных и стабильных интерфейсах между сервисами можно забыть.

Библиотеки не являются полноценной альтернативой микросервисам — в нормальных библиотеках обычно не должно быть своего логирования, своей БД… но вот встроенные микросервисы, особенно если они stateless, от библиотек отличить действительно довольно сложно.

Библиотеки могут реализовывать основную функциональность, а логирование и т. п. можно в них инжектить или делать врапперы вокруг них.
Библиотеки, имхо, вторым этапом должны быть. Первым — просто разбиение на модули/контексты, с жёстким контролем за соблюдение публичного контракта, например с активным использование DIP из SOLID в мейнстрим ООП языках, чтобы в клиентах модулей использовались только интерфейсы или абстрактные классы для взаимодействия с ними.
Только полноправные пользователи могут оставлять комментарии.
Войдите, пожалуйста.