Domain driven design 域事件能否向UI层返回反馈信息?

Domain driven design 域事件能否向UI层返回反馈信息?,domain-driven-design,Domain Driven Design,我对DDD应用程序还是相当陌生的。我正在读埃里克·埃文的“领域驱动设计”,读过Udi Dahan的 但有一件事我搞不清楚,关于域事件成功或失败完成的信息应该如何作为反馈返回给用户(即UI层) 例如,我们可以有以下应用层代码: // Application Layer public void SubmitOrder(OrderData data) { var customer = GetCustomer(data.CustomerId); var shoppingCart = GetSh

我对DDD应用程序还是相当陌生的。我正在读埃里克·埃文的“领域驱动设计”,读过Udi Dahan的

但有一件事我搞不清楚,关于域事件成功或失败完成的信息应该如何作为反馈返回给用户(即UI层)

例如,我们可以有以下应用层代码:

// Application Layer
public void SubmitOrder(OrderData data)
{ 
  var customer = GetCustomer(data.CustomerId);
  var shoppingCart = GetShoppingCart(data.CartId);
  customer.Purchase(shoppingCart);
}

// Domain Model
public class Customer
{ 
   public void Purchase(ShoppingCart cart)
   {
      // something done with the cart...

      DomainEvents.Raise(new CustomerPurchaseCompleted() { Customer = this, ShoppingCart = cart });
   }
}
现在,假设我们有以下事件处理程序,如果客户指定了电子邮件地址,则向客户发送确认电子邮件

public class CustomerPurchaseCompletedHandler : Handles<CustomerPurchaseCompleted>
{ 
   public void Handle(CustomerPurchaseCompleted args)
   {
      if (args.Customer.Email != null) {
         // send email to args.Customer
      }
      else {
         // report that no email will be sent...
      }
   }
}
公共类CustomerPurchaseCompletedHandler:句柄 { 公共无效句柄(CustomerPurchaseCompleted参数) { 如果(args.Customer.Email!=null){ //向args.Customer发送电子邮件 } 否则{ //报告将不发送电子邮件。。。 } } } 我的问题是:我应该如何向用户界面层“冒泡”一条反馈消息,说因为客户没有电子邮件集,所以不会发送电子邮件

我今天看到的选项大致如下:

  • 让UI层检查客户是否有电子邮件,并相应地用消息作出反应。这看起来很糟糕,因为UI会意识到应该发送电子邮件,这是应用程序级别的信息

  • 当没有电子邮件时抛出一个
    UserHasNoEmailException
    ,并在某处捕获该信息。这是非常糟糕的,因为不应该使用异常返回信息,而且这不是致命错误,不应该中止其他处理程序

  • 提交者()返回一些
    列表
    。这需要更改
    Purchase()
    DomainEvents.Raise()
    方法才能返回此列表。这导致域模型知道UI不应该显示的内容

  • 这三种选择似乎都不是很好和实用。那么DDD专家是如何做到这一点的呢


    谢谢。

    另一个选项是实现另一个事件处理程序,专门负责通知用户界面客户没有电子邮件地址。因此,当CustomerPurchaseCompletedHandler不发送电子邮件时,该处理程序将通知UI。此处理程序将是UI层的一部分。通知UI的一个好方法是将

    public class NotifyingCustomerPurchaseCompletedHandler : Handles<CustomerPurchaseCompleted>
    { 
       public IEventAggregator Events { get; set; }
    
       public void Handle(CustomerPurchaseCompleted args)
       {
          if (args.Customer.Email == null) {
             // notify UI
             this.Events.GetEvent....
          }
       }
    }
    
    公共类通知CustomerPurchaseCompletedHandler:句柄
    { 
    公共IEventAggregator事件{get;set;}
    公共无效句柄(CustomerPurchaseCompleted参数)
    {
    如果(args.Customer.Email==null){
    //通知用户界面
    此.Events.GetEvent。。。。
    }
    }
    }
    
    总的来说,这基本上是方法1。的确,UI层现在知道要发送电子邮件,但是UI必须知道这一点,因为它需要呈现一条声明不发送电子邮件的消息。显示消息是一个UI问题,通过将处理程序实现保留为UI层的一部分,您可以将其保留为UI层的一部分。这种方法的问题在于,您要检查客户是否有两次电子邮件。此外,电子邮件的发送与UI通知没有直接联系


    另一种选择是引入另一个事件,以表明没有电子邮件的客户已完成购买。然后UI可以订阅此事件。这种方法的缺点是,您专门为UI需求创建事件,而不是为了表达领域知识。

    事件不应报告反馈。事件是反馈

    在这种情况下,
    电子邮件
    是一项业务需求。因此,
    customer.Purchase(shoppingCart)应该确实引发异常

    public class CustomerPurchaseCompletedHandler : Handles<CustomerPurchaseCompleted>
    { 
       public void Handle(CustomerPurchaseCompleted args)
       {
          if (args.Customer.Email != null) {
             // send email to args.Customer
          }
          else {
             // report that no email will be sent...
          }
       }
    }
    
    但假设实际的电子邮件传递失败了。我通常做的是创建一个用于通知的域模型。所以我这样做:

    var notification = new Notification(userId, "Failed to deliver receipt to user.");
    notificationRepository.Save(noitification);
    

    这反过来会生成一个
    NotificationCreated
    事件,该事件可由UI拾取。

    选项1。这很好,因为它应该知道“那”是用例成功所必需的。。。正如某些错误可能不是致命的,但仍需要向用户报告。对于选项1,用户界面必须知道可能发生的每一种变体。我们不是又回到了一个“万事通”的大应用程序了吗?你需要学习什么是真正的不变量,不要用他们无法修复的东西来打扰最终用户。所以,不,这是用例成功的必要条件。谢谢,那么在调用
    submitor()
    之前,UI(ASP.NET MVC)会注册它自己的事件处理程序吗?这一秒可能只是将ViewModel中的某些属性设置为true或false,对吗?我将尝试理解您提供的
    EventAggregator
    链接,但我不确定它与事件注册过程有何区别…特定于UI的处理程序将在整个应用程序的配置区域中注册,可能在注册原始
    CustomerPurchaseCompletedHandler
    的同一位置。在调用
    SubmitOrder()
    之前不会注册它,因为在应用程序的生命周期中,该调用可能会发生多次,并且继续注册处理程序是没有意义的。特定于UI的处理程序可以在某些ViewModel中设置属性。我提出的事件聚合器将是实现这一点的一种更为解耦的方法—处理程序不再需要耦合到特定的ViewModel实例。我仍然不知道这将如何在我的应用程序中实现,因为域模型层位于UI层的下面:ASP.NET MVC-->Web Service API-->应用层-->域模型。但你的回答确实有道理……:)如果MVC项目与域层e通信