Как обрабатывать несколько сущностей, переданных из REST-API в саге, с использованием источника событий (фреймворк axon)?

так что в настоящее время я реализую сагу с фреймворком аксонов, используя источники событий и CQRS.

Обстоятельства следующие:

У меня 3 микросервиса, m1, m2 и m3

Пользователь вводит данные для 3 сущностей e1, e2, e3 в графическом интерфейсе пользователя, которые обрабатываются и сохраняются m1, m2, m3 соответственно, поэтому m1-> e1, m2-> e2, m3-> e3

Теперь о необходимости саги:

e1 не может существовать без e2, а e2 не может существовать без e3.

Таким образом, все 3 объекта должны быть успешно созданы соответствующими службами, и если одна из них выйдет из строя, саге необходимо выполнить компенсационные транзакции для обеспечения согласованности. Сначала m1 создает e1, испускает e1CreatedEvent, а оркестратор отправляет createE2Command в качестве реакции на это и т. Д.

Теперь мои проблемы:

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

Например, m1 создает команду createE1Command и добавляет к ней информацию из e1, затем после ее успешного создания оркестратор инициирует createE2Command и ЗАТЕМ добавит информацию для e2 в эту команду перед ее отправкой .... для этого необходимо, чтобы информацию для e2 нужно как-то хранить до тех пор, пока она не понадобится.

Образец кода:

@Saga
public class ManagementSaga{

@Autowired
private transient CommandGateway commandGateway

@StartSaga
@SagaEventhandler
public void handle (e1CreatedEvent e1CreatedEvent){


  commandGateway.send (new CreateE2Command (e1CreatedEvent.Id, **HERE NEEDS TO BE THE INFO THAT THE USER CREATED PREVIOUSLY**)}}

Могу ли я просто создать объект, содержащий информацию для этих трех сущностей? Это действительно неправильно.

Теперь я понимаю, что, может быть, выбирать такую ​​модель предметной области действительно плохо, и, вероятно, следует избегать этого, если это возможно, но это на всякий случай :-)


person OneRandomCoder    schedule 05.06.2019    source источник


Ответы (1)


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

Также, похоже, существует несоответствие во взаимодействии с точки зрения API и способа разработки системы. Хотя это не неверно по определению, это также может быть признаком того, что либо API неверен (не отражает должным образом поведение системы), либо модель поведения неверна.

Чтобы в любом случае дать вам ответ, который, надеюсь, более полезен, чем «в зависимости от обстоятельств», вы можете получить один результат взаимодействия в нескольких командах или даже запросах. Не во всех случаях требуется сага.

Возьмите единый запрос с информацией о E1, E2 и E3. Затем в вашем контроллере отправьте команды для создания E3 на основе результатов этой команды, создайте E2 и, наконец, создайте E1. Никакую информацию не нужно «кэшировать». Информация о запросе доступна на протяжении всего процесса. Однако этот подход не является транзакционным. Если служба выйдет из строя, она может оставить процесс наполовину выполненным.

Альтернативой является наличие обработчика команд, который регистрирует исходное намерение пользователя. Это не для создания E1, E2 или E3, а для чего-то, что охватывает все три. Событие, которое генерирует процесс, запускает сагу для выполнения каждого шага индивидуально, используя события этих шагов для запуска следующего шага. Этот процесс является транзакционным с характеристиками конечной согласованности.

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

person Allard    schedule 06.06.2019
comment
Я полностью согласен, дизайн может быть некорректным, но я должен его использовать. Мое текущее решение в точности соответствует тому, что вы предложили в своем альтернативном подходе, чтобы объединить три объекта вместе, а затем обработать их шаг за шагом. Но вот мои проблемы: нужно ли мне хранить эту информацию в самом дополнительном агрегате? Пока что единственный способ обработки команд и событий, который я нашел, - это агрегаты. Это кажется неправильным (потому что на самом деле это не нужно сохранять), но необходимо для создания события, которое включает в себя всю необходимую информацию саги, которую оно запускает. - person OneRandomCoder; 07.06.2019
comment
Кроме того, есть ли возможность опубликовать событие без его источника? - person OneRandomCoder; 07.06.2019
comment
Вы также можете поместить аннотацию @CommandHandler на одноэлементный компонент. Это не обязательно должно быть на агрегате. Чтобы опубликовать события, вы можете вставить EventBus (или EventGateway, если на то пошло) в любой компонент. Это позволяет вам инициировать события из любого места вашего приложения. - person Allard; 22.06.2019