2010-05-25 10 views
5

Estoy trabajando en una implementación de CQRS event-sourced, usando DDD en la capa de aplicación/dominio. Tengo un modelo de objetos que tiene este aspecto:¿Debe una raíz agregada de orígenes de eventos tener acceso al repositorio de abastecimiento de eventos?

public class Person : AggregateRootBase 
{ 
    private Guid? _bookingId; 

    public Person(Identification identification) 
    { 
     Apply(new PersonCreatedEvent(identification)); 
    } 

    public Booking CreateBooking() { 
     // Enforce Person invariants 
     var booking = new Booking(); 
     Apply(new PersonBookedEvent(booking.Id)); 
     return booking; 
    } 

    public void Release() { 
     // Enforce Person invariants 
     // Should we load the booking here from the aggregate repository? 
     // We need to ensure that booking is released as well. 
     var booking = BookingRepository.Load(_bookingId); 
     booking.Release(); 
     Apply(new PersonReleasedEvent(_bookingId)); 
    } 

    [EventHandler] 
    public void Handle(PersonBookedEvent @event) { _bookingId = @event.BookingId; } 

    [EventHandler] 
    public void Handle(PersonReleasedEvent @event) { _bookingId = null; } 
} 

public class Booking : AggregateRootBase 
{ 
    private DateTime _bookingDate; 
    private DateTime? _releaseDate; 

    public Booking() 
    { 
     //Enforce invariants 
     Apply(new BookingCreatedEvent()); 
    } 

    public void Release() 
    { 
     //Enforce invariants 
     Apply(new BookingReleasedEvent()); 
    } 

    [EventHandler] 
    public void Handle(BookingCreatedEvent @event) { _bookingDate = SystemTime.Now(); } 
    [EventHandler] 
    public void Handle(BookingReleasedEvent @event) { _releaseDate = SystemTime.Now(); } 
    // Some other business activities unrelated to a person 
} 

Con mi comprensión de DDD hasta ahora, tanto en persona y reserva son raíces agregados separados por dos razones:

  1. hay momentos en los componentes de negocio se extraiga los objetos de Booking por separado de la base de datos. (es decir, una persona que ha sido liberada tiene una reserva previa modificada debido a información incorrecta).
  2. No debe existir conflicto de bloqueo entre Persona y Reserva cada vez que se necesite actualizar una Reserva.

Otro requisito comercial es que una Reservación nunca puede ocurrir para una Persona más de una vez a la vez. Debido a esto, me preocupa consultar la base de datos de consulta en el lado de lectura, ya que podría existir una cierta incoherencia allí (debido al uso de CQRS y tener una base de datos de lectura eventualmente consistente).

¿Se les debe permitir a las raíces agregadas consultar el almacén de respaldo del origen del evento por id para los objetos (cargarlos de forma diferida según sea necesario)? ¿Hay otras vías de implementación que tengan más sentido?

+0

"Persona pública (identificación de Identificación) { Aplicar (nueva PersonCreatedEvent (identificación));} " qué sucede si usted está construyendo el objeto de la memoria, no se va a crear una nueva persona, sino que será la publicación un nuevo evento. Me gusta la forma en que defines los controladores de eventos.Entonces, sabiendo que el estado de un objeto solo puede cambiar al aplicar un comando a un repositorio, quizás también deba agregar controladores de comando, a menos que esté basado en un evento completo, que en mi humilde opinión no tiene mucho sentido. – Marco

+0

Lo que no puedo entender es por qué estás manejando un evento que tu propio repositorio debería publicar. "BookingCreatedEvent" – Marco

Respuesta

10

En primer lugar, ¿realmente necesita fuentes de eventos en su caso? Me parece bastante simple. El abastecimiento de eventos tiene ventajas y desventajas. Si bien le proporciona una pista de auditoría gratuita y hace que su modelo de dominio sea más expresivo, complica la solución.

OK, supongo que en este punto usted pensó en su decisión y está decidido a seguir con el abastecimiento de eventos. Creo que te estás perdiendo el concepto de mensajería como medio de comunicación entre los agregados. Se describe mejor en Pat Helland's paper (que, por cierto, no se trata de DDD o Event Sourcing, sino de escalabilidad).

La idea es que los agregados pueden enviarse mensajes entre sí para forzar algún comportamiento. No puede haber interacción sincrónica (a.k.a llamada a un método) entre los agregados porque esto introduciría problemas de consistencia.

En su ejemplo, una persona AR enviaría un mensaje de reserva a una reserva AR. Este mensaje se transporta de manera asincrónica y confiable. Booking AR manejaría este mensaje y si ya está siendo publicado por otra persona, respondería con un mensaje de Reserva rechazada. De lo contrario, enviaría ReservationConfirmed. Estos mensajes deberían ser manejados por la persona AR. Probablemente, generarían otro evento más que se transformaría en correo electrónico enviado al cliente o algo así.

No hay necesidad de recuperar datos de consulta en el modelo. Solo mensajes. Si desea un ejemplo, puede descargar las fuentes de la rama "Mensajes" del proyecto Ncqrs y echar un vistazo a la clase ScenarioTest. Demuestra mensajería entre AR que utilizan muestras de Cargo y HandlingEvent del Libro azul.

¿Responde a esta pregunta?

+1

¿Con qué tipo de problemas de coherencia se encontraría si forzara el manejo sincrónico? Parece que simplemente sería un problema de escalabilidad para mí, pero me puede estar perdiendo algo. –

+0

Supuse que el modelo de almacenamiento no admite el almacenamiento de transacciones de agregación cruzada. Si es compatible, tiene razón en que las llamadas a métodos normales estarían bien (al menos en lo que respecta a la coherencia). –

+0

Después de echar un vistazo al artículo de Pat Helland, veo de dónde vienes. En este sistema en particular, no me preocupa la escalabilidad casi infinita (aunque sería un buen problema tenerla). Como detalle de implementación, estamos usando MSMQ. Creo que 2PC usando MSMQ es aceptable en nuestro escenario. Me gusta la idea, sin embargo, y me ha dado algo para pensar. –

Cuestiones relacionadas