2010-05-25 3 views
5

Sto lavorando a un'implementazione CQRS event-sourced, utilizzando DDD nel livello applicazione/dominio. Ho un modello a oggetti che assomiglia a questo:Una root aggregata event-source può accedere al repository di sourcing degli eventi?

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 la mia comprensione delle DDD finora, sia persona e prenotazioni sono separate radici di aggregazione per due motivi:

  1. Ci sono momenti in cui i componenti d'affari estrarre gli oggetti di prenotazione separatamente dal database. (ad esempio, una persona che è stata rilasciata ha modificato una prenotazione precedente a causa di informazioni errate).
  2. Non ci devono essere conflitti di blocco tra Persona e Prenotazione ogni volta che è necessario aggiornare una prenotazione.

Un altro requisito aziendale è che una prenotazione non può mai verificarsi per una persona più di una volta alla volta. A causa di ciò, sono preoccupato di interrogare il database di query sul lato di lettura in quanto potrebbe esserci qualche incongruenza (a causa dell'uso di CQRS e di un database di lettura eventualmente coerente).

Nel caso in cui le radici aggregate possano interrogare il backing store basato su evento con id per gli oggetti (caricandoli lentamente se necessario)? Ci sono altre vie di implementazione che avrebbero più senso?

+0

"persona pubblica (identificazione Identification) { Applicare (nuova PersonCreatedEvent (identificazione));} " cosa succede se si sta costruendo l'oggetto dalla memoria, non si sta creando una nuova persona, ma vi sarà la pubblicazione un nuovo evento. Mi piace il modo in cui definisci i gestori di eventi.Quindi, sapendo che lo stato di un oggetto può essere modificato solo quando si applica un comando a un repository, forse dovresti aggiungere anche i gestori di comandi, a meno che non si stia realizzando un evento completo, che IMHO non ha molto senso. – Marco

+0

Quello che non riesco a capire è perché stai gestendo un evento che il tuo repository dovrebbe pubblicare? "BookingCreatedEvent" – Marco

risposta

10

Prima di tutto, hai veramente bisogno di un event sourcing nel tuo caso? Mi sembra abbastanza semplice. Il sourcing di eventi ha sia vantaggi che svantaggi. Mentre ti offre un audit trail gratuito e rende il tuo modello di dominio più espressivo, complica la soluzione.

OK, presumo che a questo punto hai pensato alla tua decisione e sei determinato a rimanere con il sourcing di eventi. Penso che manchi il concetto di messaggistica come mezzo di comunicazione tra aggregati. È descritto meglio in Pat Helland's paper (che è, a proposito, non su DDD o Event Sourcing, ma sulla scalabilità).

L'idea è che gli aggregati possono inviare messaggi l'uno all'altro per forzare alcuni comportamenti. Non può esserci un'interazione sincrona (a.k.a chiamata di metodo) tra aggregati perché ciò introdurrebbe problemi di coerenza.

Nel tuo esempio, una Persona AR invierà un messaggio di prenotazione a un AR di prenotazione. Questo messaggio verrebbe trasportato in modo asincrono e affidabile. Prenotazione AR gestirà questo messaggio e se è già prenotato da un'altra persona, risponderebbe con il messaggio ReservationRejected. Altrimenti, invierà ReservationConfirmed. Questi messaggi dovrebbero essere gestiti da Person AR. Probabilmente, avrebbero generato ancora un altro evento che sarebbe stato trasformato in e-mail inviata al cliente o qualcosa del genere.

Non è necessario recuperare i dati della query nel modello. Solo messaggistica Se si desidera un esempio, è possibile scaricare le origini del ramo "Messaggistica" del progetto Ncqrs e dare un'occhiata alla classe ScenarioTest. Dimostra la messaggistica tra AR che utilizza il campione Cargo e HandlingEvent dal Blue Book.

Questo risponde alla tua domanda?

+1

In quali tipi di problemi di coerenza verrebbero eseguiti se si forzasse la gestione sincrona? Sembra che sarebbe semplicemente una questione di scalabilità per me, ma potrei mancare qualcosa. –

+0

Supponevo che il modello di archiviazione non supporti la memorizzazione di transazioni cross-aggregate. Se supporta, hai ragione che le normali chiamate di metodo sarebbero ok (almeno per quanto riguarda la coerenza). –

+0

Dopo aver dato un'occhiata al documento di Pat Helland, vedo da dove vieni. In questo particolare sistema, non mi interessa la scalabilità quasi infinita (anche se sarebbe un bel problema avere). Come dettaglio di implementazione, stiamo utilizzando MSMQ. Credo che 2PC utilizzando MSMQ sia accettabile nel nostro scenario. Mi piace l'idea, però, e mi ha dato spunti di riflessione. –