Проверка в рамках асинхронного шаблона SAGA - CQRS и DDD - PullRequest
0 голосов
/ 02 октября 2019

Давайте рассмотрим поток ниже:

  • Вызовы API-клиентов [POST] /api/v1/invitation/:InvitationId/confirm
  • Подтверждение приглашения в SAGA
  • В конечном итоге поднять InvitationConfirmed событие, указывающее на успех

У нас возникли проблемы с поиском подходящего места для проверки «события», которое мы передаем САГА. Например, мы хотим убедиться, что: - указанный InvitationId существует - соответствующее приглашение не просрочено или уже не обработано

Мы попробовали несколько вещей:

  1. Запустить команду:

    • Запустить команду RequestInvitationConfirmation
    • Обрабатывать синхронно этой команды и вернуть ошибку, если команда недействительна ИЛИ иначе вызватьсобытие InvitationConfirmationRequested.

Остальная часть потока такая же

CONS: - Требует от нас следовать шаблону «запрос / ответ» (синхронно в течение времени жизни HTTP-запроса)

Создать событие:

  • Создать событие InvitationConfirmationRequested
  • В SAGA запросить службу Invitation и выполнить проверки. Если команда недействительна, мы публикуем событие InvitationConfirmationFailed (...)

CONS: - Насколько я понимаю, SAGA следует использовать для организации потока,Здесь мы вводим понятие «валидация». Я не уверен, что это рекомендуемый подход.

Валидация - очень распространенная концепция. Как бы вы справились с этим в распределенной полностью асинхронной системе?

Ответы [ 2 ]

0 голосов
/ 06 октября 2019

Важным моментом в разработке этой системы является: «Кто является клиентом этого API?» .

  • Если этот клиент является внутренним Service или Application это одно (как в распределенном приложении, микросервисах и т. Д.).
  • Если API используется сторонним клиентом, это совсем другое.

Shortответ

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

Если API используется из сторонних приложений, то имеет значение, как разделить обязанности между API и другой частью системы, которую он использует. Сделайте API ответственным за проверку и не отправляйте команды с неверными идентификаторами. Относитесь к команде с неверным идентификатором как к ошибке, как в первом случае. В этом случае, если вы используете асинхронный поток, вам потребуется способ связи со сторонним приложением для уведомления об этом. Вы можете использовать что-то вроде WebHooks .

Во второй части проверок проверьте эти серии сообщений в блоге и оригинальную статью .

Длинный ответ

Если вы будете искать вокруг, вы увидите множество обсуждений ошибок и проверок, так что вот мое мнение об этом.

Поскольку мы проводим разделение других частей наших систем, кажется естественным разделить типы ошибок, которые у нас есть. Вы можете проверить эту статью по этой теме.

Давайте определим некоторые типы ошибок.

  • Ошибки домена
  • Ошибки приложения
  • Технические ошибки (потеря соединений с базой данных и т. Д.)

Поскольку у нас есть разные типы ошибок, проверка должна выполняться из разных частей наших систем.

Также передача сообщенийэти ошибки могут быть выполнены различными механизмами в зависимости от:

  • запрашивающей стороны операции и получателя
  • используемого канала связи
  • типа связи: синхронной илиасинхронный

