Esistono molte soluzioni che compromettono più di quanto mi trovo a mio agio. Certo, se il tuo caso d'uso è complesso, come spostare denaro tra banche diverse, alternative più piacevoli potrebbero essere impossibili. Ma diamo un'occhiata a cosa possiamo fare nello scenario comune, in cui l'uso dei microservizi interferisce con le nostre potenziali transazioni di database.
Opzione 1: evitare la necessità di transazioni, se possibile
Ovvio e menzionato prima, ma ideale se riusciamo a gestirlo. I componenti appartenevano effettivamente allo stesso microservizio? Oppure possiamo ridisegnare i sistemi in modo tale che la transazione diventi superflua? Forse accettare la non transazione è il sacrificio più conveniente.
Opzione 2: utilizzare una coda
Se c'è abbastanza certezza che l'altro servizio riuscirà in qualunque modo vogliamo, possiamo chiamarlo tramite una qualche forma di coda. L'articolo in coda non verrà ritirato fino a dopo, ma possiamo assicurarci che l'articolo sia in coda .
Ad esempio, supponiamo che vogliamo inserire un'entità e inviare un'e-mail, come un'unica transazione. Invece di chiamare il server di posta, accodiamo l'e-mail in una tabella.
Begin transaction
Insert entity
Insert e-mail
Commit transaction
Un chiaro svantaggio è che più microservizi dovranno accedere alla stessa tabella.
Opzione 3: eseguire il lavoro esterno per ultimo, appena prima di completare la transazione
Questo approccio si basa sul presupposto che è improbabile che fallisca la commissione.
Begin transaction
Insert entity
Insert another entity
Make external call
Commit transaction
Se le query falliscono, la chiamata esterna non ha ancora avuto luogo. Se la chiamata esterna non riesce, la transazione non viene mai impegnata.
Questo approccio ha i limiti che possiamo fare solo una chiamata esterna, e deve essere fatto per ultimo (cioè non possiamo usare il suo risultato nelle nostre query).
Opzione 4: creare oggetti in uno stato in sospeso
Come pubblicato qui , possiamo far sì che più microservizi creino componenti diversi, ciascuno in uno stato in sospeso, non a livello transazionale.
Viene eseguita qualsiasi convalida, ma nulla viene creato in uno stato definitivo. Dopo che tutto è stato creato con successo, ogni componente viene attivato. Di solito, questa operazione è così semplice e le probabilità che qualcosa vada storto sono così ridotte, che potremmo addirittura preferire fare l'attivazione in modo non transazionale.
Il più grande svantaggio è probabilmente che dobbiamo tenere conto dell'esistenza di articoli in sospeso. Qualsiasi query selezionata deve considerare se includere dati in sospeso. La maggior parte dovrebbe ignorarlo. E gli aggiornamenti sono del tutto un'altra storia.
Opzione 5: consenti al microservizio di condividere la sua query
Nessuna delle altre opzioni lo fa per te? Allora diventiamo poco ortodossi .
A seconda dell'azienda, questo potrebbe essere inaccettabile. Sono consapevole. Questo non è ortodosso. Se non è accettabile, segui un'altra strada. Ma se questo si adatta alla tua situazione, risolve il problema in modo semplice e potente. Potrebbe essere solo il compromesso più accettabile.
Esiste un modo per trasformare le query da più microservizi in una semplice transazione con un solo database.
Restituisce la query, anziché eseguirla.
Begin transaction
Execute our own query
Make external call, receiving a query
Execute received query
Commit transaction
Per quanto riguarda la rete, ogni microservizio deve poter accedere a ciascun database. Tienilo a mente, anche per quanto riguarda il ridimensionamento futuro.
Se i database coinvolti nella transazione si trovano sullo stesso server, questa sarà una transazione regolare. Se si trovano su server diversi, sarà una transazione distribuita. Il codice è lo stesso a prescindere.
Riceviamo la query, incluso il suo tipo di connessione, i suoi parametri e la sua stringa di connessione. Possiamo racchiuderlo in un'ordinata classe Command eseguibile, mantenendo il flusso leggibile: la chiamata al microservizio si traduce in un comando, che eseguiamo, come parte della nostra transazione.
La stringa di connessione è ciò che ci dà il microservizio di origine, quindi a tutti gli effetti la query è ancora considerata eseguita da quel microservizio. Lo stiamo semplicemente instradando fisicamente attraverso il microservizio client. Questo fa la differenza? Bene, ci consente di inserirlo nella stessa transazione con un'altra query.
Se il compromesso è accettabile, questo approccio ci offre la semplice transazionalità di un'applicazione monolitica, in un'architettura a microservizi.