如何处理事件处理程序引发的域事件?

Kor*_*ijn 8 c# domain-driven-design entity-framework

我已经通过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));
    }
}
Run Code Online (Sandbox Code Playgroud)

以下事件处理程序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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以下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();
}
Run Code Online (Sandbox Code Playgroud)

如果我们现在激活优惠券,这将提高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();
}
Run Code Online (Sandbox Code Playgroud)

Mik*_*eSW 12

是的,你误解了一些事情.域事件不像C#事件,它是关于域中发生了什么变化的消息.一个规则是事件是发生的事情,它发生在过去.因此,事件处理程序根本不能(它不应该)更改事件,就像改变过去一样.

You CouponActivatedHandler至少应该让User实体形成一个存储库,然后用信用点数更新它,然后保存它,然后发布一个UserCreditsAdded事件.更好的是,处理程序应该只创建并发送命令AddCreditsToUser.

使用域事件模式,操作只是一个命令 - >事件 - >命令 - >事件等链.事件处理程序通常是一个服务(或一个方法),它只关注该位.事件的发送者对处理程序一无所知,反之亦然.

作为一个拇指规则,Domain对象在其状态发生变化时会生成一个事件.服务将获取这些事件然后将它们发送到服务总线(对于一个简单的应用程序,一个DI容器就足够了),它将发布它们以供任何感兴趣的人处理(这意味着来自本地应用程序或订阅该总线的其他应用程序的服务) ).

永远不要忘记,域事件是在执行应用程序架构时使用的高级模式,它不仅仅是执行对象事件的另一种方式(如C#的事件).

  • 例子,不是真的.但是开始这样做,练习就是完美的.做,注意什么感觉很麻烦,尝试重构等.DDD是通过这样做来学习的. (4认同)