我已经通过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
时,将执行处理程序,并将引发UserReceivedCreditEvent
和CreditCreatedEvent
。但是他们不会被处理。我误解了这种模式吗?或者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();
}
答案 0 :(得分:12)
你 CouponActivatedHandler 至少应该让用户实体形成一个存储库,然后用信用点数更新它,然后保存它,然后发布一个 UserCreditsAdded 事件。更好的是,处理程序应该只创建并发送命令 AddCreditsToUser 。
使用域事件模式,操作只是一个命令链 - &gt; event-&gt;命令 - &GT;事件处理程序通常是一个服务(或一个方法),它只关注那个位。事件的发送者对处理程序一无所知,反之亦然。
作为一个拇指规则,Domain对象在状态发生变化时会生成一个事件。服务将获取这些事件然后将它们发送到服务总线(对于一个简单的应用程序,一个DI容器就足够了),它将发布它们以供任何感兴趣的人处理(这意味着来自本地应用程序或订阅该总线的其他应用程序的服务) )。
永远不要忘记,域事件是在进行应用程序架构时使用的高级模式,它不仅仅是执行对象事件的另一种方式(如C#事件)。