Existem muitas soluções que comprometem mais do que me sinto confortável. Concedido, se o seu caso de uso for complexo, como mover dinheiro entre diferentes bancos, alternativas mais agradáveis podem ser impossíveis. Mas vamos ver o que podemos fazer no cenário comum, onde o uso de microsserviços interfere em nossas possíveis transações de banco de dados.
Opção 1: evitar a necessidade de transações, se tudo for possível
Óbvio e mencionado antes, mas ideal se pudermos administrá-lo. Os componentes realmente pertencem ao mesmo microsserviço? Ou podemos redesenhar o (s) sistema (s) de tal forma que a transação se torne desnecessária? Talvez aceitar a não-transacionalidade seja o sacrifício mais acessível.
Opção 2: usar uma fila
Se houver certeza suficiente de que o outro serviço será bem-sucedido em tudo o que queremos, podemos chamá-lo por meio de alguma forma de fila. O item enfileirado não será coletado até mais tarde, mas podemos garantir que o item esteja enfileirado .
Por exemplo, digamos que queremos inserir uma entidade e enviar um e-mail como uma única transação. Em vez de chamar o servidor de e-mail, enfileiramos o e-mail em uma tabela.
Begin transaction
Insert entity
Insert e-mail
Commit transaction
Uma clara desvantagem é que vários microsserviços precisarão acessar a mesma tabela.
Opção 3: fazer o trabalho externo por último, pouco antes de concluir a transação
Essa abordagem baseia-se no pressuposto de que comprometer a transação é muito improvável que falhe.
Begin transaction
Insert entity
Insert another entity
Make external call
Commit transaction
Se as consultas falharem, a chamada externa ainda não ocorreu. Se a chamada externa falhar, a transação nunca será confirmada.
Essa abordagem vem com as limitações que só podemos fazer uma chamada externa, e isso deve ser feito por último (ou seja, não podemos usar seu resultado em nossas consultas).
Opção 4: criar coisas em um estado pendente
Conforme postado aqui , podemos ter vários microsserviços criando componentes diferentes, cada um em um estado pendente, não transacional.
Qualquer validação é executada, mas nada é criado em um estado definitivo. Depois que tudo foi criado com sucesso, cada componente é ativado. Normalmente, essa operação é tão simples e as chances de algo dar errado são tão pequenas que podemos até mesmo preferir fazer a ativação de forma não transacional.
A maior desvantagem é provavelmente que temos que explicar a existência de itens pendentes. Qualquer consulta selecionada precisa considerar se deve incluir dados pendentes. A maioria deve ignorá-lo. E as atualizações são outra história.
Opção 5: deixe o microservice compartilhar sua consulta
Nenhuma das outras opções faz isso por você? Então vamos não ortodoxos .
Dependendo da empresa, esta pode ser inaceitável. Estou ciente. Isso é pouco ortodoxo. Se não for aceitável, siga por outro caminho. Mas se isso se adequar à sua situação, resolve o problema de maneira simples e poderosa. Pode ser apenas o compromisso mais aceitável.
Existe uma maneira de transformar consultas de vários microsserviços em uma simples transação de banco de dados.
Retorna a consulta, em vez de executá-la.
Begin transaction
Execute our own query
Make external call, receiving a query
Execute received query
Commit transaction
Em termos de rede, cada microsserviço precisa ser capaz de acessar cada banco de dados. Tenha isso em mente, também em relação ao dimensionamento futuro.
Se os bancos de dados envolvidos na transação estiverem no mesmo servidor, isso será uma transação regular. Se eles estiverem em servidores diferentes, será uma transação distribuída. O código é o mesmo, independentemente disso.
Recebemos a consulta, incluindo seu tipo de conexão, seus parâmetros e sua string de conexão. Podemos envolvê-lo em uma classe Command bem organizada, mantendo o fluxo legível: A chamada do microserviço resulta em um comando, que executamos como parte de nossa transação.
A cadeia de conexão é o que o microserviço de origem nos fornece, portanto, para todos os efeitos, a consulta ainda é considerada executada por esse microserviço. Estamos apenas roteando fisicamente através do microserviço do cliente. Isso faz alguma diferença? Bem, nos permite colocá-lo na mesma transação com outra consulta.
Se o compromisso for aceitável, essa abordagem nos dá a transacionalidade direta de um aplicativo monolítico, em uma arquitetura de microsserviço.