Akka.Net发布和订阅域事件

时间:2017-01-14 03:45:31

标签: c# asp.net-web-api akka.net

我开始用boot-camp查看Akka.Net框架。

我可以理解基本的Actor概念和使用事件源的持久性。

我很难理解域事件将如何被其他参与者发送和接收。

限制单个系统本地部署的Actors和No DI容器和 使用c#/ ASPP.NET API,我将每个AgreegateRoot分成它自己的项目

我正在概念化像

这样的东西
  • ManagerActor

    • -AggregateRoot
      • - ChildActor 1
      • - ChildActor 2
      • - ChildActor n
    • -ValidationActor

Manager Actor将收到命令消息并通过验证过程,如果验证将被发送到AggregateRoot Actor。将在根或子actor中生成事件。

请告知以下内容:

要在实体内部发布类似于eventbus的事件,我可以使用以下语法吗?

Context.System.EventStream.Publish(MyEvent);

订阅活动我明白语法是

System.EventStream.Subscribe( subscriber,MyEvent)

我希望Actor发布的事件由Handlers(其他Actors)处理,其中当前的AggregateRoot Actor或Child Entity内部应该不知道。

这是我完全陷入困境的地方。这是如何实现的?

System.EventStream.Subscribe中的

订户是IActorRef。为了得到这个,我需要了解课程。

我应该创建一个启动引导程序,它将引用所有项目/ AggregateRoots并在那里构建消息类型的订阅吗?

我试图找到博客或文章,但没有太多运气。

先谢谢。

2 个答案:

答案 0 :(得分:1)

另一个解决方案是使用Akka.Cluster.Sharding,这对于这个用例是完美的。然而;我将承认群集分片不是一个初学者主题。群集分片允许您设置如何通过道具创建actor以及将消息映射到特定的实体"的策略。

以下是我上面链接的博客文章中示例的角色分片设置示例:

using(var system = ActorSystem.Create("cluster-system"))
{
    var sharding = ClusterSharding.Get(system);
    var shardRegion = sharding.Start(
        typeName: nameof(MyActor), 
        entityProps: Props.Create<MyActor>(), // the Props used to create entities
        settings: ClusterShardingSettings.Create(system),
        messageExtractor: new MessageExtractor(maxNumberOfNodes * 10)
    );

    // ... etc
}

然后你可以发送这样的信息:

region.Tell(new ShardEnvelope("<entity-id>", new MyMessage()));

消息提取器

上面提到的消息提取器是使这一切全部工作的关键组件。消息提取器允许您将消息映射到特定实体(实际上是entityId)。因此,如果您的消息例如都具有目标实体的Id,那么这变得简单。这是一个示例消息提取器(再次来自petabridge博客文章和Akka.Net代码库):

public sealed class MessageExtractor : HashCodeMessageExtractor
{
    public MessageExtractor(int maxNumberOfShards) : base(maxNumberOfShards) { }
    public override string EntityId(object message) => 
        (message as ShardEnvelope)?.EntityId;
    public override object EntityMessage(object message) => 
        (message as ShardEnvelope)?.Payload;
}

public sealed class ShardEnvelope
{
    public readonly string EntityId;
    public readonly object Payload;

    public ShardEnvelope(string entityId, object payload)
    {
        EntityId = entityId;
        Payload = payload;
    }
}

我承认这似乎有点过分,如果在Akka.Net中为虚拟演员提供更好的一流支持,我认为这将得到纠正。

答案 1 :(得分:0)

  

我将每个AgreegateRoot分成了自己的项目

我不会为每个AggregateRoot创建一个单独的项目,这对我来说似乎有些过分。你有什么收获的?你可以使用一个类/ actor,不需要一个完全独立的项目。

听起来您可能会对如何引用代表您的事件的C#类型感到困惑,如果每个AggregateRoot都有一个单独的项目,那么这并不令人惊讶 - 您将很快遇到循环引用。尝试从单个项目开始,使用文件夹分隔有界上下文。在每个文件夹中,创建您需要的任何聚合以及它们负责的任何事件。这样,所有参与者都可以看到所有事件类型。一旦这种增长/变得无法管理,您可以将其分解为单独的项目,这类似于:

  • MyApp.BoundedContext1
  • MyApp.BoundedContext1.Events
  • MyApp.BoundedContext2
  • MyApp.BoundedContext2.Events

请注意,您的演员生成和订阅的事件代表整个系统中的一种公共合同/ API。将它们作为单独的DLL如上所述避免了循环引用(因为.Events项目不引用任何东西)。因此,在此结构中,MyApp.BoundedContext1可以引用MyApp.BoundedContext1.Events并发布它们。 MyApp.BoundedContext2 也可以引用MyApp.BoundedContext1.Events 并订阅它们。

  

我希望Actor发布的事件由Handlers(其他Actors)处理,其中当前的AggregateRoot Actor或Child Entity内部应该不知道。

     

这是我完全陷入困境的地方。这是如何实现的?

您的发布商不需要订阅者的知识。发布者只是向EventStream发布消息。定义参与者和事件类型的上下文应该是相同的(即发布者应该拥有他们的事件类型)。例如,如果您有ValidationActor发布ThingValidated个事件,则它们都应位于相同的上下文中。

  System.EventStream.Subscribe中的

订户是IActorRef。为了得到这个,我需要了解课程。

哪个班?订户已经了解自己。您只需使用Self即可获得IActorRef。如果您的意思是事件类,那么请参阅上面有关如何构建项目以引用它的内容。

一旦创建了一个actor,它就可以注册它感兴趣的任何事件类型 - 如果你需要,可以将这些代码放在actor本身的初始化代码中,或者如果你有某种类型的单身演员,如果你愿意,可以使用bootstrapper函数。