🇧🇷 Modelo de Concorrência Orientado ao Negócio

Arley Pádua
5 min readSep 29, 2018

Criar um design de software multi serviços pode te levar a algumas dores de cabeça quando pensamos em concorrência. Nesse post vou descrever os modelos técnicos de concorrência mais famosos e uma proposta diferente ao vê-los, para que possamos pensar melhor no design dos nossos sistemas.

Veja abaixo os modelos de concorrência, benefícios e desafios.

Pessimista

Esse modelo de concorrência propõe que ninguém pode alterar o estado de um objeto enquanto ele está sendo alterado por outro processo, significando que todas as transações subsequentes interessadas no objeto, terão que esperar sua vez na fila para fazer o que precisam.

Essa abordagem em alguns casos é ruim pois você bloqueia o dado, não pode escalar e as vezes acontece que um dado processo decide tirar férias enquanto segura a fila de processos interessados no mesmo objeto, forçando que alguém resolva o problema manualmente.

Otimista

Esse modelo propõe que dada duas ou mais transações em um mesmo objeto, somente uma transação ganha: ou a primeira, ou a segunda ganha. Existe um lado ruim desse modelo é que in qualquer um dos casos, um dos processos perde dado.

Existem duas estratégias para o modelo otimista:

  • Primeira ganha, significando que o segundo tem o dado rejeitado. A menos que você tenha uma retry strategy (mencionada aqui), a segunda operação perde dado.
  • Última ganha, por padrão o dado é sobrescrito pela segunda transação. Isso significa que a primeira transação tem o dado descartado. A diferença aqui é que a primeira operação não sabe que o dado foi perdido. Isso é problemático, pois você não consegue decidir o que fazer quando isso acontecer.

Negócio

Todos os modelos acima tem seus próprios desafios, mas precisamos de um modelo focado no negócio, que pode lidar com ambos os mundos: otimista e pessimista.

Para entender a ideia proposta, veja primeiro o exemplo de uma transação abaixo:

1: Begin Transaction
2: Read Entity A
3: Read Entity B
4: Update Entity C com dado de A e B
5: Commit Transaction

Olhando para o exemplo vemos que uma vez que o dado é lido, não é possível garantir que o estado de C será completamente consistente com A e B, pois A e B podem ter seus estados totalmente modificados ao final da transação, não sendo mais consistente com C.

A questão que fica é: Essa inconsistência é importante para o negócio ?

Na maioria das vezes assumimos que se colocarmos transações em todos os lugares, tudo será consistente. Isso se deve ao fato de que como os ORM’s se tornaram largamente utilizados nos nossos sistemas, desenvolvedores começaram a falar:

Ah! Não tem problema, eu não preciso me preocupar com o banco de dados, pois o ORM cuida disso pra mim. Eu vou focar no negócio!

Essa frase não é totalmente verdade. De fato, precisamos nos preocupar com o negócio, e se você está desenvolvendo um sistema em um domínio colaborativo, onde outro processo pode mudar o estado da entidade que você está para usar, então você precisa pensar em concorrência, que é parte do negócio e muito relacionado ao funcionamento do banco de dados.

Então, para ter um modelo de concorrência orientado ao negócio, a primeira regra:

  • Faça a decisão do seu modelo de concorrência conscientemente (otimista ou pessimista)

Mas o modelo de concorrência técnico não é o suficiente, você ainda tem que codificar de uma maneira que evite falhas no negócio pela sua escolha técnica.

A abordagem proposta para resolver esse problema é dividir a sua transação em pequenas partes, fazendo somente uma coisa por vez com uma porção de dado.

1: Recebe uma mensagem (comando/evento)
1: Obtém o Objeto de Domínio
2: Altera o estado do Objeto de Domínio
3: Commit (possivelmente publicando Eventos de Domínio)

É muito importante não tocar em vários objetos de domínio, porque toda vez que você acessa outro objeto de domínio na mesma transação, mais probabilidade de problemas de concorrência é adicionada. Se você precisa mudar mais estados em seus objetos de domínio, você deve olhar para os eventos de domínio.

Em essência: Relacionamento entre seus objeto de domínio, te dará problemas de concorrência. Então, pense com bastante cuidado nos seus bounded contexts, pois eles vão remover relacionamento de objetos de domínio desnecessários e, por design a maioria dos desafios de concorrência serão extintos.

Essa abordagem nos salva em vários casos, mas em um nível de abstração mais acima, precisamos de pensar um pouco mais adiante: As vezes o negócio requer que em um dado processo, somente uma transação ganha. Veja um exemplo abaixo:

Em um dado domínio colaborativo onde um cliente fez um pedido, e o negócio é proativo ao fazer o envio do produto tendo dois fornecedores de entrega diferentes em caso de demora. A seguinte racing condition pode aparecer:

1: User faz um pedido
2: Solicita envio para o fornecedor #1
4: Fornecedor #1 demora muito, solicita envio p/ fornecedor #2
5: Fornecedor #2 responde na hora que foi enviado
6: Solicita o cancelamento para o fornecedor #1
7: Ao mesmo tempo, fornecedor #1 responde que o pedido foi enviado

Nesse cenário você tem uma racing condition e ambos fornecedores vão entregar o produto. Não existe outra maneira de sair desse problem de concorrência, senão achar um motivo de negócio.

Existe uma série de soluções possíveis:

  • Entregar ambos e assumir a perda, caso o negócio queira trabalhar dessa forma.
  • Pedir para o cliente o estorno de um dos produtos, dando um incentivo em compras futuras, reduzindo os custos do problema.
  • E essa lista poderia continuar com muitas possíveis soluções…

Considerando a lista acima, você pode notar que estávamos falando sobre um problema técnico de racing condition entre duas solicitações, e agora tentamos achar um motivo de negócio para o problema: política de estorno. Baseado nisso, o domain expert é capaz de decidir em qual regra seguir nesses casos.

Se eu puder dar uma sugestão, nem tudo é resolvido por uma decisão técnica. As vezes você precisa achar um motivo de negócio para o problema e consertá-lo como parte do seu processo de negócio.

Se você está desenvolvendo um sistema de um usuário único, você não precisa se preocupar com concorrência, mas esse não é o caso da maioria.

Finalizando: Não subestime a concorrência no seu software, pois existe negócio escondido nela.

Me siga no Twitter @_arleypadua.

Esse artigo também está disponível em Inglês.

--

--

Arley Pádua

Software Engineer and passionate about distributed systems