Теперь у вас есть следующие проверки:

  • Проверьте, что Invitation с указанным Id существует
  • Проверьте, что Invitation еще не истек
  • Подтвердите, что Invitation еще не обработан (принят, отклонен и т. в нашем приложении. Давайте используем принцип DesignByContract и определим четкие правила, которые каждый слой (домен, приложение и т. Д.) Должен ожидать от других.

    Давайте определим правило, что команда, содержащая InvitationIdкоторый не соответствует существующему Invitation, не должен создаваться и отправляться.

    ПРИМЕЧАНИЕ. Используемая здесь терминология может сильно различаться в зависимости от того, какой тип архитектуры используется в проекте (Многоуровневая архитектура,Шестигранный и т. Д.)

    Это заставляет CommandCreator проверить, существует ли Invitation с указанным Id перед отправкой команды.

    В случае с APIRouteHandler (контроллер приложений и т. д.), который примет запрос, должен будет:

    • выполнить эту проверку самостоятельно
    • делегировать кому-либо еще для проверки

    Давайте далее определим, что это часть нашего ApplicationLayer (или модуля, компонентов и т. Д. Не имеет значения, как он называется, поэтому я буду использовать Layer) и сделаем это ApplicationError. Отсюда мы можем сделать это разными способами.

    Один из способов - это DispatchConfirmInvitationCommandApplicationService, который спросит у DomainLayer, существует ли Invitation с запрошенным Id и выдаст ошибку (например, исключение), если это не так. Эта ошибка будет обработана RouteHandler и будет отправлена ​​обратно запрашивающей стороне.

    Вы можете использовать как синхронизацию, так и асинхронную связь. Если это асинхронно, вам нужно создать механизм для этого. Вы можете обратиться к EnterpriseIntegrationPatterns для получения дополнительной информации об этом.

    Главное здесь: Это не является частью домена

    С этого момента все остальные в нашей системе должны учитывать, что приглашение с указанным Id в ConfirmInvitationCommand существует. Если это не так, это рассматривается как ошибка в системе и должно быть проверено разработчиками и / или администраторами. Должен быть ручной способ (административный бэкэнд) для отмены таких недопустимых команд, поэтому это необходимо учитывать при разработке системы, но это следует рассматривать как ошибку в системе.

    Две другие проверки являются частьюиз Domain.

    Допустим, у вас есть

    • Invitation совокупность
    • InvitationConfirmationSaga

    Давайтезаставить их эти агрегаты общаться с сообщениями. Давайте определим эти типы сообщений:

    • RequestConfirmInvitation
    • InvitationExpired
    • InvitationAlreadyProcessed

    Вот основной поток:

    • ConfirmInvitationCommand запускает InvitationConfirmationSaga

    • InvitationConfirmationSaga отправить RequestConfirmInvitation сообщение Invitation

    А затем:

    • Если срок действия Invitation истек, он отправляет сообщение InvitationExpired InvitationConfirmationSaga

    • Если Invitation обработан, он отправляет InvitationAlreadyProcessed сообщение InvitationConfirmationSaga

    • Если Invitation не истек, он принимается и отправляет InvitationAccepted сообщениеInvitationConfirmationSaga

    Тогда:

    • InvitationConfirmationSaga получит эти сообщения и соответственно вызовет события.

    Таким образом, высохранить логику домена в Domain, в этом случае Invitation Aggregate.

0 голосов
/ 02 октября 2019

У вас есть команда ConfirmInvitation, содержащая InvitationId. Вы отправляете его на свой Invitation домен с InvaitationAppService. Ваш Invitation домен должен выглядеть следующим образом

...
public void ConfirmInvitation()
{
    if (this.Status == InvitationStatus.Confirmed)
        throw new InvalidInvitationException("Requested invitation has already been confirmed");
    //check more business logic here
    this.Status = InvitationStatus.Confirmed;
    Publish(new InviationConfirmedEvent(...));
}
...

Ваш InvitationAppService должен иметь что-то вроде ниже:

...
public void ConfirmInvitation(Guid invitationId)
{
    // rehydrate your domain from eventstore
    var invitation = repo.GetById<Invitation>(invitationId);
    if (invitation == null)
        throw new InvalidInvitationException("Invalid Invitation requested");
    invitation.ConfirmInvitation(new ConfirmInvitation(...));
}

Вам не нужно вводить новое событие InvitationConfirmationRequested,DDD - это подход, при котором проверка вашего домена / бизнеса должна проводиться внутри доменов. Не пытайтесь использовать другие шаблоны или технологии в вашем домене. Проверка вашего домена внутри саги (которая используется для организации распределения транзакций по сервисам) может создать сложности и хаос

...