DDD,Saga和Event Dispatcher之间的区别?

fj1*_*23x 8 events domain-driven-design saga

在多个站点上(例如,此处此处, Sagas被描述为一种机制,用于侦听域事件并对其作出反应,执行新命令,最后修改域等.

Saga和简单的事件调度员之间有什么区别,你有一些订阅者对事件作出反应吗?

Ebe*_*oux 11

"saga"维持流程状态.一个更准确的术语是流程管理器.术语"传奇"由NServiceBus推广,这就是为什么现在许多人将其称为"NServiceBus传奇".真正的传奇是数据库概念.

无论如何,由于事件调度程序对进程状态不感兴趣,因此它不是进程管理器.正如您所指出的,服务总线也可以充当事件调度程序,通常用于其他系统,尽管服务总线可以处理更多内容.

有一些方法可以在不使用传奇的情况下处理流程状态,例如:路由单和"编排".流程管理者更像是一种"编排"机制.

流程管理人员可以使您的生活变得更加简单,因此它比事件调度员更多.

基本上,您的订户将与您的流程经理进行交互,以实现与流程相关的任何更改.

你可能会认为这有点像工作流程,你会是正确的.但是,工作流引擎是一件非常沉重的事情,而流程经理应该是DDD世界中的一等公民:)

流程管理器示例

以下只是一个快速,我的头脑和广泛的样本.最初,创建成员的数据在进程管理器中存储为状态.只有在验证了电子邮件地址后,才会使用其有效的电子邮件地址创建和存储实际成员.

然后,可能使用服务总线发送欢迎电子邮件.一旦EMailService收到来自端点的响应,表明邮件已成功发送,该处理程序就会指示进程管理器已发送电子邮件,然后完成进程管理器.

所以会有一个MemberRegistrationProcessRepository.如果确实不再需要,完成某个流程可能会导致其被归档甚至删除.

我怀疑事件采购会很好地适用于流程管理人员,但为了保持样本简单,我根据我之前自己实现的内容将以下内容放在一起.

我之前也做的是跟踪状态变化,每个状态的SLA为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";
    }
}
Run Code Online (Sandbox Code Playgroud)


jlv*_*ero 7

Saga 是一个长时间运行的过程,由域外的事件触发。这些事件可能在几秒钟、几分钟或几天内发生。

与简单事件总线的不同之处在于,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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

一个简单的

Bus.Send(new RequestRegistration(...))
Run Code Online (Sandbox Code Playgroud)

由 ie web 控制器应该做的工作。

使用简单的事件总线硬编码此行为将需要您以丑陋的方式模拟域中的状态机;即在域持久性的用户表中添加一个布尔字段“已确认”,并且必须在系统的用户管理模块中查询和使用“已确认=真”用户。或者在您的持久域中有一个“待确认用户”表。我想你会明白的。

因此,Saga 就像一个简单的事件总线,它可以帮助您不因为“断开连接”的长时间运行过程而使用状态机污染您的域和域持久性。这只是良好 OO 设计中的职责分离。