Когда выполняются обработчики событий домена?

У меня есть простая область описания двух совокупных корней и одного регулярного объекта. Tenant, UserGroup и User, где в этом конкретном примере Tenant и User составляют два AggregateRoots.

Когда команда получена с уровня пользовательского интерфейса/службы, она достигает обработчика команд, который управляет только доменом записи.

Можно сказать, что User не должен быть AggregateRoot вообще, но поскольку на него будут ссылаться другие, он не может быть обычным сущностью. (Да?)

Эти два элемента AggregateRoots должны взаимодействовать. A User не может быть создан без принадлежности к UserGroup, который является сущностью в ограниченном контексте Tenant. Предположительно, мы можем создать, поскольку это простое ограничение, Пользователь через конструктор. User.Create(TenantId, UserGroupId)

Он генерирует DomainEvent с Date, AggregateVersion и AggregateId (пользователя). Теперь мы попадаем в размытые части.

Откройте это событие в хранилище, это событие транслируется на шину (память, что угодно). Это точка, в которой обработчики событий домена, похожие на обработчики команд, ловят созданного пользователя и уведомляют/управляют Tenant UserGroup, чтобы добавить UserId?

Разве мои мысли о том, чтобы решить это в совершенно неправильном направлении?

Ответ 1

A 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);
    }
}

Подробнее о сагах в этой статье Рината Абдуллина или смотрите "CQRS, условия гонки и саги - о мой!" от Udi Dahan

Update:

После нашего расширенного обсуждения в комментариях я попытаюсь показать, как это может работать под другим углом (псевдокод впереди). Это, надеюсь, проливает свет на возможное решение:

// 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