相关疑难解决方法(0)

在持久化聚合之前发布域事件是否安全?

在许多不同的项目中,我看到了两种不同的方法来提升域事件.

  1. 直接从聚合中提升域事件.例如,假设您有Customer聚合,这里面是一个方法:

    public virtual void ChangeEmail(string email)
    {
        if(this.Email != email)
        {
            this.Email = email;
            DomainEvents.Raise<CustomerChangedEmail>(new CustomerChangedEmail(email));
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    我可以看到这种方法存在两个问题.第一个是无论聚合是否持久化,都会引发事件.想象一下,如果您想在注册成功后向客户发送电子邮件.将引发事件"CustomerChangedEmail",即使未保存聚合,某些IEmailSender也会发送电子邮件.当前实现的第二个问题是每个事件都应该是不可变的.所以问题是如何初始化其"OccuredOn"属性?只在里面汇总!合乎逻辑,对吧!它迫使我将ISystemClock(系统时间抽象)传递给聚合中的每个方法!Whaaat ??? 难道你不觉得这个设计很脆弱吗?以下是我们将要提出的建议:

    public virtual void ChangeEmail(string email, ISystemClock systemClock)
    {
        if(this.Email != email)
        {
            this.Email = email;
            DomainEvents.Raise<CustomerChangedEmail>(new CustomerChangedEmail(email, systemClock.DateTimeNow));
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 第二种方法是采用Event Sourcing模式建议的方法.在每个聚合上,我们定义一个未列出事件的(List)列表.请注意UncommitedEvent不是域事件!它甚至没有OccuredOn属性.现在,当在Customer Aggregate上调用ChangeEmail方法时,我们不会提出任何内容.我们只是将事件保存到我们的聚合中存在的uncommitedEvents集合.像这样:

    public virtual void ChangeEmail(string email)
    {
        if(this.Email != email)
        {
            this.Email = email;
            UncommitedEvents.Add(new CustomerChangedEmail(email));
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

那么,什么时候实际的域事件被提出?此职责委托给持久层.在ICustomerRepository中,我们可以访问ISystemClock,因为我们可以轻松地将其注入到存储库中.在ICustomerRepository的Save()方法中,我们应该从Aggregate中提取所有uncommitedEvents,并为每个事件创建一个DomainEvent.然后我们在新创建的Domain Event上设置OccuredOn属性.然后,在IN ONE TRANSACTION中,我们保存聚合并发布所有域事件.通过这种方式,我们将确保所有事件都将在具有聚合持久性的跨国界限中提升.
我对这种方法不喜欢什么?我不想为同一事件创建2种不同的类型,即对于CustomerChangedEmail行为,我应该有CustomerChangedEmailUncommited类型和CustomerChangedEmailDomainEvent.只有一种类型会很好.请分享您对此主题的体验!

c# dns domain-driven-design

8
推荐指数
1
解决办法
1500
查看次数

标签 统计

c# ×1

dns ×1

domain-driven-design ×1