Всем привет!
Хочу продолжить серию постов про #microservices
Я уже писал про их плюсы и минусы. Одним из главных минусов является увеличенная сложность развертывания и поддержки, а также накладные расходы на сетевое взаимодействие. Как ответ на эту сложность может возникнуть идея - а давайте сделаем несколько независимых модулей в одном микросервисе, а потом при необходимости их разделим. Ключевое слово здесь - независимый. Идея на самом деле здравая. "Модулем" здесь может быть модуль Maven\Gradle или даже пакет. Но есть одна проблема: если не следить за связями между "модулями" - они со временем становятся связанными и получается тот самый спагетти код))) А тогда выделение нового микросервиса превратится в распутывание клубка зависимостей. Значит нужна проверка границ "модулей". Лучший способ сделать надежную постоянно выполняемую проверку - это написать unit тест. И запускать его на prcheck и сборке конечно же. Но любой тест должен быть антихрупким - т.е. при изменениях в коде оставаться актуальным. В нашем случае - в случае добавлении\изменении\удалении "модулей" в проекте.
К чему я веду: есть технология, решающая эту проблему - Spring Modulith https://spring.io/projects/spring-modulith
А вот статья, описывающая предпосылки его появления более подробно и способ его использования: https://habr.com/ru/articles/701984/
Мне нравится.
Зависимость от Spring на мой взгляд не является большим минусом. Требование объявить все пакеты в одном модулей Maven\Gradle - минус чуть пожирнее, но на мой взгляд тоже не критично. И сборка в этом случае будет быстрее.
#spring #microservices
Хочу продолжить серию постов про #microservices
Я уже писал про их плюсы и минусы. Одним из главных минусов является увеличенная сложность развертывания и поддержки, а также накладные расходы на сетевое взаимодействие. Как ответ на эту сложность может возникнуть идея - а давайте сделаем несколько независимых модулей в одном микросервисе, а потом при необходимости их разделим. Ключевое слово здесь - независимый. Идея на самом деле здравая. "Модулем" здесь может быть модуль Maven\Gradle или даже пакет. Но есть одна проблема: если не следить за связями между "модулями" - они со временем становятся связанными и получается тот самый спагетти код))) А тогда выделение нового микросервиса превратится в распутывание клубка зависимостей. Значит нужна проверка границ "модулей". Лучший способ сделать надежную постоянно выполняемую проверку - это написать unit тест. И запускать его на prcheck и сборке конечно же. Но любой тест должен быть антихрупким - т.е. при изменениях в коде оставаться актуальным. В нашем случае - в случае добавлении\изменении\удалении "модулей" в проекте.
К чему я веду: есть технология, решающая эту проблему - Spring Modulith https://spring.io/projects/spring-modulith
А вот статья, описывающая предпосылки его появления более подробно и способ его использования: https://habr.com/ru/articles/701984/
Мне нравится.
Зависимость от Spring на мой взгляд не является большим минусом. Требование объявить все пакеты в одном модулей Maven\Gradle - минус чуть пожирнее, но на мой взгляд тоже не критично. И сборка в этом случае будет быстрее.
#spring #microservices
Spring Modulith
Level up your Java code and explore what Spring can do for you.
Всем привет!
Продолжая тему рефакторинга. Основное предусловие для начала рефакторинга - это наличие хорошего тестового покрытия. Т.к. мы не меняем бизнес функционал, а улучшаем код либо для повышения производительности, либо для его упрощения. Но при этом нужно гарантировать, что бизнес функционал не сломался, т.е. не появились регрессионные баги. Ведь рефакторинг, в отличие от новой фичи, может затронуть все приложение. Соответственно, баги могут появиться в любом месте, и без тестового покрытия - это большие риски.
Можно рассмотреть похожий кейс. У нас есть монолит, мы хотим распилить его на микросервисы. Это тоже своего рода рефакторинг, только на уровне архитектурном уровне. И тоже аналогичное условие для его начала - наличие достаточного набора тестов. В данном случае повышается важность интеграционных тестов.
Важный момент: в процессе подготовки к разбиению монолита или серьёзному рефакторингу может возникнуть вопрос-предложение - а давайте все выкинем и напишем заново. Так вот - одним из базовых критериев для ответа на этот вопрос также является покрытие тестами. Очевидно, не единственным, но важным. Другие критерии - объем техдолга, соответствие текущей архитектуры и целевой.
Еще кейс - разработчик «боится» рефакторить код, т.к. он слишком сложный или затрагивает слишком много зависимостей. С тестами решиться на рефакторинг намного проще.
Вывод простой - пишите тесты, это страховка при рефакторинге)
#refactoring #unittests #microservices
Продолжая тему рефакторинга. Основное предусловие для начала рефакторинга - это наличие хорошего тестового покрытия. Т.к. мы не меняем бизнес функционал, а улучшаем код либо для повышения производительности, либо для его упрощения. Но при этом нужно гарантировать, что бизнес функционал не сломался, т.е. не появились регрессионные баги. Ведь рефакторинг, в отличие от новой фичи, может затронуть все приложение. Соответственно, баги могут появиться в любом месте, и без тестового покрытия - это большие риски.
Можно рассмотреть похожий кейс. У нас есть монолит, мы хотим распилить его на микросервисы. Это тоже своего рода рефакторинг, только на уровне архитектурном уровне. И тоже аналогичное условие для его начала - наличие достаточного набора тестов. В данном случае повышается важность интеграционных тестов.
Важный момент: в процессе подготовки к разбиению монолита или серьёзному рефакторингу может возникнуть вопрос-предложение - а давайте все выкинем и напишем заново. Так вот - одним из базовых критериев для ответа на этот вопрос также является покрытие тестами. Очевидно, не единственным, но важным. Другие критерии - объем техдолга, соответствие текущей архитектуры и целевой.
Еще кейс - разработчик «боится» рефакторить код, т.к. он слишком сложный или затрагивает слишком много зависимостей. С тестами решиться на рефакторинг намного проще.
Вывод простой - пишите тесты, это страховка при рефакторинге)
#refactoring #unittests #microservices
Всем привет!
Сегодняшний пост начну издалека. Распределенные системы обмениваются сообщениями. Каждое сообщение можно рассматривать как событие, требующее какой-то обработки и передачи дальше - в другую подобную систему или для хранения в БД. Т.об. мы получаем распределенную цепочку микросервисов, через которые проходит событие. Существуют т.наз. семантики доставки сообщений:
- at-most-once
- at-least-once
- exactly-once
at-most-once - максимум один раз, т.е. 0 или 1. Т.е. есть вероятность потерять и не обработать сообщение. Подходит для случаев, когда поток сообщений большой, используется для сбора статистики и потеря одного сообщения не критична. Например - статистика по кликам на сайте.
at-least-once - минимум один раз, т.е. 1 или более. Т.е. есть вероятность повторно отправить какое-то сообщение и, соответственно, обработать его дважды. Подходит для систем, где важно знать последнее значение. Пример: стоимость акции на сайте брокера. Или для систем, идемпотентно обрабатывающих входящие сообщения - https://habr.com/ru/companies/domclick/articles/779872/
exactly-once - строго один раз. Идеальный случай.
Да, система может поддерживать сразу несколько семантик, т.е. по сути иметь несколько режимов работы.
Самый интересный и сложный - это конечно exactly-once. Как с ним обстоят дела?
Например, его поддерживает Kafka - самая распространённая система потоковой передачи сообщений - https://docs.confluent.io/kafka/design/delivery-semantics.html
А также самые распространенные системы потоковой обработки данных:
Kafka Streams - https://kafka.apache.org/33/documentation/streams/core-concepts
Flink - https://flink.apache.org/2018/02/28/an-overview-of-end-to-end-exactly-once-processing-in-apache-flink-with-apache-kafka-too/
Spark - https://spark.apache.org/docs/latest/structured-streaming-programming-guide.html
Кажется, что все хорошо. Но не совсем)
Если прочитать внимательнее, что пишут, например, создатели Kafka, то выяснится что exactly-once гарантируется на участке Producer - Kafka, но далее все зависит от того, как организована работа в Consumer-е. Вот неплохая переводная статья на эту тему от одного из создателей Kafka: https://habr.com/ru/companies/badoo/articles/333046/ // в статье detected american style самореклама, но все равно она неплохая)))
Создатели Flink тоже говорят, что
а) мы даем механизм для exactly-once - в случае сбоя мы откатим ваш обрабатывающий процесс на конкретное состояние (checkpoint), и вы четко будете знать, что это за состояние - его метку времени, что было до и после него, но
б) что делать уже обработанными записями, находящимися после восстановленного состояния - разбирайтесь сами
в) возможность вернуться к сообщениям, на которые указывает checkpoint - тоже на стороне разработчика. В случае Kafka это чаще всего элементарно - сообщения не удаляются сразу после чтения из топика, а если это MQ или сетевой сокет...
г) а еще можно рассмотреть кейс, если кто-то обновил хранилище, где хранится состояние
д) или если в функции потоковой обработки используются какие-то внешние вызовы, которые сломаются на повторах после отката...
Но по большому счету это частности. Основная проблема - системы типа Kafka или Flink могут обеспечить exactly-once на какой-то небольшой части вашей микросервисной системы. Как обеспечить ее на всей системе - в любом случае задача архитектора и разработчика.
Подсказка: наличие operationId (traceId), идемпотентность, транзакции там где это возможно, докаты и наконец админка для ручного разбора инцидентов если не помогли все предыдущие варианты.
#streaming #kafka #flink #arch #microservices #exactly_once
Сегодняшний пост начну издалека. Распределенные системы обмениваются сообщениями. Каждое сообщение можно рассматривать как событие, требующее какой-то обработки и передачи дальше - в другую подобную систему или для хранения в БД. Т.об. мы получаем распределенную цепочку микросервисов, через которые проходит событие. Существуют т.наз. семантики доставки сообщений:
- at-most-once
- at-least-once
- exactly-once
at-most-once - максимум один раз, т.е. 0 или 1. Т.е. есть вероятность потерять и не обработать сообщение. Подходит для случаев, когда поток сообщений большой, используется для сбора статистики и потеря одного сообщения не критична. Например - статистика по кликам на сайте.
at-least-once - минимум один раз, т.е. 1 или более. Т.е. есть вероятность повторно отправить какое-то сообщение и, соответственно, обработать его дважды. Подходит для систем, где важно знать последнее значение. Пример: стоимость акции на сайте брокера. Или для систем, идемпотентно обрабатывающих входящие сообщения - https://habr.com/ru/companies/domclick/articles/779872/
exactly-once - строго один раз. Идеальный случай.
Да, система может поддерживать сразу несколько семантик, т.е. по сути иметь несколько режимов работы.
Самый интересный и сложный - это конечно exactly-once. Как с ним обстоят дела?
Например, его поддерживает Kafka - самая распространённая система потоковой передачи сообщений - https://docs.confluent.io/kafka/design/delivery-semantics.html
А также самые распространенные системы потоковой обработки данных:
Kafka Streams - https://kafka.apache.org/33/documentation/streams/core-concepts
Flink - https://flink.apache.org/2018/02/28/an-overview-of-end-to-end-exactly-once-processing-in-apache-flink-with-apache-kafka-too/
Spark - https://spark.apache.org/docs/latest/structured-streaming-programming-guide.html
Кажется, что все хорошо. Но не совсем)
Если прочитать внимательнее, что пишут, например, создатели Kafka, то выяснится что exactly-once гарантируется на участке Producer - Kafka, но далее все зависит от того, как организована работа в Consumer-е. Вот неплохая переводная статья на эту тему от одного из создателей Kafka: https://habr.com/ru/companies/badoo/articles/333046/ // в статье detected american style самореклама, но все равно она неплохая)))
Создатели Flink тоже говорят, что
а) мы даем механизм для exactly-once - в случае сбоя мы откатим ваш обрабатывающий процесс на конкретное состояние (checkpoint), и вы четко будете знать, что это за состояние - его метку времени, что было до и после него, но
б) что делать уже обработанными записями, находящимися после восстановленного состояния - разбирайтесь сами
в) возможность вернуться к сообщениям, на которые указывает checkpoint - тоже на стороне разработчика. В случае Kafka это чаще всего элементарно - сообщения не удаляются сразу после чтения из топика, а если это MQ или сетевой сокет...
г) а еще можно рассмотреть кейс, если кто-то обновил хранилище, где хранится состояние
д) или если в функции потоковой обработки используются какие-то внешние вызовы, которые сломаются на повторах после отката...
Но по большому счету это частности. Основная проблема - системы типа Kafka или Flink могут обеспечить exactly-once на какой-то небольшой части вашей микросервисной системы. Как обеспечить ее на всей системе - в любом случае задача архитектора и разработчика.
Подсказка: наличие operationId (traceId), идемпотентность, транзакции там где это возможно, докаты и наконец админка для ручного разбора инцидентов если не помогли все предыдущие варианты.
#streaming #kafka #flink #arch #microservices #exactly_once
Хабр
Идемпотентность: больше, чем кажется
Друзья, всем привет! Идемпотентность в проектировании API — не просто формальность. Это свойство, часто рассматриваемое как способ получения одинакового ответа на повторяющийся запрос, на самом деле...
Всем привет!
Микросервисная архитектура, которая становится все более популярной, требует хранения кода каждого сервиса в отдельном репозитории. На всякий пожарный - про остальные требования к микросервисам можно почитать тут https://t.me/javaKotlinDevOps/55
Бонусом мы получаем "штатную" работы IDE, pipeline, механизмов код-ревью. Микросервис подразумевает небольшой объём кода, т.е. все инструменты будут отрабатывать достаточно быстро, с меньшей вероятностью тормозить или падать, меньше вероятность конфликтов merge и т.д.
Зачем же тогда могут понадобится гигантские монорепы?
А ведь примеров хватает:
Яндекс https://habr.com/ru/companies/yandex/articles/469021/
Гугл https://qeunit.com/blog/how-google-does-monorepo/
Microsoft Windows https://habr.com/ru/articles/795635/
И даже Юла: https://habr.com/ru/companies/oleg-bunin/articles/531632/
Отдельно я бы выделил три типовых кейса, не относящиеся к микросервисам:
1) собственно монолит. И причина понятна - странно части монолита собирать из разных репозиториев. Релизный цикл, pipeline все равно же общие
2) игры. Тоже по сути отдельный вид монолита - огромные размеры репо, особенность: основной объем занимают бинарные данные: текстуры, картинки, видео...
3) большие мобильные приложения, SuperApp или стремящиеся к ним. Тоже по сути отдельный случай монолита.
Но в списке выше есть и микросервисы, зачем им лезть в монорепы? Суммируя, можно выделить такие причины:
1) проще контроль над своевременностью обновления библиотек
2) возможность одним PR занести кросс-сервисную фичу. Кстати, полезная штука
3) обучение новичков и обмен знаниями на примерах из других сервисов. Кажется, что это можно сделать и с разными репо, но в монорепе, конечно, удобнее
4) облегчение работы с общим кодом - не надо подключать новые модули явно в каждый сервис, просто работаем с тем, что есть в develop. Опять же все ошибки при выпуске новой версии общего кода ловятся быстрее, т.к. все тесты в одном месте. Конечно тесты, а точнее достаточное тестовое покрытие, должно быть) Рефакторинг, застрагивающий все части системы, как правило связанный с изменением общего API, также проводить проще
5) легче обеспечить унификацию требований к коду. Тоже - можно решить на уровне pipeline и commit hook, но в одном репо проще
6) т.к. модули в монорепо скорее всего работают вместе, а иначе зачем их код хранится вместе - с монорепо можно развернуть тестовый стенд проще, чем с отдельными микросервисами
Что касается минусов - про то как их побороть, можно глянуть статьи выше. Особенно полезна статья про Microsoft, содержащая технические советы по обслуживанию репо на уровне git-а. Часть я знал и ранее, и даже использовал, часть для меня стало сюрпризом. Что особенно важно - все доработки из своего форка git Microsoft портировала в master git. Вот краткий список советов:
1) sparse checkout - выкачивание только нужных папок
2) shallow copy - выкачивание ограниченного числа версии из истории изменений, как правило, только последней
3) клонирование без blob (--filter=blob: none) В blob хранится содержимое файлов. Ясно, что без содержимого файлов разработка невозможна, но в этом режиме git во-первых подкачивает blob для последней версии, а во-вторых - будет подкачивать необходимые ему файлы по требованию
4) также может быть полезно клонирование без checkout, опция --no-checkout, т.е. без копирования собственно файлов в рабочий каталог. Так мы сможем избежать случайного копирования всех файлов до того, как будет проведена фильтрация
5) запуск git maintenance в фоне - обновление графа коммитов, ускоряет выполнение некоторых команд git
6) фоновый монитор файловой системы, настройка git config core.fsmonitor true. Слушает уведомления об изменений файлов в каталоге с исходниками, поэтому git status отрабатывает быстрее.
Более того, в состав Git есть отдельная утилита scalar, из коробки делающая многое из перечисленных выше пунктов.
Итоги: если можно обойтись без монорепо - так и нужно делать. Но если все же решили его использовать - боль можно облегчить)
#git #microservices #monorepo
Микросервисная архитектура, которая становится все более популярной, требует хранения кода каждого сервиса в отдельном репозитории. На всякий пожарный - про остальные требования к микросервисам можно почитать тут https://t.me/javaKotlinDevOps/55
Бонусом мы получаем "штатную" работы IDE, pipeline, механизмов код-ревью. Микросервис подразумевает небольшой объём кода, т.е. все инструменты будут отрабатывать достаточно быстро, с меньшей вероятностью тормозить или падать, меньше вероятность конфликтов merge и т.д.
Зачем же тогда могут понадобится гигантские монорепы?
А ведь примеров хватает:
Яндекс https://habr.com/ru/companies/yandex/articles/469021/
Гугл https://qeunit.com/blog/how-google-does-monorepo/
Microsoft Windows https://habr.com/ru/articles/795635/
И даже Юла: https://habr.com/ru/companies/oleg-bunin/articles/531632/
Отдельно я бы выделил три типовых кейса, не относящиеся к микросервисам:
1) собственно монолит. И причина понятна - странно части монолита собирать из разных репозиториев. Релизный цикл, pipeline все равно же общие
2) игры. Тоже по сути отдельный вид монолита - огромные размеры репо, особенность: основной объем занимают бинарные данные: текстуры, картинки, видео...
3) большие мобильные приложения, SuperApp или стремящиеся к ним. Тоже по сути отдельный случай монолита.
Но в списке выше есть и микросервисы, зачем им лезть в монорепы? Суммируя, можно выделить такие причины:
1) проще контроль над своевременностью обновления библиотек
2) возможность одним PR занести кросс-сервисную фичу. Кстати, полезная штука
3) обучение новичков и обмен знаниями на примерах из других сервисов. Кажется, что это можно сделать и с разными репо, но в монорепе, конечно, удобнее
4) облегчение работы с общим кодом - не надо подключать новые модули явно в каждый сервис, просто работаем с тем, что есть в develop. Опять же все ошибки при выпуске новой версии общего кода ловятся быстрее, т.к. все тесты в одном месте. Конечно тесты, а точнее достаточное тестовое покрытие, должно быть) Рефакторинг, застрагивающий все части системы, как правило связанный с изменением общего API, также проводить проще
5) легче обеспечить унификацию требований к коду. Тоже - можно решить на уровне pipeline и commit hook, но в одном репо проще
6) т.к. модули в монорепо скорее всего работают вместе, а иначе зачем их код хранится вместе - с монорепо можно развернуть тестовый стенд проще, чем с отдельными микросервисами
Что касается минусов - про то как их побороть, можно глянуть статьи выше. Особенно полезна статья про Microsoft, содержащая технические советы по обслуживанию репо на уровне git-а. Часть я знал и ранее, и даже использовал, часть для меня стало сюрпризом. Что особенно важно - все доработки из своего форка git Microsoft портировала в master git. Вот краткий список советов:
1) sparse checkout - выкачивание только нужных папок
2) shallow copy - выкачивание ограниченного числа версии из истории изменений, как правило, только последней
3) клонирование без blob (--filter=blob: none) В blob хранится содержимое файлов. Ясно, что без содержимого файлов разработка невозможна, но в этом режиме git во-первых подкачивает blob для последней версии, а во-вторых - будет подкачивать необходимые ему файлы по требованию
4) также может быть полезно клонирование без checkout, опция --no-checkout, т.е. без копирования собственно файлов в рабочий каталог. Так мы сможем избежать случайного копирования всех файлов до того, как будет проведена фильтрация
5) запуск git maintenance в фоне - обновление графа коммитов, ускоряет выполнение некоторых команд git
6) фоновый монитор файловой системы, настройка git config core.fsmonitor true. Слушает уведомления об изменений файлов в каталоге с исходниками, поэтому git status отрабатывает быстрее.
Более того, в состав Git есть отдельная утилита scalar, из коробки делающая многое из перечисленных выше пунктов.
Итоги: если можно обойтись без монорепо - так и нужно делать. Но если все же решили его использовать - боль можно облегчить)
#git #microservices #monorepo
Telegram
(java || kotlin) && devOps
Всем привет!
Поговорим про микросервисы. Статей будет несколько, от плюсов и минусов до саги и прочих паттернов.
Для начала что такое микросервис. Я дам определение в виде набора обязательных признаков. Обязательны все из них.
1) одна команда
2) отдельная…
Поговорим про микросервисы. Статей будет несколько, от плюсов и минусов до саги и прочих паттернов.
Для начала что такое микросервис. Я дам определение в виде набора обязательных признаков. Обязательны все из них.
1) одна команда
2) отдельная…
Всем привет!
Сегодня будет пост про паттерн Saga.
Saga - это способ осуществить распределённую транзакцию. Обычная транзакция осуществляется в рамках одной сущности, как правило базы данных. Распределённая - между несколькими. Проблема здесь в том, что для одной системы - реляционной БД или кластера Kafka - можно воспользоваться встроенным механизмом транзакций, для распределённой - нет.
В общем случае распределённые транзакции могут понадобиться и для операций между несколькими монолитными приложениями, но наиболее актуальны они стали при переходе на микросервисы, т.к. при этом расширились границы существующих бизнес транзакций.
Возможно кто-то слышал в применении к распределённым транзакциям и Java такие аббревиатуры как XA или JTA. Это стандарт Java EE (Jakarta EE) для осуществления распределённых транзакций между JDBC и JMS источниками. Существует давно, есть работающие реализации - https://samolisov.blogspot.com/2011/02/xa-jta-javase-spring-atomikos-2.html
Что же с ним не так, раз понадобился новый патерн?
1) т.к. в JTA появляется новая сущность - координатор транзакций - и сам процесс двухфазный - подготовка и фиксация транзакции - то это приводит к накладным расходам на сетевые вызовы и увеличению задержек (latency)
2) JTA стандарт ограничивает нас JRE совместимыми языками. Более того, не все поддерживают JDBC - см. noSQL хранилища - и JMS - см. Kafka. Причём последняя не стала добавлять поддержку JMS/JTA принципиально https://docs.confluent.io/platform/current/clients/kafka-jms-client/index.html
3) диспетчер транзакций - это ещё одна точка отказа. Пусть их и так много - для транзакции из 3 фаз это минимум 9 = (сервис + хранилище + сеть) х 3, и это не учитывая датацентры, СХД... И кажется, что добавление ещё одной сильно ситуацию не ухудшит. Но эта диспетчер - это централизованная (единая) точка отказа, при сбое диспетчера придётся повторять всю транзакцию с начала.
Но вернёмся к саге. Во-первых у нее есть 2 варианта реализации - оркестрация и хореография. Оркестрация - транзакция управляется из одного микросервиса, хореография - нет единой точки управления, просто идёт обмен сообщениями между микросервисами. Оркестрацией проще управлять и тестировать, хореография более надёжна, т.к. нет единой точки отказа.
Вот тут неплохое описание отличий https://learn.microsoft.com/ru-ru/azure/architecture/reference-architectures/saga/saga
Во-вторых: сага - это не стандарт или библиотека, это архитектурный патерн - реализацию нужно будет писать самому.
Суть саги - одну большую транзакцию мы делим на ряд локальных транзакций, в рамках которых обеспечивается строгая тразакционность. Плюс все локальные транзакции мы упорядочиваем таким образом, что вначале идут компенсируемые транзакции, а потом - повторяемые. Первые в случае сбоя мы компенсируем - т.е. откатываем, вторые - докатываем. Соответственно, в середине есть поворотная (pivot) локальная транзакция, после успешного выполнения которой все последующие транзакции мы обязаны докатить.
To be continued...
#patterns #microservices #saga
Сегодня будет пост про паттерн Saga.
Saga - это способ осуществить распределённую транзакцию. Обычная транзакция осуществляется в рамках одной сущности, как правило базы данных. Распределённая - между несколькими. Проблема здесь в том, что для одной системы - реляционной БД или кластера Kafka - можно воспользоваться встроенным механизмом транзакций, для распределённой - нет.
В общем случае распределённые транзакции могут понадобиться и для операций между несколькими монолитными приложениями, но наиболее актуальны они стали при переходе на микросервисы, т.к. при этом расширились границы существующих бизнес транзакций.
Возможно кто-то слышал в применении к распределённым транзакциям и Java такие аббревиатуры как XA или JTA. Это стандарт Java EE (Jakarta EE) для осуществления распределённых транзакций между JDBC и JMS источниками. Существует давно, есть работающие реализации - https://samolisov.blogspot.com/2011/02/xa-jta-javase-spring-atomikos-2.html
Что же с ним не так, раз понадобился новый патерн?
1) т.к. в JTA появляется новая сущность - координатор транзакций - и сам процесс двухфазный - подготовка и фиксация транзакции - то это приводит к накладным расходам на сетевые вызовы и увеличению задержек (latency)
2) JTA стандарт ограничивает нас JRE совместимыми языками. Более того, не все поддерживают JDBC - см. noSQL хранилища - и JMS - см. Kafka. Причём последняя не стала добавлять поддержку JMS/JTA принципиально https://docs.confluent.io/platform/current/clients/kafka-jms-client/index.html
3) диспетчер транзакций - это ещё одна точка отказа. Пусть их и так много - для транзакции из 3 фаз это минимум 9 = (сервис + хранилище + сеть) х 3, и это не учитывая датацентры, СХД... И кажется, что добавление ещё одной сильно ситуацию не ухудшит. Но эта диспетчер - это централизованная (единая) точка отказа, при сбое диспетчера придётся повторять всю транзакцию с начала.
Но вернёмся к саге. Во-первых у нее есть 2 варианта реализации - оркестрация и хореография. Оркестрация - транзакция управляется из одного микросервиса, хореография - нет единой точки управления, просто идёт обмен сообщениями между микросервисами. Оркестрацией проще управлять и тестировать, хореография более надёжна, т.к. нет единой точки отказа.
Вот тут неплохое описание отличий https://learn.microsoft.com/ru-ru/azure/architecture/reference-architectures/saga/saga
Во-вторых: сага - это не стандарт или библиотека, это архитектурный патерн - реализацию нужно будет писать самому.
Суть саги - одну большую транзакцию мы делим на ряд локальных транзакций, в рамках которых обеспечивается строгая тразакционность. Плюс все локальные транзакции мы упорядочиваем таким образом, что вначале идут компенсируемые транзакции, а потом - повторяемые. Первые в случае сбоя мы компенсируем - т.е. откатываем, вторые - докатываем. Соответственно, в середине есть поворотная (pivot) локальная транзакция, после успешного выполнения которой все последующие транзакции мы обязаны докатить.
To be continued...
#patterns #microservices #saga
Blogspot
Распределенные транзакции (XA) с помощью JTA в JavaSE (на примере Spring + Atomikos)
При интеграции приложений в единую информационную систему наиболее остро встает проблема обеспечения целостности и непротиворечивости данны...
Всем привет!
Продолжение про сагу.
Когда мы говорим про транзакции, сначала всплывает аббревиатура ACID. Транзакции должны обеспечивать принципы ACID. Посмотрим что тут у нас с сагой.
A - атомарность: или все выполняется, или все откатывается. Собственно атормарность есть в определении паттерна, см. выше. Единственное отличие - у нас нет волшебного rollback на всю распределённую транзакцию, бизнес логику отката придётся писать руками.
C - консистентность данных. Сага обеспечивает т.наз. eventually consistentcy - конечную согласованность. Т.е. данные будут согласованы только после окончания распределённой транзакции. В течение транзакции данные в разных микросервисах могут расходится. Транзакция в БД может обеспечить строгую согласованность изменяемых данных с нужным уровнем изоляции. Поэтому переходим к
I - изоляции изменений внутри транзакции от других операций. Сага не обеспечивает ее совсем, что с этим можно сделать описано в статье про это патерн от Microsoft по ссылке выше. Важный момент - в отличие от транзакции в БД, которая как правило длится миллисекунды, распределённая транзакция - это секунды, может даже десятки секунд. Несогласованность данных в течение этого времени из-за отсутствия изоляции нужно иметь в виду. В дополнение к описанным в статье по ссылке способом скажу ещё один - завершать транзакцию как можно быстрее и игнорировать несогласованность данных) Пример: клиент вряд ли будет жаловаться в службу поддержки, если после отмены заказа деньги и бонусы вернутся на счёт в течение минуты. И скорее всего будет - если это не будет сделано через час.
D - надёжность хранения данных, к саге отношения напрямую не имеет, обеспечивается используемыми хранилищами.
Т.к. в итоге мы получили ACD, причем неполноценный, то для распределенных транзакций придумали новую аббревиатуру - Basically Available, Soft-state, Eventually consistent - https://ru.m.wikipedia.org/wiki/Теорема_CAP#BASE-архитектура
Ещё один интересный момент про сагу: определение последовательности шагов - локальных транзакций. Единственно верной схемы нет, но есть рекомендации. Первая - fail fast. Т.е. если есть локальная транзакция, которая упадёт с большей вероятностью - ее нужно ставить вначале. Пример: резерв билета или товара. Вторая - если какая-то локальная транзакция проводит к критичной для клиента несогласованность данных - ее нужно выполнять как можно позже. Что делать, если эти рекомендации противоречат друг другу - зависит от сценария, но в целом я бы выбрал уменьшение времени неконсистентности.
Еще интересный момент касается саги в виде оркестрации. Т.к. ее главный плюс - сделать простой и понятной бизнес логику саги, то самая очевидная ее реализация вот такая:
class OrderSaga {
SagaResult execute() {
// шаг 1
// шаг 2
// ...
}
}
Назовём этот подход Transaction Script, есть такой Паттерн организации бизнес логики.
Просто - да. Но если процесс сложный, каждый шаг тоже, то мы ухудшим читаемость кода, получим замечание SonarQube про длину метода да и нарушим S из SOLID, принцип единой ответственности. Что делать? Использовать event driven подход:
class OrderSaga {
PrepareEvent start(...) {..}
ReserveEvent makeReservation(...) {...}
// ...
}
При необходимости обработку событий можно разнести в разные классы. Чтобы было понимание как работает процесс нужно написать пару модульных тестов - позитивный и негативные сценарии, ведь тесты в идеале - лучшая документация к коду. Ещё один плюс - в событийной стиле легко сделать весь процесс неблокирующим, например, через адаптер отправляя и принимая все события в Kafka. Да, есть ещё БД, запись в БД в эту парадигме - это такое же событие. В этом случае стоит посмотреть в сторону R2DBC https://www.baeldung.com/r2dbc Для REST endpoint и client есть Spring WebFlux.
К слову, Transaction script тоже может обеспечить неблокирующее выполнение, но только в языках программирования с async await: c#, python, rust https://learn.microsoft.com/ru-ru/dotnet/csharp/language-reference/operators/await
To be continued...
#patterns #saga #microservices #acid #arch_compromises
Продолжение про сагу.
Когда мы говорим про транзакции, сначала всплывает аббревиатура ACID. Транзакции должны обеспечивать принципы ACID. Посмотрим что тут у нас с сагой.
A - атомарность: или все выполняется, или все откатывается. Собственно атормарность есть в определении паттерна, см. выше. Единственное отличие - у нас нет волшебного rollback на всю распределённую транзакцию, бизнес логику отката придётся писать руками.
C - консистентность данных. Сага обеспечивает т.наз. eventually consistentcy - конечную согласованность. Т.е. данные будут согласованы только после окончания распределённой транзакции. В течение транзакции данные в разных микросервисах могут расходится. Транзакция в БД может обеспечить строгую согласованность изменяемых данных с нужным уровнем изоляции. Поэтому переходим к
I - изоляции изменений внутри транзакции от других операций. Сага не обеспечивает ее совсем, что с этим можно сделать описано в статье про это патерн от Microsoft по ссылке выше. Важный момент - в отличие от транзакции в БД, которая как правило длится миллисекунды, распределённая транзакция - это секунды, может даже десятки секунд. Несогласованность данных в течение этого времени из-за отсутствия изоляции нужно иметь в виду. В дополнение к описанным в статье по ссылке способом скажу ещё один - завершать транзакцию как можно быстрее и игнорировать несогласованность данных) Пример: клиент вряд ли будет жаловаться в службу поддержки, если после отмены заказа деньги и бонусы вернутся на счёт в течение минуты. И скорее всего будет - если это не будет сделано через час.
D - надёжность хранения данных, к саге отношения напрямую не имеет, обеспечивается используемыми хранилищами.
Т.к. в итоге мы получили ACD, причем неполноценный, то для распределенных транзакций придумали новую аббревиатуру - Basically Available, Soft-state, Eventually consistent - https://ru.m.wikipedia.org/wiki/Теорема_CAP#BASE-архитектура
Ещё один интересный момент про сагу: определение последовательности шагов - локальных транзакций. Единственно верной схемы нет, но есть рекомендации. Первая - fail fast. Т.е. если есть локальная транзакция, которая упадёт с большей вероятностью - ее нужно ставить вначале. Пример: резерв билета или товара. Вторая - если какая-то локальная транзакция проводит к критичной для клиента несогласованность данных - ее нужно выполнять как можно позже. Что делать, если эти рекомендации противоречат друг другу - зависит от сценария, но в целом я бы выбрал уменьшение времени неконсистентности.
Еще интересный момент касается саги в виде оркестрации. Т.к. ее главный плюс - сделать простой и понятной бизнес логику саги, то самая очевидная ее реализация вот такая:
class OrderSaga {
SagaResult execute() {
// шаг 1
// шаг 2
// ...
}
}
Назовём этот подход Transaction Script, есть такой Паттерн организации бизнес логики.
Просто - да. Но если процесс сложный, каждый шаг тоже, то мы ухудшим читаемость кода, получим замечание SonarQube про длину метода да и нарушим S из SOLID, принцип единой ответственности. Что делать? Использовать event driven подход:
class OrderSaga {
PrepareEvent start(...) {..}
ReserveEvent makeReservation(...) {...}
// ...
}
При необходимости обработку событий можно разнести в разные классы. Чтобы было понимание как работает процесс нужно написать пару модульных тестов - позитивный и негативные сценарии, ведь тесты в идеале - лучшая документация к коду. Ещё один плюс - в событийной стиле легко сделать весь процесс неблокирующим, например, через адаптер отправляя и принимая все события в Kafka. Да, есть ещё БД, запись в БД в эту парадигме - это такое же событие. В этом случае стоит посмотреть в сторону R2DBC https://www.baeldung.com/r2dbc Для REST endpoint и client есть Spring WebFlux.
К слову, Transaction script тоже может обеспечить неблокирующее выполнение, но только в языках программирования с async await: c#, python, rust https://learn.microsoft.com/ru-ru/dotnet/csharp/language-reference/operators/await
To be continued...
#patterns #saga #microservices #acid #arch_compromises
Baeldung
R2DBC – Reactive Relational Database Connectivity | Baeldung
A quick and practical overview of R2DBC - reactive database connectivity.
Всем привет!
Этим постом завершается серия по паттерну Сага.
В предыдущем посте забыл упомянуть 3-й и 4-й способ реализации Саги.
Третий - если вы используете BPMN движок, например, Camunda, то он отлично подходит для оркестратора Саги. Более того, использовать BPMN как оркестратор - лучшая идея, чем использовать его как среду для low-code разработки. Ну не верю я в low-code, не сталкивался с работающими кейсами) Главные плюсы BPMN в данном - случае готовая state machine и визуализация Саги. К слову сама Camunda поставила этот use case на первое место в списке https://camunda.com/solutions/microservices-orchestration/ что как бы намекает. На всякий случай: Camunda - это самый распространенный BPMN движок, собственно движок - opensource, платить нужно только за UI консоль.
Аналогично - если вы уже используете Apache Camel - он тоже умеет в сагу, https://camel.apache.org/components/4.4.x/eips/saga-eip.html
Тут встает вопрос - стоит ли внедрять данные инструменты только ради Саги? Базовый ответ нет, идеальный кейс: если какой-то из этих компонент уже у вас используется - логично реализовать оркестрацию с его помощью. Я бы внедрял, если бы были какие-то еще плюсы от использования, кроме собственно реализации паттерна.
Еще важный момент при реализации оркестратора - stateless или statefull? Да, любая бизнес операция имеет как минимум ID и состояние, которые нужно хранить. Но необязательно это делать в классе Саги. Особенно используя event driven подход, можно просто передавать все не необходимые данные в событиях\командах. Напомню, при этом сохранение состояния операции в БД - это тоже событие. Плюс такого подхода - не нужно думать о букве D из ACID, т.е. персистентности, для данных, хранимых в оркестраторе. А где персистентность, там и кэширование, т.к. обращение к БД - дорого. И восстановление данных из БД при сбоях. Поэтому если вы все же решили хранить состояние операции в коде - я бы рекомендовал не изобретать велосипед, а воспользоваться готовым фреймворком. Два из них я уже упомянул выше, но они достаточно "тяжелые". Вот еще несколько, заточенных собственно под паттерн Сага и под DDD, который в общем-то тесно связан с сагой. Ведь если мы делим систему на ограниченный контексты, Bounded Context, то их данные лежат в разных БД, а следовательно возникает распределенная транзакция...
1) Axios https://docs.axoniq.io/reference-guide/v/3.1/part-ii-domain-logic/sagas
2) Eventuate Tram Saga https://eventuate.io/docs/manual/eventuate-tram/latest/getting-started-eventuate-tram-sagas.html
3) Seata https://www.seata.io/docs/user/mode/saga
Фреймворк помогает нам с:
а) персистентностью
б) кэшированием
в) созданием экземпляра саги для конкретной бизнес-операции
г) удобной работой с параметрами операции
При этом он не отменяет написания кода оркестрации и компенсирующих действий.
На этом пожалуй все.
Хотя нет. Остается вопрос - как же лучше реализовать Сагу? Ответ - лучше сделать свой ограниченный контекст = микросервис таким, чтобы Сага была не нужна)
А если серьезно.
1) постарайтесь использовать только локальные транзакции
2) если это не возможно, и у вас 2-4 шага - используйте хореографию
3) если шагов от 4+ и сервис создаётся с нуля - используйте оркестратор, для начала самописный, stateless event driven
4) у вас уже используется Camunda или Camel - делайте оркестратор на их основе
5) если вас нужен state - используйте фреймворки из последнего списка, например, Axios
6) если нужна сага и state machine - Camunda или Seata
#saga #microservices #ddd #patterns
Этим постом завершается серия по паттерну Сага.
В предыдущем посте забыл упомянуть 3-й и 4-й способ реализации Саги.
Третий - если вы используете BPMN движок, например, Camunda, то он отлично подходит для оркестратора Саги. Более того, использовать BPMN как оркестратор - лучшая идея, чем использовать его как среду для low-code разработки. Ну не верю я в low-code, не сталкивался с работающими кейсами) Главные плюсы BPMN в данном - случае готовая state machine и визуализация Саги. К слову сама Camunda поставила этот use case на первое место в списке https://camunda.com/solutions/microservices-orchestration/ что как бы намекает. На всякий случай: Camunda - это самый распространенный BPMN движок, собственно движок - opensource, платить нужно только за UI консоль.
Аналогично - если вы уже используете Apache Camel - он тоже умеет в сагу, https://camel.apache.org/components/4.4.x/eips/saga-eip.html
Тут встает вопрос - стоит ли внедрять данные инструменты только ради Саги? Базовый ответ нет, идеальный кейс: если какой-то из этих компонент уже у вас используется - логично реализовать оркестрацию с его помощью. Я бы внедрял, если бы были какие-то еще плюсы от использования, кроме собственно реализации паттерна.
Еще важный момент при реализации оркестратора - stateless или statefull? Да, любая бизнес операция имеет как минимум ID и состояние, которые нужно хранить. Но необязательно это делать в классе Саги. Особенно используя event driven подход, можно просто передавать все не необходимые данные в событиях\командах. Напомню, при этом сохранение состояния операции в БД - это тоже событие. Плюс такого подхода - не нужно думать о букве D из ACID, т.е. персистентности, для данных, хранимых в оркестраторе. А где персистентность, там и кэширование, т.к. обращение к БД - дорого. И восстановление данных из БД при сбоях. Поэтому если вы все же решили хранить состояние операции в коде - я бы рекомендовал не изобретать велосипед, а воспользоваться готовым фреймворком. Два из них я уже упомянул выше, но они достаточно "тяжелые". Вот еще несколько, заточенных собственно под паттерн Сага и под DDD, который в общем-то тесно связан с сагой. Ведь если мы делим систему на ограниченный контексты, Bounded Context, то их данные лежат в разных БД, а следовательно возникает распределенная транзакция...
1) Axios https://docs.axoniq.io/reference-guide/v/3.1/part-ii-domain-logic/sagas
2) Eventuate Tram Saga https://eventuate.io/docs/manual/eventuate-tram/latest/getting-started-eventuate-tram-sagas.html
3) Seata https://www.seata.io/docs/user/mode/saga
Фреймворк помогает нам с:
а) персистентностью
б) кэшированием
в) созданием экземпляра саги для конкретной бизнес-операции
г) удобной работой с параметрами операции
При этом он не отменяет написания кода оркестрации и компенсирующих действий.
На этом пожалуй все.
Хотя нет. Остается вопрос - как же лучше реализовать Сагу? Ответ - лучше сделать свой ограниченный контекст = микросервис таким, чтобы Сага была не нужна)
А если серьезно.
1) постарайтесь использовать только локальные транзакции
2) если это не возможно, и у вас 2-4 шага - используйте хореографию
3) если шагов от 4+ и сервис создаётся с нуля - используйте оркестратор, для начала самописный, stateless event driven
4) у вас уже используется Camunda или Camel - делайте оркестратор на их основе
5) если вас нужен state - используйте фреймворки из последнего списка, например, Axios
6) если нужна сага и state machine - Camunda или Seata
#saga #microservices #ddd #patterns
Camunda
Orchestrate Microservices | Camunda
Overcome challenges of microservices orchestration with Camunda. Get speed, scale, and resiliency without compromising microservice autonomy. Learn how.
Всем привет!
При проектировании системы применяя микросервисный подход всегда появляется главный вопрос - как делить?
Сделаешь слишком крупно - получишь маленький монолит. Это как правило всем понятно, т.к. от монолита мы пытаемся уйти создавая микросервисы.
Но есть и другая крайность - слишком мелкое деление. Уже немного писал об этом https://t.me/javaKotlinDevOps/57
Сейчас же хочу проиллюстрировать эту крайность примером.
Предположим у нас есть некая система, представляющая клиентам CRUD REST API. Create, Read, Update, Delete методы. И еще List, который сильно отличается от Read поэтому должен быть выделен отдельно - pagination, сортировка, кэширование...
Можно применить назовем его "наивный" подход к микросервисам и сделать 5 микросервисов по числу методов API. Точнее даже "миллисервисов")
Что получим?
Вспоминаем, что у каждого микросервиса должна быть своя БД.
Это значит что от микросервисов Create и Delete зависят все остальные, т.к. им нужно будет обновить свою копию данных. Это может быть event driven подход с Kafka, CQRS или что-то другое, но в любом случае это зависимость.
От микросервиса Update зависят Read и List.
А если структура данных меняется?
И это зависимости "из коробки" на сферическом CRUD в вакууме. В реальном кейсе по мере развития системы число зависимостей будет больше. Что получилось? Получился распределённый "ком грязи". Такой же "ком грязи", как в старом неподдерживаемом монолите, от которого мы уходили, только хуже. Там хоть БД одна была и интеграций сильно меньше.
Можно попробовать вынести все взаимодействие с БД в отдельный микросервис Storage, но тогда мы нарушаем Single Responsibility - за ту же операцию Create отвечает и микросервис Create, и микросервис Storage. И Create скорее всего станет слишком простым для отдельного микросервиса.
Пример специально взят простой, в реальности выбор может быть сложнее. Зато на этом примере хорошо видны недостатки "миллисервисов".
P.S. За идею примера спасибо все из той же книжке по DDD, расскажу о ней в ближайшее время.
#microservices #rest #arch_compromises
При проектировании системы применяя микросервисный подход всегда появляется главный вопрос - как делить?
Сделаешь слишком крупно - получишь маленький монолит. Это как правило всем понятно, т.к. от монолита мы пытаемся уйти создавая микросервисы.
Но есть и другая крайность - слишком мелкое деление. Уже немного писал об этом https://t.me/javaKotlinDevOps/57
Сейчас же хочу проиллюстрировать эту крайность примером.
Предположим у нас есть некая система, представляющая клиентам CRUD REST API. Create, Read, Update, Delete методы. И еще List, который сильно отличается от Read поэтому должен быть выделен отдельно - pagination, сортировка, кэширование...
Можно применить назовем его "наивный" подход к микросервисам и сделать 5 микросервисов по числу методов API. Точнее даже "миллисервисов")
Что получим?
Вспоминаем, что у каждого микросервиса должна быть своя БД.
Это значит что от микросервисов Create и Delete зависят все остальные, т.к. им нужно будет обновить свою копию данных. Это может быть event driven подход с Kafka, CQRS или что-то другое, но в любом случае это зависимость.
От микросервиса Update зависят Read и List.
А если структура данных меняется?
И это зависимости "из коробки" на сферическом CRUD в вакууме. В реальном кейсе по мере развития системы число зависимостей будет больше. Что получилось? Получился распределённый "ком грязи". Такой же "ком грязи", как в старом неподдерживаемом монолите, от которого мы уходили, только хуже. Там хоть БД одна была и интеграций сильно меньше.
Можно попробовать вынести все взаимодействие с БД в отдельный микросервис Storage, но тогда мы нарушаем Single Responsibility - за ту же операцию Create отвечает и микросервис Create, и микросервис Storage. И Create скорее всего станет слишком простым для отдельного микросервиса.
Пример специально взят простой, в реальности выбор может быть сложнее. Зато на этом примере хорошо видны недостатки "миллисервисов".
P.S. За идею примера спасибо все из той же книжке по DDD, расскажу о ней в ближайшее время.
#microservices #rest #arch_compromises
Telegram
(java || kotlin) && devOps
Всем привет!
Пару заблуждений про микросервисы.
1) Облачные технологии (k8s) не равно микросервисам. Да, если вы начинаете проект с микросервисами с нуля и есть возможность развернуть его в облаке - так и нужно делать.
Облако упрощает развертывание и масштабирование…
Пару заблуждений про микросервисы.
1) Облачные технологии (k8s) не равно микросервисам. Да, если вы начинаете проект с микросервисами с нуля и есть возможность развернуть его в облаке - так и нужно делать.
Облако упрощает развертывание и масштабирование…
Всем привет!
Поговорим снова о микросервисах. Я уже писал, почему не стоит делать слишком мелкие микросервисы https://t.me/javaKotlinDevOps/305
Но встает закономерный вопрос - "сколько вешать в граммах", в смысле - а какого размера должны быть микросервисы?
Обозначим нижний и верхний предел, а для этого придется вспомнить DDD.
Для начала рассмотрим понятие ограниченного контекста (bounded context). Это связанный набор сущностей из реального мира, для наименования которых используется "единый язык" (ubiquitous language) - непротиворечивый набор терминов. Эти сущности описываются в аналитике, тест-кейсах и превращаются в классы в нашем сервисе и в таблицы в БД. Контекстом как правило занимается одна команда - так проще всего поддерживать "единый язык". И за микросервис тоже должна отвечать одна команда. Т.е. ограниченный контекст - это отличный кандидат на микросервис. Но при этом у одной команды может быть несколько микросервисов. И контекст может быть достаточно большим. Т.е. у нас есть верхняя граница микросервиса.
Теперь рассмотрим понятие агрегата - группу сущностей, имеющую уникальный идентификатор, изменение которой производится атомарно. Т.е. агрегат - граница транзакции в БД. А т.к. возможность делегировать управление транзакцией СУБД - это очень крутая штука, то разделять агрегат между разными БД не стоит. При этом один микросервис = одна БД. Поэтому агрегат - нижняя граница микросервиса.
#microservices #ddd
Поговорим снова о микросервисах. Я уже писал, почему не стоит делать слишком мелкие микросервисы https://t.me/javaKotlinDevOps/305
Но встает закономерный вопрос - "сколько вешать в граммах", в смысле - а какого размера должны быть микросервисы?
Обозначим нижний и верхний предел, а для этого придется вспомнить DDD.
Для начала рассмотрим понятие ограниченного контекста (bounded context). Это связанный набор сущностей из реального мира, для наименования которых используется "единый язык" (ubiquitous language) - непротиворечивый набор терминов. Эти сущности описываются в аналитике, тест-кейсах и превращаются в классы в нашем сервисе и в таблицы в БД. Контекстом как правило занимается одна команда - так проще всего поддерживать "единый язык". И за микросервис тоже должна отвечать одна команда. Т.е. ограниченный контекст - это отличный кандидат на микросервис. Но при этом у одной команды может быть несколько микросервисов. И контекст может быть достаточно большим. Т.е. у нас есть верхняя граница микросервиса.
Теперь рассмотрим понятие агрегата - группу сущностей, имеющую уникальный идентификатор, изменение которой производится атомарно. Т.е. агрегат - граница транзакции в БД. А т.к. возможность делегировать управление транзакцией СУБД - это очень крутая штука, то разделять агрегат между разными БД не стоит. При этом один микросервис = одна БД. Поэтому агрегат - нижняя граница микросервиса.
#microservices #ddd
Telegram
(java || kotlin) && devOps
Всем привет!
При проектировании системы применяя микросервисный подход всегда появляется главный вопрос - как делить?
Сделаешь слишком крупно - получишь маленький монолит. Это как правило всем понятно, т.к. от монолита мы пытаемся уйти создавая микросервисы.…
При проектировании системы применяя микросервисный подход всегда появляется главный вопрос - как делить?
Сделаешь слишком крупно - получишь маленький монолит. Это как правило всем понятно, т.к. от монолита мы пытаемся уйти создавая микросервисы.…
Всем привет!
Случайно наткнулся на старую статью - 2015 год - про переход с legacy на Service Oriented Architecture ака SOA.
И хочу сказать, что это хороший пример развития истории по спирали)
Что в статье актуально?
Заменяем слово SOA на микросервисы, и в целом все, что касается преимуществ микросервисной архитектуры и стратегии перехода на нее - актуально. Микросервисы = SOA 2.0 )))
REST оставляем, SOAP+XML заменяем на gRPC\GraphQL для тех случаев, когда требуется большая производительность и гибкость соответственно по сравнению с REST. К слову, недостаток производительности и гибкости - это основные проблемы SOAP. Ремарка - знаю места, где SOAP еще жив (интеграция с госорганами), но он в любом случае вымирает.
ESB, трудности реализации асинхронного взаимодействия - все эти задачи взяла на себя Kafka. Прорывной инструмент - быстрый, надежный (обеспечивает дешевую персистентность), opensource, простой с точки зрения разработчика. В т.ч. потому, что нет необходимости разрабатывать логику маппинга сообщений на брокере. Да, он реализует только одну из двух основных моделей асинхронного взаимодействия - Publisher-Subscriber - и не реализует Message Queue. Но понятно, что топиками можно пользоваться как заменой очередей, и в большинстве случаев проблем при этом не будет.
Облачные решения - за 10 лет из вызова превратились в новую реальность)
А вызов сейчас - внедрение AI. Как-то так)
#microservices #ai #cloud #kafka #rest
Случайно наткнулся на старую статью - 2015 год - про переход с legacy на Service Oriented Architecture ака SOA.
И хочу сказать, что это хороший пример развития истории по спирали)
Что в статье актуально?
Заменяем слово SOA на микросервисы, и в целом все, что касается преимуществ микросервисной архитектуры и стратегии перехода на нее - актуально. Микросервисы = SOA 2.0 )))
REST оставляем, SOAP+XML заменяем на gRPC\GraphQL для тех случаев, когда требуется большая производительность и гибкость соответственно по сравнению с REST. К слову, недостаток производительности и гибкости - это основные проблемы SOAP. Ремарка - знаю места, где SOAP еще жив (интеграция с госорганами), но он в любом случае вымирает.
ESB, трудности реализации асинхронного взаимодействия - все эти задачи взяла на себя Kafka. Прорывной инструмент - быстрый, надежный (обеспечивает дешевую персистентность), opensource, простой с точки зрения разработчика. В т.ч. потому, что нет необходимости разрабатывать логику маппинга сообщений на брокере. Да, он реализует только одну из двух основных моделей асинхронного взаимодействия - Publisher-Subscriber - и не реализует Message Queue. Но понятно, что топиками можно пользоваться как заменой очередей, и в большинстве случаев проблем при этом не будет.
Облачные решения - за 10 лет из вызова превратились в новую реальность)
А вызов сейчас - внедрение AI. Как-то так)
#microservices #ai #cloud #kafka #rest