2011-11-15 14 views
5

Ho una domanda riguardante gli aggiornamenti di più aggregati in una singola transazione utilizzando JOliver's Event Store. Come ho capito, ogni aggregato dovrebbe avere il proprio flusso di eventi. Ora, mentre molti gestori di comandi caricano solo un singolo aggregato e aggiornano solo quell'aggregato (ad esempio, salvano gli eventi per tali aggregati), posso immaginare che ci saranno gestori di comandi che devono aggiornare più aggregati. E, naturalmente, mi piacerebbe farlo in modo transazionale.Aggiornamento di più aggregati utilizzando JOliver EventStore

Tuttavia, non vedo come potrei farlo con l'Event Store. La memorizzazione degli eventi viene effettuata chiamando lo CommitChanges() su un flusso di eventi. Se stiamo aggiornando più aggregati, avremo più stream di eventi e quindi più chiamate a CommitChanges(). L'unico modo per renderlo transazionale è quello di racchiuderlo in un TransactionScope, ma ciò non ha molto senso, poiché la tecnologia di archiviazione sottostante potrebbe non supportare le transazioni. Così io alla fine con questo codice, che è sicuramente non è quello che sto cercando:

 Guid aggregateGuid1 = Guid.NewGuid(); 
     Guid aggregateGuid2 = Guid.NewGuid(); 
     Guid commitGuid = Guid.NewGuid(); 

     var stream = store.OpenStream(aggregateGuid1, 0, int.MaxValue); 
     stream.Add(new EventMessage() { Body = new MonitorDisabled { MonitorGuid = aggregateGuid1, User = "A" } }); 
     stream.CommitChanges(commitGuid); 

     stream = store.OpenStream(aggregateGuid2, 0, int.MaxValue); 
     stream.Add(new EventMessage() { Body = new MonitorEnabled { MonitorGuid = aggregateGuid2, User = "B" } }); 
     // Can't commit twice with the same commit id, what if fails after first one? No way for the store to know it had to write the second part of the commit. 
     stream.CommitChanges(commitGuid); 

Questo mi fa sentire che sono qualcosa di completamente mancante su come deve essere utilizzato l'evento di archivio. Qualcuno potrebbe aiutarmi qui? Molte grazie!

risposta

3

Non posso parlare per John Oliver, ma penso che la risposta sia "non farlo". Gli aggregati sono confini delle transazioni. Se è necessario coordinare il commit di più aggregati, è necessario un processo di coordinamento esplicito, come una saga, che esegua e, se necessario, annulla gli eventi pertinenti.

+0

Ora, supponiamo che siamo in hosting business e stiamo gestendo server in stanze in rack nei data center. E ogni server è monitorato da una serie di monitor, chiamiamolo una configurazione del monitor. Ora, vogliamo disabilitare i monitor durante la manutenzione. E, possiamo fare manutenzione su server, rack, stanze o centri dati completi. La configurazione del monitor è un aggregato naturale per i monitor per un server, vogliamo generare eventi MonitorDisabled sull'aggregato di configurazione del monitor quando mettiamo un rack in manutenzione. Dove definiamo il limite TX? Il server, il rack, la stanza, ...? – Jochen

+3

Se si dispone di limiti di trascrizione che attraversano i confini aggregati, si sta facendo male. Per definizione, un aggregato è un confine transazionale e il tuo modello dovrebbe riflettere questo. –

8

Un aggregato definisce un limite di transazione.

Se è necessario eseguire transazioni tra aggregati, è necessario rivedere i propri aggregati e, eventualmente, riprogettarli.

Nei casi in cui un'operazione (comando) interessa più di un aggregato e si è sicuri che i propri aggregati siano ben progettati e mappati ai limiti di consistenza reali nel proprio dominio, eventual consitency potrebbe essere quello che si sta cercando. Basta inviare un comando a ciascun aggregato e avere due transazioni, una per ciascuna di esse. Se non ritieni che la coerenza finale sia giusta per il tuo caso, temo che sia di nuovo al tavolo da disegno.

+0

Hmm, facciamo un esempio classico: transazioni bancarie. Un account è un aggregato e devo trasferire denaro da un account all'altro. Ciò significa che devo memorizzare un evento come MoneySent su un account e un evento MoneyReceived sull'altro account. Ciò rappresenta per me uno scenario legittimo per un aggiornamento transazionale su 2 aggregati. Cosa mi manca? – Jochen

+0

Un'altra lettura mi dice che questo caso dovrebbe essere implementato usando azioni di compensazione. Dovrò fare ancora un po 'di lettura su questo. Essere in grado di scrivere una singola transazione su 2 aggregati sembra essere una soluzione molto più pulita (quando controlli entrambi gli account nel tuo dominio). – Jochen

+0

Con DDD ed ES/CQRS guadagnerai qualcosa e perderai qualcosa. È il [CAP Teorem] (http://en.wikipedia.org/wiki/CAP_theorem) Ottieni partizionamento, scalabilità, alta disponibilità, ecc., Ma perdi consistenza. Beh, in realtà non lo perdi, lo fai solo in ritardo. Supponendo che i guadagni superino le perdite che ti rimangono nel trovare soluzioni per i casi che descrivi, e come hai già scoperto, di solito ci sono soluzioni comunemente accettate come le azioni di compensazione. –

0

A volte può essere più facile sfruttare le transazioni distribuite in questi scenari se l'infrastruttura li supporta.

TransactionScope con EventStore:

L'EventStore di default sopprime qualsiasi transazione di ambiente creato da NServiceBus prima di commettere le modifiche al database. Tuttavia se si utilizza una coda e un database che supporta le transazioni distribuite (MSMQ, SQL Server, Raven ecc.), È possibile modificare il TransactionScopeOption di EventStore su Obbligatorio. Ciò assicurerà che l'EventStore presenti nella transazione ambientale, di cui verrà distribuito utilizzando MSDTC e la coda di messaggi e il database verranno mantenuti sincronizzati. - NES Documentation

transazioni di documenti trasversali con RavenDB:

Potrai estrae le transazioni dai miei morti, mani fredde e spezzate - Ayende