Domanda

Sono ancora alle prese con quello che deve essere di base (e risolto) le questioni relative alla architettura in stile CQRS:

Come si fa a implementare regole di business che si basano su un set di radici di aggregazione?

Prendiamo, come esempio, una richiesta di prenotazione. Essa può consentire di prenotare i biglietti per un concerto, sedili per un film o un tavolo al ristorante. In tutti i casi, c'è solo sarà un numero limitato di 'oggetti' in vendita.

Immaginiamo che l'evento o luogo è molto popolare. Quando le vendite aprono per un nuovo slot evento o il tempo, le prenotazioni iniziano ad arrivare molto rapidamente - forse molti al secondo

.

Dal punto di vista di query che può scalare in maniera massiccia, e le prenotazioni sono messo in una coda per essere gestita in modo asincrono da un componente autonomo. In un primo momento, quando tiriamo fuori prenotazione comandi dalla coda di noi li accetta, ma a un certo momento dovremo iniziare a respingendo il resto .

Come facciamo a sapere quando si raggiunge il limite?

Per ogni prenotazione di comando dovremmo interrogare una sorta di negozio per capire se siamo in grado di accogliere la richiesta. Questo significa che avremo bisogno di sapere quante prenotazioni abbiamo già ricevuto in quel momento.

Tuttavia, se il dominio Store è un dati non relazionali memorizzano, come ad esempio Windows Azure Storage Table, non possiamo benissimo fare un SELECT COUNT(*) FROM ...

Una possibilità sarebbe quella di mantenere un separato Aggregate Root che semplicemente registra il conteggio corrente, in questo modo:

  • AR: Prenotazione (?? Che quanti)
  • AR: Evento / Ora Slot / Data (conteggio aggregato)

Il secondo aggregato Root sarebbe un'aggregazione denormalizzato del primo, ma quando l'archivio dati sottostante non supporta le transazioni, allora è molto probabile che questi possono ottenere fuori sincronia in scenari ad alto volume (che è quello che abbiamo stanno cercando di indirizzo in primo luogo).

Una soluzione possibile è quella di serialize la gestione della prenotazione comandi in modo che solo una alla volta viene manipolato, ma questo va contro i nostri obiettivi di scalabilità (e ridondanza).

Tali scenari mi ricordano standard "out of stock" scenari, ma la differenza è che non possiamo molto ben messo la riserva in ordine indietro. Una volta che un evento è sold out, è venduto fuori, quindi non riesco a vedere quale sarebbe un'azione di compensazione.

Come gestiamo tali scenari?

È stato utile?

Soluzione

Dopo aver riflettuto su questo per un po 'finalmente mi resi conto che il problema di fondo è meno legato alla CQRS quanto lo sia per il non trasactional la natura dei servizi REST disparati.

In realtà tutto si riduce a questo problema:? Se è necessario aggiornare più risorse, come si fa a garantire la coerenza, se la seconda operazione di scrittura fallisce

Let immaginiamo che vogliamo scrivere gli aggiornamenti di risorsa A e la risorsa B in sequenza.

  1. Resource A viene aggiornato con successo
  2. Il tentativo di aggiornare la risorsa B non riesce

La prima operazione di scrittura non può essere facilmente rollback a fronte di un'eccezione, in modo cosa possiamo fare? Catching e sopprimendo l'eccezione di svolgere un'azione di compensazione nei confronti della risorsa A non è una valida opzione. Prima di tutto è complessa da implementare, ma in secondo luogo non è sicuro: cosa succede se la prima eccezione è accaduto a causa di una connessione di rete fallito? In questo scenario, non possiamo scrivere un'azione di compensazione nei confronti della risorsa A sia.

La chiave sta nel esplicita idempotenza . Mentre Windows Azure Le code non garantiamo esattamente una volta la semantica, che fanno garanzia almeno una volta la semantica. Ciò significa che, a fronte di eccezioni intermittenti, il messaggio verrà successivamente ripetizione dell'incontro .

Nello scenario precedente, questo è ciò che succede allora:

  1. Resource A è tentato aggiornato. Tuttavia, la riproduzione viene rilevato in modo lo stato di A non viene influenzata. Tuttavia, l'operazione 'write' ha successo.
  2. Resource B viene aggiornato con successo.

Quando tutte le operazioni di scrittura sono idempotente, coerenza eventuale può essere raggiunto con i replay Messaggio .

Altri suggerimenti

Domanda interessante e con questo si sta inchiodando uno dei punti di dolore in CQRS.

Il modo in cui Amazon sta gestendo questo è di avere lo scenario di business affrontare uno stato di errore se gli elementi richiesti è esaurito. Lo stato di errore è semplicemente quello di informare il cliente per posta elettronica che gli elementi non richiesti attualmente in magazzino e il giorno previsto per la spedizione.

Tuttavia -. Questo non risponde pienamente alla tua domanda

Pensando a uno scenario di vendita di biglietti vorrei fare in modo al dire al cliente che la richiesta hanno dato fosse un richiesta di prenotazione . Che la richiesta di prenotazione otterrebbe processati il ??più presto possibile e che faranno rivivere la risposta definitiva in una mail successiva. Con questo alowing alcuni clienti potrebbero ottenere una e-mail con un rifiuto alla loro richiesta.

