Используя сагу, учитывая событие EventA, начинается сага, она отправляет команду (или несколько). Как мы можем убедиться, что команда отправлена успешно, тогда фактическая логика в другом микросервисе не сработала и т. Д.
Давайте рассмотрим пример электронной саги: когда пользователь регистрируется, мы создаем User Aggregate, который публикует UserRegisteredEvent, будет создана сага, и эта сага отвечает за то, чтобы электронное письмо с регистрацией было отправлено пользователю (электронное письмо может содержать ключ проверки, приветственное сообщение и т. д.).
Должны ли мы использовать:
commandGateway.sendAndWait
с попыткой / уловкой -> масштабируется?commandGateway.send
и используйте крайний срок и используйте какое-то «событие сбоя», например SendEmailFailedEvent -> требуется связать «токен» для команд, чтобы можно было связать «AssociationProperty» с правильной сагой, которая отправила SendRegistrationEmailCommandcommandGateway.send(...).handle(...)
-> в дескрипторе можем ли мы ссылаться на eventGateway / commandGateway, которые были в MyEmailSaga? Если ошибка отправляем событие? Или мы можем изменить / вызвать метод из экземпляра саги, который у нас был. Если ошибок нет, значит другая служба отправила событие, подобное «RegistrationEmailSentEvent», и сага закончится.используйте крайний срок, потому что мы просто используем «отправить» и не обрабатываем возможную ошибку команды, которая могла быть не отправлена (другая служба не работает и т. д.)
что-то другое?
Или все вместе?
Как обрабатывать ошибки ниже? (используйте крайний срок или .handle (...) или другое)
Ошибки могут быть:
у команды нет обработчиков (нет обслуживания и т. д.)
команда была обработана, но возникло исключение в другой службе, и событие не отправлено (в другой службе нет попыток / улова)
команда была обработана, исключение создано и перехвачено, другая служба публикует событие, чтобы уведомить, что не удалось отправить электронное письмо (сага получит событие и выполнит соответствующее действие в зависимости от типа события и предоставленных данных -> возможно, электронное письмо неверное или не существует, поэтому нет необходимости повторить попытку)
другие ошибки, которые я пропустил?
@Saga
public class MyEmailSaga {
@Autowired
transient CommandGateway commandGateway;
@Autowired
transient EventGateway eventGateway;
@Autowired
transient SomeService someService;
String id;
SomeData state;
/** count retry times we send email so can apply logic on it */
int sendRetryCount;
@StartSaga
@SagaEventHandler(associationProperty = "id")
public void on(UserRegisteredEvent event) {
id = event.getApplicationId();
//state = event........
// what are the possibilities here?
// Can we use sendAndWait but it does not scale very well, right?
commandGateway.send(new SendRegistrationEmailCommand(...));
// Is deadline good since we do not handle the "send" of the command
}
// Use a @DeadlineHandler to retry ?
@DeadlineHandler(deadlineName = "retry_send_registration_email")
fun on() {
// resend command and re-schedule a deadline, etc
}
@EndSaga
@SagaEventHandler(associationProperty = "id")
public void on(RegistrationEmailSentEvent event) {
}
}
ИЗМЕНИТЬ (после принятого ответа):
В основном два варианта (извините, но код котлина ниже):
Первый вариант
commandGateway.send(SendRegistrationEmailCommand(...))
.handle({ t, result ->
if (t != null) {
// send event (could be caught be the same saga eventually) or send command or both
}else{
// send event (could be caught be the same saga eventually) or send command or both
}
})
// If not use handle(...) then you can use thenApply as well
.thenApply { eventGateway.publish(SomeSuccessfulEvent(...)) }
.thenApply { commandGateway.send(SomeSuccessfulSendOnSuccessCommand) }
2-й вариант: используйте крайний срок, чтобы убедиться, что сага что-то сделает, если SendRegistrationEmailCommand завершился неудачно, и вы не получили никаких событий при сбое (когда вы не обрабатываете отправленную команду).
Конечно, можно использовать дедлайн для других целей.
Когда SendRegistrationEmailCommand был успешно получен, получатель опубликует событие, чтобы сага была уведомлена и отреагировала на него. Может быть RegistrationEmailSentEvent или RegistrationEmailSendFailedEvent.
Резюме:
Кажется, что лучше всего использовать handle()
только в том случае, если команда не была отправлена или получатель выдал неожиданное исключение, в таком случае опубликуйте событие, чтобы сага действовала на него. В случае успеха получатель должен опубликовать событие, saga будет его прослушивать (и в конечном итоге на всякий случай зарегистрирует крайний срок); Получатель также может отправлять событие, чтобы уведомить об ошибке и не генерировать, saga также будет прослушивать это событие.