Всем привет!
Сегодня будет пост про паттерн 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