Микросервис это не про размер. Просто, термин так сложился, чтобы от SOA отличать
Так в этом и вопрос. Вы сформировали микросервисную архитектуру, несмотря на то, что у вас есть некий микросервис - ядро. И тут же говорите, что микросервисы - не очень.
многие ставят знак равенства между "имеются (микро)сервисы" и "каждый домен в отдельном небольшом микросервисе, тысячи их!"
Ну так архитектура и там и там микросервисная. Что не пойму претензии. Это примерно как написать "многие ставят знак равенства между " имеется монолит на 50к строк и монолит на 1_000_000 строк".
И мне не нравится выражение "делить на МС". Скорее, отделять от монолита.
Все зависит от контекста. Мне приходилось и делить, и отделять.
Ну так даже если внутри архитектуры есть большой сервис с большой функциональностью, вокруг которого микросервисы - это уже микросервисная архитектура (исключим случаи распределенных монолитов). И в этом случае много минусов микросервисной архиьектуры уже присутствуют, поэтому, например, волноваться за появление еще одной единицы деплоя вряд ли стоит.
Но думать, нужен ли для функциональности отдельный МС- надо всегда.
Но именно эту мысль донести не получилось ни в этой статье, ни в предыдущей. Надо ли выделять МС с умом? Надо. Плох ли микросервисный подход от этого? Конечно, нет. У него свои плюсы и свои минусы, и при выделении нового микросервиса мы всегда должны определить - получим ли мы от этого больше, чем потеряем.
А у вас получилось "микросервисы не очень, и есть множество более грамотных подходов".
Ну так это про масштабируемость. А это явно не слабая сторона микроскрвисов.
Я же написал всë
Вы обосновали выбор микросервисной архитектуры. Почему же вы выбрали именно эту архитектуру, если настолько разочарованы в ней и есть множество решений лучше?
Хорошая статья, но есть "но": в начале вы жестко критикуете микросервисы, описываете ряд проблем, а потом внезапно первым решением предлагаете микросервисы. Вам не кажется это странным?
Я не имею ввиду, что пример плох, я имею ввиду, что он не коррелирует с начальным тезисом статьи "микросервисы вам не нужны". Нужны, получается, а все описанные минусы вы получите в предложенном вами же решении)
Другой вопрос, что делить на МС надо с умом. Но про это ни слова в начальном тезисе - там только минусы микросервисов. Даже без перечисления плюсов.
Если следовать нарисованной схеме, то не можем, нижний модуль знает о верхнем только интерфейс, который ему надо реализовать.
Он использует только интерфейс, а знает то обо всем модуле.
В том и дело что нет, берется только интерфейс. Задумка в том, что интерфейс меняется намного реже реализации.
Да задумка понятна. Только проблема в том, что далеко не каждый разработчик прочитает доку и поймет, что надо только интерфейсы брать из этого модуля. Еще меньше разработчиков будут проверять, в каком пакете находится класс, который они заинжектили. А еще часть просто насрëт на ваши архитектурные правила, потому что "надо как быстрее". В результате - изолированности нет.
Но так можно сказать и про исходную схему, а что мешает из нижнего модуля использовать верхний?
Ну вот в том и дело, что "дефолтная" трехслойная архитектура изолирует нижний слой от верхнего, но не наоборот. Потому что эти (нижние) слои должны быть более обособленными, более простыми и менее нагруженными бизнес правилами. При этом, если мы не хотим, что бы лишние классы "протекали" вовне - мы можем ограничить их видимость настройками приватности. Теоретически мы можем и в модуле с бизнес логикой ограничить видимость классов, но скорее всего тогда нам придется отказаться от структуры папок в модуле, из-за особенностей настроек приватности.
но даже в мартиновской схеме этого
Это действительно так. Если не ошибаюсь, то представленная в статье схема соответствует "чистой архитектуре". И да, мне это не понравилось и там. Есть решение лучше, если нам действительно необходимо изолировать модули (слои) друг от друга. Но оно потребует промежуточного общего модуля (слоя). Если же это решение избыточно для решаемой проблемы - скорее всего, и нет необходимости городить модули, просто заинжектите через интерфейс реализацию и радуйтесь.
Правильное разделение по слоям тоже важно, но это не относится к инверсии зависимостей.
Не относится. Но нарисованная схема ломает это разделение.
Как изменения в верхнем модуле могут по всему проекту расползтись?
Верхний модуль содержит ведь не только абстракцию для нижнего. В нем еще и своя логика. Которую мы теперь можем использовать в нижнем. Наша реализация теперь не только от абстракций зависит, но и от остальной кодовой базы.
А то, что изменение верхнего модуля расползается по всему проекту - это не важно? Ведь тут точно такая же проблема, но в обратную сторону.
Да, на словах - реализация зависит от верхнего модуля, хорошо. На деле - мы потеряли его (нижнего модуля) изолированность, и теперь ничто не мешает нам реализовывать логику, которая по нашему разделению должна быть в верхнем модуле, в нижнем. И может получиться так, что когда мы решим сменить модуль - реализацию, в нем окажется пачка классов, которая важна для нашей бизнес логики. Но их засунули в модуль с доступом к бд. Или модуль с интеграциями. Или любой другой, в которой бизнесовой логики быть не должно.
Ну у интерфейсы можно использовать не только ради нескольких реализаций, а еще (как минимум) для гибкости и изменяемости приложения.
Например, мы можем понимать, что реализацию в будущем придется поменять (или возможно придется), и интерфейс сразу нам в этом помогает.
Несколько реализаций - но тут скорее про паттерны типа стратегии, для сервисов это не лучший подход.
В импортируемых модулях (библиотеках) интерфейсы дают возможность переопределить реализацию у клиента.
Однозначно, я не упомянул все варианты использования. Но делать интерфейс ради интерфейса - это тоже плохая затея. Но и причин их использовать - не мало.
Код делится на слои для того, что бы его было легче поддерживать. Слои можно разделить по модулям и изолировать их. Этому нас учит стандартная слоистая архитектура.
Но появляется проблема, что код верхнего уровня жестко связан с кодом нижнего уровня. Например, если мы решим сменить принципиально используемое хранилище данных, это будет довольно трудно реализовать. Потому что слой сильно привязан к реализации.
И вот придумали решение- что бы не зависеть от реализации, давайте абстракции хранить в верхнем слое. Таким образом мы отвязали логику от реализации нижнего слоя. Но теперь нижний слой может напрямую вызывать нашу бизнес логику. А это - изначальная проблема. Имхо, в общем случае мы теряем больше, чем получаем.
Поэтому единственным хорошим решением является использовать третий модуль, содержащий только абстракции. Другой вопрос, что подобное разделение нужно далеко не всегда, и в большинстве случаев достаточно просто вызывать сервисы нижнего слоя через интерфейс.
Да. В общем, проблема в том, что на диаграмме в чистой архитектуре слой взаимодействия с бд почему то зависит от слоя с бизнес логикой, и находится на самом верхнем уровне.
"Почему то" не в том смысле, что я не понимаю причин и что Мартин хотел этим изобразить. А в том, что в слое, взаимодействующем с БД нам архитектурно ничто не мешает использовать кодовую базу всей нашей бизнес логики. А это как раз то, от чего нас предостерегает стандартная слоистая архитектура.
Между слоем модулями нижних и верхних уровней должен быть отдельный модуль с абстракциями, только в этом случае мы сможем сохранить изолированность слоев и соблюсти принцип DI.
В вашем примере вы не зависите от конкретной реализации, это так. Но если вы применили архитектуру, указанную в статье в качестве DI, реализация такси будет странным образом зависеть от вас. То есть это прям ваш таксопарк будет. Настолько, что в нем еще и диспетчера не будет - если надо будет перевезти кого то еще, этот "кто то" позвонит вам, что бы такси вызвать. При это будет думать, что звонит в такси.
Почему никого ге смущает, что модуль нижнего уровня зависит от модуля верхнего уровня? Зачем ему это? Вы разделяете логику по слоям, но при этом модуль, который должен условно взаимодействовать с бд и ничего больше, потенциально может стучаться в модуль с бизнес логикой. Т. е. прямая зависимость верхнего модуля от нижнего - это плохо, а прямая зависимость нижнего модуля (который, на секунду, должен только вызываться в вернем, и только для этого мы делаем весь этот финт ушами) - это нормально.
Да, подменить реализацию мы сможем, а вот изолировать ее от верхнего модуля - нет. А какой тогда вообще смысл делить на модули и слои, если изначально эти слои именно невозможность вызова из нижнего модуля верхний?
Имхо - если мы оставляем интерфейс в верхнем модуле - скорее всего, нет смысла выносить логику в другой модуль приложения. Если же мы хотим реализовать реальную независимость модулей - надо делать третий модуль, содержащий дто и интерфейсы сервисов, и через него настраивать межмодульное взаимодействие.
А как вам минусы за то, что "не узнал ничего нового"? У меня таких парочка есть, и я прям представляю, как кто то ходит и минусует статьи, потому что уже это знает)
Кстати, если много и минусов и плюсов - это тоже хороший знак, значит, в статье затронута актуальная и неоднозначная тема.
Был у меня на работе тест, который падал в выходные дни (там что то с проверкой рабочих графиков). Это был прекрасный тест, который не позволял партизанить по выходным)
Плюсом если вдруг когда то конечная точка изменится - поправить тест и убедиться, что все ок - минутное дело.
Вы можете это все проверить, например, через постмана, но это не всегда быстрее и так же чревато "ошибками невнимательности". А можете вообще отдать функционал в тестирование без проверки, что, по моему мнению, не профессионально.
Я не призываю читателей тестировать весь код. И не утверждаю, что это "правильно" или "не правильно". Я просто написал те выводы, которые на данный момент извлек из своего опыта. Не надо представлять это "сектой".
И по поводу "почему автор как достижение говорит про вот такие вещи, что стало меньше ошибок по невнимательности " - хоть я и не пытался преподнести это как "достижение", а лишь указал один из плюсов тестирования, однако: а разве это не достижение?)
Так в этом и вопрос. Вы сформировали микросервисную архитектуру, несмотря на то, что у вас есть некий микросервис - ядро. И тут же говорите, что микросервисы - не очень.
Ну так архитектура и там и там микросервисная. Что не пойму претензии. Это примерно как написать "многие ставят знак равенства между " имеется монолит на 50к строк и монолит на 1_000_000 строк".
Все зависит от контекста. Мне приходилось и делить, и отделять.
Дак вы сами привели пример микросервисной архитектуры в своей же статье. Почему не выбрали другой подход, если есть лучше?
Ну так даже если внутри архитектуры есть большой сервис с большой функциональностью, вокруг которого микросервисы - это уже микросервисная архитектура (исключим случаи распределенных монолитов). И в этом случае много минусов микросервисной архиьектуры уже присутствуют, поэтому, например, волноваться за появление еще одной единицы деплоя вряд ли стоит.
Но думать, нужен ли для функциональности отдельный МС- надо всегда.
Но именно эту мысль донести не получилось ни в этой статье, ни в предыдущей. Надо ли выделять МС с умом? Надо. Плох ли микросервисный подход от этого? Конечно, нет. У него свои плюсы и свои минусы, и при выделении нового микросервиса мы всегда должны определить - получим ли мы от этого больше, чем потеряем.
А у вас получилось "микросервисы не очень, и есть множество более грамотных подходов".
Ну так это про масштабируемость. А это явно не слабая сторона микроскрвисов.
Вы обосновали выбор микросервисной архитектуры. Почему же вы выбрали именно эту архитектуру, если настолько разочарованы в ней и есть множество решений лучше?
По моему, в плюсах МС никогда не было "повышения быстродействия". Наоборот, всегда пишется о задержке сети.
Как вы можете писать, что "микросервисы переоценены", если сами в предыдущей статье предложили решение на микросервисах?
Хорошая статья, но есть "но": в начале вы жестко критикуете микросервисы, описываете ряд проблем, а потом внезапно первым решением предлагаете микросервисы. Вам не кажется это странным?
Я не имею ввиду, что пример плох, я имею ввиду, что он не коррелирует с начальным тезисом статьи "микросервисы вам не нужны". Нужны, получается, а все описанные минусы вы получите в предложенном вами же решении)
Другой вопрос, что делить на МС надо с умом. Но про это ни слова в начальном тезисе - там только минусы микросервисов. Даже без перечисления плюсов.
Думаю, мы пришли к единому мнению)
Он использует только интерфейс, а знает то обо всем модуле.
Да задумка понятна. Только проблема в том, что далеко не каждый разработчик прочитает доку и поймет, что надо только интерфейсы брать из этого модуля. Еще меньше разработчиков будут проверять, в каком пакете находится класс, который они заинжектили. А еще часть просто насрëт на ваши архитектурные правила, потому что "надо как быстрее". В результате - изолированности нет.
Ну вот в том и дело, что "дефолтная" трехслойная архитектура изолирует нижний слой от верхнего, но не наоборот. Потому что эти (нижние) слои должны быть более обособленными, более простыми и менее нагруженными бизнес правилами. При этом, если мы не хотим, что бы лишние классы "протекали" вовне - мы можем ограничить их видимость настройками приватности. Теоретически мы можем и в модуле с бизнес логикой ограничить видимость классов, но скорее всего тогда нам придется отказаться от структуры папок в модуле, из-за особенностей настроек приватности.
Это действительно так. Если не ошибаюсь, то представленная в статье схема соответствует "чистой архитектуре". И да, мне это не понравилось и там. Есть решение лучше, если нам действительно необходимо изолировать модули (слои) друг от друга. Но оно потребует промежуточного общего модуля (слоя). Если же это решение избыточно для решаемой проблемы - скорее всего, и нет необходимости городить модули, просто заинжектите через интерфейс реализацию и радуйтесь.
Не относится. Но нарисованная схема ломает это разделение.
Верхний модуль содержит ведь не только абстракцию для нижнего. В нем еще и своя логика. Которую мы теперь можем использовать в нижнем. Наша реализация теперь не только от абстракций зависит, но и от остальной кодовой базы.
А то, что изменение верхнего модуля расползается по всему проекту - это не важно? Ведь тут точно такая же проблема, но в обратную сторону.
Да, на словах - реализация зависит от верхнего модуля, хорошо. На деле - мы потеряли его (нижнего модуля) изолированность, и теперь ничто не мешает нам реализовывать логику, которая по нашему разделению должна быть в верхнем модуле, в нижнем. И может получиться так, что когда мы решим сменить модуль - реализацию, в нем окажется пачка классов, которая важна для нашей бизнес логики. Но их засунули в модуль с доступом к бд. Или модуль с интеграциями. Или любой другой, в которой бизнесовой логики быть не должно.
Ну у интерфейсы можно использовать не только ради нескольких реализаций, а еще (как минимум) для гибкости и изменяемости приложения.
Например, мы можем понимать, что реализацию в будущем придется поменять (или возможно придется), и интерфейс сразу нам в этом помогает.
Несколько реализаций - но тут скорее про паттерны типа стратегии, для сервисов это не лучший подход.
В импортируемых модулях (библиотеках) интерфейсы дают возможность переопределить реализацию у клиента.
Однозначно, я не упомянул все варианты использования. Но делать интерфейс ради интерфейса - это тоже плохая затея. Но и причин их использовать - не мало.
Код делится на слои для того, что бы его было легче поддерживать. Слои можно разделить по модулям и изолировать их. Этому нас учит стандартная слоистая архитектура.
Но появляется проблема, что код верхнего уровня жестко связан с кодом нижнего уровня. Например, если мы решим сменить принципиально используемое хранилище данных, это будет довольно трудно реализовать. Потому что слой сильно привязан к реализации.
И вот придумали решение- что бы не зависеть от реализации, давайте абстракции хранить в верхнем слое. Таким образом мы отвязали логику от реализации нижнего слоя. Но теперь нижний слой может напрямую вызывать нашу бизнес логику. А это - изначальная проблема. Имхо, в общем случае мы теряем больше, чем получаем.
Поэтому единственным хорошим решением является использовать третий модуль, содержащий только абстракции. Другой вопрос, что подобное разделение нужно далеко не всегда, и в большинстве случаев достаточно просто вызывать сервисы нижнего слоя через интерфейс.
Ну вот и получается, что у вас интерфейс в отдельном слое, а не в вашей квартире (модуле).
Только это не соответствует диаграмме в статье.
Да. В общем, проблема в том, что на диаграмме в чистой архитектуре слой взаимодействия с бд почему то зависит от слоя с бизнес логикой, и находится на самом верхнем уровне.
"Почему то" не в том смысле, что я не понимаю причин и что Мартин хотел этим изобразить. А в том, что в слое, взаимодействующем с БД нам архитектурно ничто не мешает использовать кодовую базу всей нашей бизнес логики. А это как раз то, от чего нас предостерегает стандартная слоистая архитектура.
Между слоем модулями нижних и верхних уровней должен быть отдельный модуль с абстракциями, только в этом случае мы сможем сохранить изолированность слоев и соблюсти принцип DI.
В вашем примере вы не зависите от конкретной реализации, это так. Но если вы применили архитектуру, указанную в статье в качестве DI, реализация такси будет странным образом зависеть от вас. То есть это прям ваш таксопарк будет. Настолько, что в нем еще и диспетчера не будет - если надо будет перевезти кого то еще, этот "кто то" позвонит вам, что бы такси вызвать. При это будет думать, что звонит в такси.
Почему никого ге смущает, что модуль нижнего уровня зависит от модуля верхнего уровня? Зачем ему это? Вы разделяете логику по слоям, но при этом модуль, который должен условно взаимодействовать с бд и ничего больше, потенциально может стучаться в модуль с бизнес логикой. Т. е. прямая зависимость верхнего модуля от нижнего - это плохо, а прямая зависимость нижнего модуля (который, на секунду, должен только вызываться в вернем, и только для этого мы делаем весь этот финт ушами) - это нормально.
Да, подменить реализацию мы сможем, а вот изолировать ее от верхнего модуля - нет. А какой тогда вообще смысл делить на модули и слои, если изначально эти слои именно невозможность вызова из нижнего модуля верхний?
Имхо - если мы оставляем интерфейс в верхнем модуле - скорее всего, нет смысла выносить логику в другой модуль приложения. Если же мы хотим реализовать реальную независимость модулей - надо делать третий модуль, содержащий дто и интерфейсы сервисов, и через него настраивать межмодульное взаимодействие.
А как вам минусы за то, что "не узнал ничего нового"? У меня таких парочка есть, и я прям представляю, как кто то ходит и минусует статьи, потому что уже это знает)
Кстати, если много и минусов и плюсов - это тоже хороший знак, значит, в статье затронута актуальная и неоднозначная тема.
Был у меня на работе тест, который падал в выходные дни (там что то с проверкой рабочих графиков). Это был прекрасный тест, который не позволял партизанить по выходным)
Ну смотрите.
Написать тест на конечную точку с использованием wire mock стоит 5-10 минут рабочего времени.
Взамен мы проверяем:
Корректность метода.
Корректность самого урла.
Корректность парсинга переменных пути/хедеров/параметров запроса.
Корректность десериализации тела запроса.
Плюсом если вдруг когда то конечная точка изменится - поправить тест и убедиться, что все ок - минутное дело.
Вы можете это все проверить, например, через постмана, но это не всегда быстрее и так же чревато "ошибками невнимательности". А можете вообще отдать функционал в тестирование без проверки, что, по моему мнению, не профессионально.
Я не призываю читателей тестировать весь код. И не утверждаю, что это "правильно" или "не правильно". Я просто написал те выводы, которые на данный момент извлек из своего опыта. Не надо представлять это "сектой".
И по поводу "почему автор как достижение говорит про вот такие вещи, что стало меньше ошибок по невнимательности " - хоть я и не пытался преподнести это как "достижение", а лишь указал один из плюсов тестирования, однако: а разве это не достижение?)