2014-10-31 60 views
7

我已經jbogard實現以下模式:如何處理由事件處理程序引發的域事件?

http://lostechies.com/jimmybogard/2014/05/13/a-better-domain-events-pattern/

想象一下下面的實體Coupon和事件CouponActivatedEvent

public class Coupon : DomainEntity 
{ 
    public virtual User User { get; private set; } 

    // ...omitted... 

    public void Activate(User user) 
    { 
     if (User != null) 
      throw new InvalidOperationException("Coupon already activated"); 

     User = user; 

     Events.Add(new CouponActivatedEvent(this)); 
    } 
} 

下面的事件處理CouponActivatedHandler

public class CouponActivatedHandler : IDomainEventHandler<CouponActivatedEvent> 
{ 
    public void Handle(CouponActivatedEvent e) 
    { 
     // user gets 5 credits because coupon was activated 
     for (int i = 0; i < 5; i++) 
     { 
      e.Coupon.User.AddCredit(); // raises UserReceivedCreditEvent and CreditCreatedEvent 
     } 
    } 
} 

以下SaveChanges覆蓋上DbContext(實體框架6),從jbogard的博客文章採取:

public override int SaveChanges() 
{ 
    var domainEventEntities = ChangeTracker.Entries<IDomainEntity>() 
     .Select(po => po.Entity) 
     .Where(po => po.Events.Any()) 
     .ToArray(); 

    foreach (var entity in domainEventEntities) 
    { 
     var events = entity.Events.ToArray(); 
     entity.Events.Clear(); 
     foreach (var domainEvent in events) 
     { 
      _dispatcher.Dispatch(domainEvent); 
     } 
    } 

    return base.SaveChanges(); 
} 

如果我們現在激活的優惠券,這將提高CouponActivatedEvent。當調用SaveChanges時,處理程序將被執行,並且UserReceivedCreditEventCreditCreatedEvent將被引發。他們不會被處理。我誤解了這種模式?或者是SaveChanges覆蓋不合適?

我已經考慮過創建一個循環,直到沒有新的事件發生,然後移動到base.SaveChanges(); ...但我擔心我會無意中創建無限循環。像這樣:

public override int SaveChanges() 
{ 
    do 
    { 
     var domainEventEntities = ChangeTracker.Entries<IDomainEntity>() 
      .Select(po => po.Entity) 
      .Where(po => po.Events.Any()) 
      .ToArray(); 

     foreach (var entity in domainEventEntities) 
     { 
      var events = entity.Events.ToArray(); 
      entity.Events.Clear(); 
      foreach (var domainEvent in events) 
      { 
       _dispatcher.Dispatch(domainEvent); 
      } 
     } 
    } 
    while (ChangeTracker.Entries<IDomainEntity>().Any(po => po.Entity.Events.Any())); 

    return base.SaveChanges(); 
} 

回答

12

是的,你誤解了事情。域事件不像C#事件,它是關於域中更改內容的消息。一條規則是事件是發生的事情,這是過去的事情。因此,事件處理程序根本不能(不應該)改變事件,就像改變過去一樣。

CouponActivatedHandler至少應該得到用戶的實體形成一個倉庫,然後用積分數量進行更新,然後將其保存,然後發佈一個UserCreditsAdded事件。更好的是,處理程序應該創建併發送命令AddCreditsToUser

使用域事件模式,操作只是命令 - >事件 - >命令 - >事件等的鏈。事件處理程序通常是一個服務(或一個方法),只需要注意那一點。事件的發件人將不知道處理程序的任何內容,反之亦然。

作爲一個縮略規則,Domain對象在其狀態發生變化時會生成一個事件。一項服務將把這些事件發送到服務總線(對於一個簡單的應用程序,一個DI容器就足夠了),它將發佈它們以供任何感興趣的人處理(這意味着來自本地應用程序或訂閱該總線的其他應用程序的服務)。

永遠不要忘記,域事件是一個高層次的模式,當做一個應用程序的體系結構時,它不只是另一種做對象事件的方式(如C#的事件)。

+0

謝謝,你已經爲我澄清了多件事情。你是否知道你設計的一個示例項目?我想我明白了,我只是不確定如何正確實施它。 – Korijn 2014-10-31 19:38:19

+4

例如,不是真的。但開始這樣做,練習變得完美。做,注意什麼感覺麻煩,嘗試重構等DDD是通過這樣做來學習。 – MikeSW 2014-10-31 19:40:52

+0

我現在明白,特別是*事件處理程序對過去發生的事情做出反應*解釋了很多。 – Korijn 2014-11-04 09:42:56