Estratégias de Paralelismo e Divisão de Responsabilidades em Sistemas Complexos
Tags: java, eventdriven, rabbitmq, braziliandevs
Published: 2023-11-04 19:18:22

Paralelismo é extremamente funcional quando temos algumas responsabilidades bem definidas dentro do nosso sistema, a divisão de responsabilidade entendo que é o principal ponto de partida.
## Escopo
Vamos imaginar um sistema que fará o fechamento da folha de pagamento dos funcionários.

Nesse fechamento hipotético precisamos identificar quantidade de horas extras dos funcionários, multiplicar o resultado pelo valor da hora do funcionário e adicionar o valor encontrado ao salário para assim chegarmos ao valor final a ser pago.
## Quebrando responsabilidades

Como podemos dividir as responsabilidades nesse cenário? existe uma ordem de acontecimento ?

Primeiro vamos pensar, nesse cenário não podemos fazer o fechamento sem as horas extras previamente calculadas, com isso entendido fica claro que o cálculo de horas extras não depende de nada mas o fechamento depende do cálculo das horas extras, agora vamos pensar mais um pouco, vamos iniciar com a rotina de calcular as horas extras, será que conseguimos quebrar essa rotina ? será que a rotina precisa mesmo processar todos os funcionários de uma vez ? 

Vamos imaginar que todos os meses esse sistema fará o cálculo de horas extras para 2mil funcionários, nessa nossa primeira implementação faremos um `select` na base de dados, pegaremos os 2mil funcionários e para cada funcionário encontrado calcularemos o valor de hora extra.

Um dos problemas com a abordagem acima é que caso um único funcionário falhe, faremos o reprocessamento de todos os funcionários pois tudo está acoplado dentro de uma única rotina.

Beleza, para mitigar esse problema, vamos dividir nosso problema em duas partes:
* Identificação de cada Funcionário.
* Execução do calculo de hora extra dado um funcionário.
## Identificando todos os Funcionários

Abaixo temos um simples diagrama que representa a primeira parte da implementação, vamos identificar todos os funcionários que estão aptos e enfileirá-los.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jh12s4nrdcavoil0a0u5.png)
> Em nosso exemplo estamos utilizando o RabbitMQ para controlar nossas mensagens

Podemos implementar controles de status ou outro mecanismo que de alguma forma ajude a identificar se um Funcionário está ou não apito para ter suas horas extras calculas.

Quando lidamos com eventos temos o benefício do desacoplamento mas temos que nos preocupar com outros fatores, como por exemplo, lidar com mensagens duplicadas, caso por qualquer motivo chegue uma mensagem que já foi processada essa mensagem precisa ser descartada para evitar sobrescrita ou desperdício de recursos.
## Executando o cálculo de hora extra

Agora vamos implementar um `Listener` que receberá esse único funcionário e realizará todo o processo de cálculo de hora extra, com o RabbitMQ, conseguimos configurar a quantidade de `Consumers` que um determinado `Listener` produzirá, ou seja, podemos fazer com que processemos vários Funcionários ao mesmo tempo.

Vale observar que a nossa rotina de cálculo de hora extra é totalmente isolada entre os funcionários, podemos processar com segurança todos em paralelo, pois nesse momento não existe recurso em concorrência.

Com essa abordagem temos o processo de cálculo de hora extra sendo executado por funcionário e caso algum funcionário falhe conseguiremos reprocessar apenas o funcionário que falhou.
## DLQ

Com nosso `Listener` implementado podemos tirar proveito das DLQs (Dead Letter Queue) que nada mais é do que o local para onde as mensagem que não conseguiram ser processadas vão ser armazenadas, é indicado que sempre que uma mensagem for direcionada para DLQ, seja disparado algum tipo de alarme para que a equipe responsável possa fazer suas análises, isso lembra um post que falo sobre [a importância dos logs](https://dev.to/cassunde/a-importancia-dos-logs-23c9). 
## O Processo de fechamento

E sobre o processo de fechamento? será que conseguimos fazer da mesma forma? a resposta é sim, todos os processos serão por funcionário, quando o funcionário sair da fila de `cálculo de hora extra` ele irá para a fila de `fechamento de folha de pagamento`. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hylggmgtujvl1687s7zd.png)

Nesse fluxo fica explicito que para um funcionário chegar na `Fila de Fechamento` ele precisará passar na fila de `Calculo de Hora Extra`.
## Conclusão

Para alcançar o paralelizar com segurança e escalabilidade, é crucial isolar adequadamente os processos, só isso já é bem difícil principalmente em sistemas monolíticos onde existe muito acoplamento, tolerância a falhas não é coisa de microservice e sim de qualquer sistemas. 

Temos cenários mais complexos, como por exemplo, receber mensagem para descontar valor de um saldo único, isso gera concorrência ou seja, dois processos querendo descontar do mesmo saldo, mas nesse post vou abordar a forma mais simples do paralelismo.

Espero que tenha ajudado a pensar em alternativas antes de tentar paralelizar um processo já existente ou no desenho de um novo processo, qualquer dúvida ou comentário é só chamar ;)

{% embed https://dev.to/cassunde/implementando-paralelismo-com-virtual-threads-no-java-21-3om8 %}