2011-12-06 20 views
10

Tengo un dominio de examen simple de dos raíces agregadas y una entidad regular. Tenant, UserGroup y User donde en este ejemplo particular, el Tenant y User compensar los dos AggregateRoots.¿Cuándo entran en juego los controladores de eventos de dominio?

Cuando se recibe un comando de la capa de UI/Servicio, llega al controlador de comandos que manipula el dominio de solo escritura.

Se podría decir que User no se supone que es un AggregateRoot en absoluto, pero ya que será referenciado por otros, no puede haber una entidad regular. (¿sí?)

Estos dos agregados necesitan comunicarse. No se puede crear un User sin pertenecer a un UserGroup, que es una entidad en el contexto delimitado de Tenant. Presumiblemente, podemos crear, dado que es una restricción simple, un Usuario a través del constructor. User.Create(TenantId, UserGroupId)

Genera un DomainEvent con Fecha, AgregadoVersión y AgregadoId (del usuario). Ahora llegamos a las partes borrosas.

Abra la entrada de este evento en la tienda, este evento se retransmite en el bus (memoria, lo que sea). Es este el punto donde los manejadores de eventos del dominio, similares a los manejadores de comandos, atrapan al usuario creado y notifican/manipulan Tenant's UserGroup para agregar el UserId?

¿Mi idea de resolver esto va en la dirección totalmente equivocada?

+1

Puede que simplifique su problema reconsiderando su modelo. Los eventos de dominio son útiles cuando se requiere una comunicación de contexto cruzada o cuando se desea invocar la lógica de negocios, como la correspondencia por correo electrónico, en respuesta a eventos de dominio. ¿Su modelo requiere contextos acotados múltiples? ¿Puede contener todas las operaciones requeridas en la capa de servicio, como un servicio de usuario? – eulerfx

+0

Uno siempre debe verificar si no puede modelar el problema. En este caso, sin embargo, es un ejemplo artificial para obtener respuestas sobre cómo se supone que debe ocurrir la comunicación entre diferentes AR delimitadas. – Jaapjan

+0

"¿Qué comportamiento futuro en UserGroup requiere UserId?" es una pregunta muy importante que debe hacerse (de lo contrario, ¿por qué se comunicaría entre agregados?).Hacer que el inquilino forme parte de su dominio parece ... extraño si su dominio no se trata de tenencia múltiple (no debe confundirse con el arrendamiento múltiple como requisito no funcional). Usa tus objetos de dominio para colaborar combinados con algunos TDA si están en el mismo BC. –

Respuesta

6

A Saga podría ser lo que estás buscando.

En pocas palabras: una saga se puede implementar como un controlador de eventos que escucha eventos específicos y emite comandos a diferentes raíces agregadas, o incluso a través de los límites de contexto.

En su caso, podría tener este aspecto:

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

Más información acerca de las sagas en this article de Rinat Abdullin o ver "CQRS, race conditions, and sagas - oh my!" por Udi Dahan

Actualización:

Después de nuestra extendida discusión en los comentarios Trataré de mostrar cómo esto podría funcionar desde un ángulo diferente (pseudo código adelante). Es de esperar que arroje más luz sobre una posible solución:

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

Si es posible, ¿puede arrojar algo de luz si tengo en mente la secuencia correcta? ¿Cambiar AR, comprometerse con ES, transmitir eventos, Saga recoge y cambia otros AR, etc.? Y esto necesitaría algún mecanismo para despertar, instanciar y llamar periódicamente a Saga, ¿sí? – Jaapjan

+0

Esta es exactamente la secuencia que tenía en mente. Una implementación básica de saga sin estado podría simplemente registrarse al inicio como cualquier otro controlador de eventos. Una implementación más sofisticada puede incluir persistencia. Para empezar, una simple implementación sin estado debería ser suficiente para acostumbrarse al concepto. Para obtener variantes más complejas, eche un vistazo a las sagas en NServiceBus y Common Domain/Event Store de Jonathan Oliver en GitHub. –

+0

En este caso particular, dado que el evento UserCreated podría no ser recogido inmediatamente por la Saga, estamos tratando con la consistencia eventual en el dominio, ¿sí? Dado que un usuario podría haber sido 'agregado' al grupo de un inquilino pero el inquilino AR aún no se ha actualizado? – Jaapjan

Cuestiones relacionadas