域事件处理程序什么时候开始发挥作用?

时间:2011-12-06 10:48:42

标签: c# events domain-driven-design cqrs domain-events

我有一个简单的两个聚合根和一个常规实体的检查域。 TenantUserGroupUser在此特定示例中TenantUser弥补了两个 AggregateRoots

当从UI /服务层收到命令时,它会到达操作只写域的命令处理程序。

您可以说User根本不应该是 AggregateRoot ,但由于它会被其他人引用,因此它不能是常规实体。 (是?)

这两个AggregateRoot需要进行通信。如果不属于User,则无法创建UserGroupTenantUser.Create(TenantId, UserGroupId)的有界上下文中的实体。据推测,我们可以通过构造函数创建一个用户,因为它是一个简单的约束。 DomainEvent

它使用Date,AggregateVersion和AggregateId(用户)生成Tenant。现在我们到了模糊的部分。

打开将此事件提交到商店,此事件将被广播到总线(内存,无论如何)。在这一点上,域的事件处理程序(类似于命令处理程序)捕获用户创建并通知/操纵UserGroup的{​​{1}}以添加UserId

我对解决这个问题的想法是否会走向完全错误的方向?

1 个答案:

答案 0 :(得分:7)

您可能正在寻找Saga

简单地说:一个saga可以实现为一个事件处理程序,它可以侦听特定事件并向不同的聚合根或甚至跨上下文边界发出命令。

在您的情况下,它可能如下所示:

public class RegisterUserSaga : Handles<UserCreated>
{
    public void Handle<UserCreated>(UserCreated evnt) {
        var tenantId = // you probably know how to find this
        var groupId =  // same here
        var command = new RegisterUserForTenant(evnt.UserId, tenantId, groupId);
        Bus.Send(command);
    }
}

通过Rinat Abdullin在this article了解更多关于传奇的信息,或者通过Udi Dahan观看"CQRS, race conditions, and sagas - oh my!"

<强>更新

在我们在评论中进行了扩展讨论之后,我将尝试从不同的角度展示这是如何工作的(前面的伪代码)。这有望为可能的解决方案提供更多信息:

// Aggregates:

Tenant
    Guid TenantId
    List<Guid> UserGroups

UserGroup
    Guid UserGroupId
    List<Guid> Users

User
    Guid UserId
    Some more details

// Commands:

RequestRegistration(userId, userGroupId, user details)
CreateUser(userId, user details)
AddUserToGroup(userId, userGroupId)

// The initial command would be:

RequestRegistration (leading to a RegistrationRequested event)

// The Saga handles the RegistrationRequested and all subsequent events

UserRegistrationSaga 
    Handle(RegistrationRequested)
    -> send CreateUser command (which eventually leads to a UserCreated event)
    Handle(UserCreated)
    -> send AddUserToGroup command (-> UserAddedToGroup event)
    Handle(UserAddedToGroup)
    -> Done