[MICROSERVICES] Outbox Pattern

Victor Hugo
4 min readJul 26, 2020

--

Olá novamente, estou de volta após alguns anos desde meu último artigo e hoje estou aqui para falar sobre o querido, outbox pattern.

Há alguns meses, assisti a uma palestra sobre micro serviços em geral, e um dos palestrantes focou em explanar sobre este padrão, de início foi perguntado quantas pessoas conheciam esse pattern e pouquíssimas responderam que sim. Após o fechamento da apresentação, conversando com alguns dos ouvintes, percebi que boa parte deles ficaram com um conceito mal formulado sobre o que realmente se tratava a técnica. Então vamos tentar abordar aqui de forma sucinta e prática esse tão simples (e necessário) padrão.

Imagine um sistema de catálogo de filmes, composto por basicamente dois aggregates: User e Recommendations — quando um novo usuário é cadastrado em User, o serviço de Recommendations começa a traçar um perfil para o usuário e realizar recomendações de filmes que ele possa se interessar em assistir.

Assumindo-se que a comunicação entre os aggregates se dá por um sistema de mensageira, nós poderíamos ter o seguinte fluxo:

O que pode dar errado?

Perceba que a finalização da transação (commit) que efetua a persistência dos dados do usuário e a publicação da mensagem no broker, são operações distintas, não-atômicas. Após o commit, o message broker pode ficar indisponível e a mensagem não ser publicada ou ainda, a própria aplicação pode sofrer um crash nesse momento e a mensagem novamente não seria publicada. Em ambos os casos, acabamos gerando uma inconsistência perigosa em nosso domínio que pode comprometer até seu motivo de ser — no nosso caso, o usuário pode ficar sem recomendações. Assim sendo, devemos adotar alguma tática que garanta que as mensagens serão publicadas mesmo ocorrendo falhas após a operação de commit, e é aí que entra o outbox:

O outbox, é uma técnica de garantia de entrega utilizada comumente na troca de mensagens entre sistemas ou partes de um sistema, quando não podemos ter atomicidade na operação.

O padrão basicamente nos diz, que ao invés de tentar uma ação direta não-atômica (publicação da mensagem), devemos utilizar operações atômicas que posteriormente nos permitam a execução da ação desejada.

Podemos atingir esse objetivo simplesmente criando uma nova tabela no banco de dados chamada ‘outbox’, onde registramos as mensagens que devem ser publicadas e posteriormente, realizando de fato a sua publicação. Inserindo os dados da mensagem a ser enviada junto com os dados do novo usuário, nós garantimos uma operação atômica, então sabemos que sempre que um usuário for criado com sucesso, o registro da mensagem a ser publicada também será.

Com o registro das mensagens bem definido, nos resta realizar a recuperação daquelas que ainda não foram publicadas e finalmente, executar a publicação delas no nosso message broker. Podemos utilizar várias técnicas para executar essa operação, como um schedule que realiza polling nessa nova tabela ou mesmo via leitura direta do log tail da base de dados detectando as inserções que ocorreram.

Refatorando nossa solução anterior, aplicando o outbox pattern, com o uso da técnica de polling, teríamos:

Desta forma garantimos que a mensagem ‘user_created’ será publicada em algum momento e assim o Recommendations aggregate saberá quando realizar as recomendações de filmes.

Algo ainda pode dar errado?

Note que ainda temos um problema semelhante ao inicial após aplicar o outbox, ainda temos duas operações não-atômicas sendo executadas, a publicação da mensagem pendente e a marcação dessa mensagem como já publicada, para não ser enviada novamente. Se o sistema sofresse um crash logo após a publicação da mensagem, nós não teríamos realizado a persistência na alteração do status da mesma, então corremos o risco de termos mais de uma publicação da mesma mensagem.

O outbox, nesta arquitetura com polling, tem a característica de at least once delivery, ou seja, garantimos que a mensagem será enviada ao menos uma vez, mas ela eventualmente pode ser enviada mais vezes. Com isto em mente, devemos projetar nossas soluções de forma idempotente, para que a mesma mensagem não possa afetar nosso estado desnecessariamente e assim termos um sistema confiável e que atenda satisfatoriamente nosso domínio.

É isto pessoal, na maioria das vezes nós iremos encontrar este tipo de cenário fazendo o uso do outbox pattern, se estiverem interessados em um exemplo prático, estou disponibilizando um neste repositório no github.

Dúvidas, comentários e sugestões são bem vindas. Até o próximo artigo ;D

--

--