如何使用Akka.net和Akka.Persistence应用CQRS的EventBus

时间:2017-01-28 18:34:05

标签: c# cqrs akka.net akka.net-persistence

我对如何在Akka.net上应用CQRS和事件采购感兴趣。我已经注意到Akka.Persistence提供了ES部分。

据我所知,Command Handler和AggergateRoot可以由一个类中的ReceivePersistentActor表示。我有一个基类(这里没有完成)......

public abstract class AggregateRoot<TState> : ReceivePersistentActor
{
    private readonly Func<TState, bool> shouldSafeSnapShot;

    protected AggregateRoot(TState state, Func<TState, bool> shouldSafeSnapShot)
    {
        if (state == null) throw new ArgumentNullException(nameof(state));
        if (shouldSafeSnapShot == null) throw new ArgumentNullException(nameof(shouldSafeSnapShot));

        var path = this.Self.Path;
        this.PersistenceId = $"{path.Parent.Name}/{path.Name}";
        this.State = state;
        this.shouldSafeSnapShot = shouldSafeSnapShot;
    }

    public sealed override string PersistenceId { get; }

    protected TState State { get; }

    protected void Emit<TEvent>(TEvent e, Action<TEvent> apply = null)
    {
        this.Persist(e,
            @event =>
                {
                    // update state
                    apply?.Invoke(@event);

                    // safe snapshot or so...
                    this.SaveSnapshotIfRequired();

                    // publish to event bus?
                    Context.System.EventStream.Publish(e);
                });
    }

    private void SaveSnapshotIfRequired()
    {
        var state = this.State;

        if (this.shouldSafeSnapShot(state))
        {
            this.SaveSnapshot(state);
        }
    }
}

不是我想通过EventBus发送事件,Akka似乎带来了一些内容,它可能适合其他CQRS框架的方式(NCqrs中的EventBus抽象,{ {3}})或Gregory Young Inceptum的简单样本。

// publish to event bus?
Context.System.EventStream.Publish(e);

然而,在AggregateRoot域对象actor中了解eventbus的实现似乎很奇怪。我可以把责任交给一个演员孩子或注入类似用于切换实现的 IAbstractEventBus 接口,但我不认为它应该由AggregateRoot负责发布事件之后坚持并告知所有订阅者,对吧!? 但ES部分似乎与演员联系在一起。

使用Akka.net和Akka.Persistence的典型方法是什么?任何设计思路如何拆分?

我偶然发现了Akka.net的 IEventAdapter 界面,但我不确定这会把我带到正确的道路(或黑暗的一面)......

public class SomeEventAdapter : IEventAdapter
{
    public object ToJournal(object evt)
    {
        return evt; // should I add the event emitter here?
        // but is this already stored now? hmmm
    }
}

2 个答案:

答案 0 :(得分:2)

在一个理想主义的世界中,你的命令处理程序可以负责跟踪事件以推送到事件总线,并在命令完全处理后没有任何问题时将它们保持某种形式的sotrage - 这可以确保你不会过早地在聚合方法完成所有事件之前推送事件。

在Akka.net中,您可以在将命令传递给聚合时将Tell更改为Ask(假设您的处理程序为了纯度而将您的处理程序与聚合分开)并让聚合返回它想要引发的事件和持久化回处理程序,所以处理程序然后可以发送并持久化它们(可能间接地自己...可能通过UOW上的某种形式)...但是这意味着没有其他命令可以处理命令处理程序配置处理直到你的聚合完成...即使其他聚合可能由一个处理程序提供服务 - 毕竟问问锁...告诉没有。

在其他一些系统中,持久层本身可以充当事件总线。 Greg Young的EventStore就是其中之一......你完全不关心你的申请。

这是理想主义理论与现实实施和框架限制的挑战。

在一个理想主义的世界中,聚合根本身并不关心存储它所引发的事件......它的目的(有人会说)是从提供的事件集合中提升自身,引发事件并拥有其他组件管理这些事件的广播,持久性和查询(补充水分)。我知道Akka.net为它的演员提供了一个很好的数据库访问/持久层...这很好......但是它偏离了纯粹的SOLID CQRS实现。因此,您可以开始在实施过程中混淆水域,这就是您所处的位置。

这远非理想,只有一个选项

    protected virtual void Execute<TCommand>(Action<TCommand> action, TCommand command)
    {
        UnitOfWork.Add(this);
        try
        {
            action(command);

            UnitOfWork.Commit();

            Sender.Tell(true, Self);
        }
        catch(Exception exception)
        {
            Logger.LogError("Executing an Akka.net request failed.", exception: exception);
            Sender.Tell(false, Self);
            throw;
        }
    }

然后可以通过

连接
    Receive<SayHelloWorldCommand>(command => Execute(SayHello, command));

这取自cqrs.net在聚合中使用工作单元(U.O.W.)而不是将其传递回处理程序。这是他们已经做出的妥协,至少可以将上述问题直接排除在汇总之外,并保持某种形式的SOLID实施。

答案 1 :(得分:1)

@Beachwalker我在https://github.com/thangchung/magazine-website-akka使用CQRS方法实现了。我不知道它可以帮到你吗?但希望你能找到有用的东西。我将继续为它添加更多功能和特性。