Ci sono molte soluzioni che compromettono più di quanto mi sento a mio agio. Certo, se il tuo caso d'uso è complesso, come lo spostamento di denaro tra banche diverse, alternative più piacevoli potrebbero essere impossibili. Ma vediamo cosa possiamo fare nello scenario comune, in cui l'uso dei microservizi interferisce con le nostre potenziali transazioni del database.
Opzione 1: evita la necessità di transazioni se è tutto possibile
Ovvio e menzionato prima, ma ideale se possiamo gestirlo. I componenti appartenevano effettivamente allo stesso microservizio? O possiamo ridisegnare il / i sistema / i in modo tale che la transazione non sia necessaria? Forse accettare la non-transazione è il sacrificio più conveniente.
Opzione 2: utilizza una coda
Se c'è abbastanza certezza che l'altro servizio riuscirà a qualunque cosa vogliamo fare, possiamo chiamarlo tramite una qualche forma di coda. L'elemento in coda non verrà raccolto fino a un momento successivo, ma possiamo assicurarci che l'elemento sia messo in coda .
Ad esempio, diciamo che vogliamo inserire un'entità e inviare un'e-mail, come una singola 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 avranno bisogno di accedere alla stessa tabella.
Opzione 3: dura il lavoro esterno, prima di completare la transazione
Questo approccio si fonda sul presupposto che è improbabile che il commit della transazione fallisca.
Begin transaction
Insert entity
Insert another entity
Make external call
Commit transaction
Se le query non riescono, la chiamata esterna non ha ancora avuto luogo. Se la chiamata esterna non riesce, la transazione non viene mai eseguita.
Questo approccio viene fornito con le limitazioni che possiamo solo effettuare una chiamata esterna one e deve essere eseguita per ultima (ovvero non possiamo usare il suo risultato nelle nostre query).
Opzione 4: crea oggetti in sospeso
Come pubblicato qui , possiamo avere più microservizi per creare componenti diversi, ciascuno in uno stato in sospeso, non transazionale.
Qualsiasi convalida viene eseguita, ma nulla viene creato in uno stato definitivo. Dopo che tutto è stato creato con successo, ogni componente è attivato. Di solito, questa operazione è così semplice e le probabilità che qualcosa vada storto sono così piccole che potremmo persino preferire l'attivazione non transazionale.
Il più grande svantaggio è probabilmente che dobbiamo tener conto dell'esistenza di elementi in sospeso. Qualsiasi query di selezione deve considerare se includere i dati in sospeso. La maggior parte dovrebbe ignorarlo. E gli aggiornamenti sono un'altra storia.
Opzione 5: lascia che il microservizio condivida la sua query
Nessuna delle altre opzioni lo fa per te? Allora prendiamo non ortodossi .
A seconda della compagnia, questa 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.
C'è un modo per trasformare le query da più microservizi in una semplice transazione di database singolo.
Restituisce la query, anziché eseguirla.
Begin transaction
Execute our own query
Make external call, receiving a query
Execute received query
Commit transaction
Dal punto di vista della rete, ogni microservizio deve essere in grado di accedere a ciascun database. Tienilo a mente, anche per quanto riguarda il futuro ridimensionamento.
Se i database coinvolti nella transazione si trovano sullo stesso server, questa sarà una transazione normale. Se sono 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 una classe Command eseguibile, mantenendo il flusso leggibile: la chiamata microservice genera un comando, che eseguiamo, come parte della nostra transazione.
La stringa di connessione è ciò che ci fornisce il microservizio di origine, quindi, a tutti gli effetti, la query viene ancora considerata eseguita da quel microservizio. Lo stiamo instradando fisicamente attraverso il microservice del client. Questo fa la differenza? Bene, ci consente di metterlo nella stessa transazione con un'altra query.
Se il compromesso è accettabile, questo approccio ci fornisce la linearità diretta di un'applicazione monolite, in un'architettura di microservizi.