Как обрабатывать команды, отправленные из саги, в фреймворке Axon

Используя сагу, учитывая событие EventA, начинается сага, она отправляет команду (или несколько). Как мы можем убедиться, что команда отправлена ​​успешно, тогда фактическая логика в другом микросервисе не сработала и т. Д.

Давайте рассмотрим пример электронной саги: когда пользователь регистрируется, мы создаем User Aggregate, который публикует UserRegisteredEvent, будет создана сага, и эта сага отвечает за то, чтобы электронное письмо с регистрацией было отправлено пользователю (электронное письмо может содержать ключ проверки, приветственное сообщение и т. д.).

Должны ли мы использовать:

  1. commandGateway.sendAndWait с попыткой / уловкой -> масштабируется?

  2. commandGateway.send и используйте крайний срок и используйте какое-то «событие сбоя», например SendEmailFailedEvent -> требуется связать «токен» для команд, чтобы можно было связать «AssociationProperty» с правильной сагой, которая отправила SendRegistrationEmailCommand

  3. commandGateway.send(...).handle(...) -> в дескрипторе можем ли мы ссылаться на eventGateway / commandGateway, которые были в MyEmailSaga? Если ошибка отправляем событие? Или мы можем изменить / вызвать метод из экземпляра саги, который у нас был. Если ошибок нет, значит другая служба отправила событие, подобное «RegistrationEmailSentEvent», и сага закончится.

  4. используйте крайний срок, потому что мы просто используем «отправить» и не обрабатываем возможную ошибку команды, которая могла быть не отправлена ​​(другая служба не работает и т. д.)

  5. что-то другое?

  6. Или все вместе?

Как обрабатывать ошибки ниже? (используйте крайний срок или .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 также будет прослушивать это событие.


person Yoann CAPLAIN    schedule 03.11.2019    source источник


Ответы (1)


в идеале вы должны использовать асинхронные параметры для работы с ошибками. Это будет либо commandGateway.send(command), либо commandGateway.send(command).thenApply(). Если сбой связан с бизнес-логикой, то может иметь смысл генерировать события по этим сбоям. Тогда простой gateway.send(command) имеет смысл; Сага может реагировать на возвращаемые в результате события. В противном случае вам придется иметь дело с результатом выполнения команды.

Нужно ли вам использовать sendAndWait или просто send().then..., зависит от действий, которые вам нужно выполнить в случае сбоя. К сожалению, при асинхронной работе с результатами вы больше не можете безопасно изменять состояние саги. Axon, возможно, уже сохранил состояние саги, из-за чего эти изменения были потеряны. sendAndWait решает эту проблему. Масштабируемость не часто является проблемой, потому что разные саги могут выполняться параллельно, в зависимости от конфигурации вашего процессора.

Команда Axon в настоящее время изучает возможные API-интерфейсы, которые позволят обеспечить безопасное асинхронное выполнение логики в Sagas, сохраняя при этом гарантии безопасности потоков и сохранения состояния.

person Allard    schedule 07.11.2019
comment
Таким образом, в основном есть 2 варианта: Первый вариант - ›использовать thenApply() или handle() для отправки событий (может быть обнаружен той же сагой, которая инициировала ранее команду) или отправки команд или обоих событий / команд. Второй вариант - ›используйте handle() for в случае, если у команды нет обработчика (служба не работает или другой), и отправьте событие или другое в этом случае; когда команда обрабатывается, подумайте, что прием должен без проблем опубликовать соответствующее событие, чтобы сага получила уведомление и отреагировала на него, или вызовет крайний срок, если он не получил никаких событий через некоторое время, когда он отправил команду - person Yoann CAPLAIN; 07.11.2019