DDD, разница между сагой и диспетчером событий?

На нескольких сайтах (например, здесь или здесь Саги описываются как механизм, который прослушивает события домена и реагирует на них, выполняя новые команды и, наконец, изменяя домен и т. д.

Есть ли разница между Saga и простым диспетчером событий, в котором некоторые подписчики реагируют на события?


person fj123x    schedule 14.09.2015    source источник


Ответы (2)


«Сага» поддерживает состояние процесса. Более точный термин - менеджер процессов. Термин «сага» популяризировал NServiceBus, поэтому многие люди в наши дни называют его «сагой NServiceBus». Настоящая сага - это концепция базы данных.

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

Есть способы справиться с состоянием процесса без использования саги, например: маршрутные карты и «хореография». Менеджеры процессов - это скорее механизм «оркестровки».

Менеджеры процессов могут сделать вашу жизнь намного проще, поэтому они делают немного больше, чем диспетчер событий.

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

Вы можете подумать, что это немного похоже на рабочий процесс, и вы будете правы. Однако движок рабочего процесса - довольно сложное дело, тогда как менеджер процессов должен быть первоклассным гражданином в вашем мире DDD :)

Пример диспетчера процессов

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

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

Так что будет MemberRegistrationProcessRepository. Завершение процесса может привести к его архивированию или даже удалению, если он действительно больше не требуется.

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

Раньше я также отслеживал изменения статуса, и у нас было соглашение об уровне обслуживания в 15 минут на каждый статус. Это отслеживалось, и обо всех менеджерах процессов, которые находились в состоянии более 15 минут, сообщалось основной операционной группе для расследования.

В C # могло быть что-то вроде этого:

public class MemberRegistrationProcess
{
    public Guid ProcessId { get; private set; }
    public string Name { get; private set; }
    public EMailAddress EMailAddress { get; private set; }
    public string Status { get; private set; }

    public static MemberRegistrationProcess Create(string name, EMailAddress eMailAddress)
    {
        return new MemberRegistrationProcess(Guid.NewGuid(), name, eMailAddress, "Started");
    }

    public MemberRegistrationProcess(Guid processId, string name, EMailAddress eMailAddress, string status)
    {
        ProcessId = processId;
        Name = name;
        EMailAddress = eMailAddress;
        Status = status;
    }

    public void EMailAddressVerified(IMemberRepository memberRepository)
    {
        if (!Status.Equals("Started"))
        {
            throw new InvalidOperationException("Can only verify e-mail address if in 'started' state.");
        }

        memberRepository.Add(new Member(Name, EMailAddress));

        Status = "EMailAddressVerififed";
    }

    public void WelcomeEMailSent()
    {
        if (!Status.Equals("EMailAddressVerififed"))
        {
            throw new InvalidOperationException("Can only set welcome e-mail sent if in 'EMailAddressVerififed' state.");
        }

        Status = "WelcomeEMailSent";
    }

    public void Complete(Member member)
    {
        if (!Status.Equals("WelcomeEMailSent"))
        {
            throw new InvalidOperationException("Can only complete in 'WelcomeEMailSent' state.");
        }

        member.Activate();

        Status = "Complete";
    }
}
person Eben Roux    schedule 15.09.2015
comment
не могли бы вы добавить абстрактный пример того, что означает состояние процесса и как обрабатывается диспетчером процессов? Спасибо! - person fj123x; 15.09.2015
comment
Я добавил образец. Имейте в виду, что это может быть настолько сложно или просто, насколько требуется. Статус процесса также является состоянием и показывает, насколько далеко процесс продвинулся. - person Eben Roux; 16.09.2015

Сага - это длительный процесс, который запускается событиями за пределами домена. Эти события могут произойти за секунды, минуты или дни.

Отличие от простой шины событий состоит в том, что Saga поддерживает конечный автомат, который может быть сохранен для обработки длительно выполняющегося процесса в «отключенном» рабочем процессе из-за внешних событий.

Самый простой способ понять это - пример из реальной жизни. Классическая фраза «Мы отправили вам электронное письмо с подтверждением о завершении регистрации на нашем замечательном форуме» должна работать:

Пример с NServiceBus:

// data to be persisted to start and resume Saga when needed
public class UserRegistrationSagaData : ISagaEntity 
{
    public Guid Id { get; set; }
    public string Originator { get; set; }
    public string OriginalMessageId { get; set; }
    public  string Email { get; set; }
    public  int Ticket { get; set; }
}


    // the saga itself
    public class UserRegistrationSaga :
                     Saga<UserRegistrationSagaData>,
                     // tell NServiceBus the Saga is created when RequestRegistration message arrives
                     ISagaStartedBy<RequestRegistration>,
                     // tell NServiceBus the Saga is resumed when ConfirmRegistration message arrives
                     // (user click in the link inside the e-mail)                              
                     IMessageHandler<ConfirmRegistration>   
    {
        public override void ConfigureHowToFindSaga() //primary keys of this saga in persistence
        {
            ConfigureMapping<RequestRegistration>(saga => saga.Email, message => message.Email);
            ConfigureMapping<ConfirmRegistration>(saga => saga.Ticket, message => message.Ticket);
        }

        // when requestRegistrarion arrives this code is executed
        public void Handle(RequestRegistration message)
        {

            // generate new ticket if it has not been generated
            if (Data.Ticket == 0)
            {
                Data.Ticket = NewUserService.CreateTicket();
            }

            Data.Email = message.Email;

            MailSender.Send(message.Email,
                          "Your registration request",
                          "Please go to /registration/confirm and enter the following ticket: " + Data.Ticket);

            Console.WriteLine("New registration request for email {0} - ticket is {1}", Data.Email, Data.Ticket);
        }

        // when ConfirmRegistration arrives this code is executed
        public void Handle(ConfirmRegistration message)
        {
            Console.WriteLine("Confirming email {0}", Data.Email);
            NewUserService.CreateNewUserAccount(Data.Email);
            MailSender.Send(Data.Email,
                          "Your registration request",
                          "Your email has been confirmed, and your user account has been created");

            // tell NServiceBus that this saga can be cleaned up afterwards
            MarkAsComplete();
        }
    }
}

Просто

Bus.Send(new RequestRegistration(...))

То есть веб-контроллер должен выполнять свою работу.

Жесткое кодирование этого поведения с помощью простой шины событий потребует от вас уродливой имитации конечного автомата в вашем домене; то есть для добавления логического поля «подтверждено» в таблицу пользователей в сохраняющемся домене и необходимости запрашивать и работать с «подтвержденными = истинными» пользователями в модуле управления пользователями вашей системы. Или наличие таблицы «ожидающих подтверждения пользователей» в вашем постоянном домене. Я думаю, вы уловили идею.

Итак, Saga похожа на простую шину событий, которая помогает вам не загрязнять ваш домен и его постоянство конечным автоматом из-за «отключенного» длительного процесса. Это просто разделение ответственности в хорошем объектно-ориентированном дизайне.

person jlvaquero    schedule 14.09.2015
comment
Итак, в чем разница между диспетчером событий / шиной событий / слушателями событий? (например, github.com/greenrobot/EventBus) В этом примере я просто вижу типичное событие с подпиской, на которое прослушивает желаемое событие, а затем, когда кто-то отправляет его ему (шина событий), слушатель реагирует на него - person fj123x; 15.09.2015
comment
В саге есть логика для продвижения процесса путем сохранения и изменения состояния. - person dwu39; 10.07.2020