Ora. Potremmo fare questa rejestion meno doloroso? Certamente. Inserendo una chiave nella nostra cache distribuita con la percentuale o la quantità di articoli a magazzino e decrementare questo contatore quando mai un articolo viene venduto. In questo modo abbiamo potuto avvertire l'utente prima che venga data la richiesta di prenotazione, diciamo che se solo il 10% del numero iniziale di voci è di sinistra, che il cliente potrebbe non essere in grado di ottenere l'oggetto in questione. Se il contatore è a zero dovremmo semplicemente rifiutiamo di accettare le richieste di più di prenotazione.

Il mio punto essere:

1) far conoscere all'utente che si tratta di una richiesta che stanno facendo e che questo potrebbe avere rifiutato 2) informare l'utente che le possibilità di successo per ottenere l'oggetto in questione è bassa

Non è esattamente una risposta precisa alla sua domanda, ma questo è come avrei gestire uno scenario come questo quando si tratta di CQRS.

L'eTag consente concorrenza ottimistica che si può usare al posto di blocco transazionale per aggiornare un documento e gestire in modo sicuro i potenziali condizioni di gara. Vedi note rilevanti qui http://msdn.microsoft.com/en-us/library/ dd179427.aspx per maggiori informazioni.

La storia potrebbe andare qualcosa come questo: L'utente A crea un evento e con i biglietti massimo di 2, eTag è 123. A causa della forte domanda 3 gli utenti tentano di acquistare i biglietti a quasi nello stesso momento. L'utente B crea una richiesta di prenotazione B. Utente C crea una richiesta di prenotazione C. Utente D crea una richiesta di prenotazione D.

Sistema S riceve richiesta di prenotazione B, legge manifestazione con l'ETAG 123 e cambia l'evento per avere 1 biglietto rimanente, S presenta l'aggiornamento compreso eTag 123 che corrisponde eTag originale in modo l'aggiornamento ha esito positivo. L'eTag è ora 456. Richiesta di prenotazione è approvata e l'utente viene informato ha avuto successo.

Un altro sistema S2 riceve richiesta di prenotazione C, allo stesso tempo come System S ha elaborato richiesta B, quindi legge anche evento l'evento con eTag 123 modifiche che a 1 biglietti e tentativi per modificare il documento rimanenti. Questa volta però l'eTag 123 non corrisponde quindi l'aggiornamento non riesce con un'eccezione. Sistema S2 ritenta l'operazione rileggere il documento che ora ha eTag 456 e un conteggio di 1 in modo decrementa a 0 e invia nuovamente con eTag 456.

Purtroppo per l'utente utente C, Sistema S ha iniziato l'elaborazione di richiesta dell'utente D's subito dopo che l'utente B e anche leggere il documento con l'ETAG 456, ma perché il sistema S è più veloce di sistema S2 è stato in grado di aggiornare l'evento con l'ETAG 456 prima che il sistema S2 così utente D riservato anche con successo il suo biglietto. eTag è ora 789

Sistema S2 non riesce ancora una volta, dà ancora un altro tentativo, ma questa volta, quando si legge l'evento con l'ETAG 789 vede che non ci sono biglietti disponibili e nega richiesta di prenotazione dell'utente di C così.

Come si comunica agli utenti che le loro richieste di prenotazione hanno avuto successo o meno dipende da voi. Si può solo interrogare il server ogni pochi secondi e attendere che lo stato di prenotazione per essere aggiornato.

Diamo un'occhiata alla prospettiva di business (mi occupo di cose simili - prenotazione appuntamenti in slot liberi) ...

La prima cosa nella vostra analisi che mi colpisce come essere fuori è che non c'è alcuna nozione di un biglietto di prenotabile / sedile / tavolo. Queste sono le risorse in fase prenotati.

In caso di essere transazionale, è possibile utilizzare una qualche forma di unicità per garantire che una doppia prenotazione non avviene per lo stesso biglietto / sedile / tavolo (maggiori informazioni all'indirizzo http://seabites.wordpress.com/2010/11/11/consistent-indexes-constraints ). Questo scenario richiede sincrono (ma ancora concomitante) l'elaborazione del comando.

In caso di non essere transazionale, è possibile retroattivamente monitorare il flusso di eventi e di compensare il comando. Si può anche dare all'utente finale un'esperienza di attesa per la conferma della prenotazione fino a quando il sistema sa per certo - vale a dire dopo l'analisi flusso di eventi - che il comando è stato completato ed è stato o non è stato compensato (che si riduce a "è stata la prenotazione effettuata? si o no?"). In altre parole compensazione potrebbe essere parte del ciclo di conferma.

Let passo indietro un po 'di più ...

Quando la fatturazione è coinvolto come (per esempio la vendita dei biglietti on-line) Beh, credo che tutto questo si evolve scenario in una saga in ogni modo (biglietto + biglietto disegno di legge di riserva). Anche senza la fatturazione, devi avere una saga (riserva tavolo + prenotazione conferma) per rendere l'esperienza credibile. Quindi, anche se si sta solo in zoom su un solo aspetto di prenotazione di un biglietto / tavolo / sede (cioè è ancora disponibile), la "lunga durata" transazione non è completa fino a quando ho pagato per esso o fino a quando mi ha confermato che . Compensazione accadrà in ogni modo, liberando il biglietto di nuovo quando ho interrompere la transazione per qualsiasi motivo. La parte interessante diventa ora come l'azienda vuole affrontare questo: forse qualche altro cliente avrebbe completato l'operazione fosse che gli aveva dato / lei lo stesso biglietto. In questo caso il rimborso potrebbe diventare più interessante quando la doppia prenotazione di un biglietto / sedile / tavolo - anche offrendo uno sconto su un prossimo evento / simile per compensare per l'inconveniente. La risposta sta nel modello di business, non il modello tecnico.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top