<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Carlos Henrique dos Santos</title>
    <description>The latest articles on Forem by Carlos Henrique dos Santos (@devrodrigues_94).</description>
    <link>https://forem.com/devrodrigues_94</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F505016%2Fd8200494-add0-485d-83e1-99d590533a1e.png</url>
      <title>Forem: Carlos Henrique dos Santos</title>
      <link>https://forem.com/devrodrigues_94</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/devrodrigues_94"/>
    <language>en</language>
    <item>
      <title>Construindo a Arquitetura de uma Casa de Apostas Escalável: Um Estudo Prático com Spring Boot e Kafka (1/n)</title>
      <dc:creator>Carlos Henrique dos Santos</dc:creator>
      <pubDate>Tue, 02 Dec 2025 16:20:10 +0000</pubDate>
      <link>https://forem.com/devrodrigues_94/construindo-a-arquitetura-de-uma-casa-de-apostas-escalavel-um-estudo-pratico-com-spring-boot-e-3dn4</link>
      <guid>https://forem.com/devrodrigues_94/construindo-a-arquitetura-de-uma-casa-de-apostas-escalavel-um-estudo-pratico-com-spring-boot-e-3dn4</guid>
      <description>&lt;p&gt;Este artigo é o primeiro de uma série onde quero compartilhar um desafio técnico que enfrentei recentemente e que acabou me motivando a escrever sobre o assunto. Pediram que eu desenhasse a arquitetura de uma plataforma de apostas — um sistema que lida com eventos o tempo todo, escala enorme e a necessidade de tudo funcionar sem parar. Decidi transformar essa experiência em conteúdo não só para registrar um pouco do que sei, mas também para mostrar como penso e como costumo resolver problemas desse porte.&lt;/p&gt;

&lt;p&gt;Nesta série, você vai desde a concepção da arquitetura até a implementação do POC, passando também por infraestrutura e deploy usando GitOps, Kubernetes e ArgoCD. É uma forma que encontrei de abrir minha caixa de ferramentas e, ao mesmo tempo, me desafiar a explicar de forma clara aquilo que estou pensando enquanto estou projetando. &lt;/p&gt;

&lt;h2&gt;
  
  
  Introdução — O Problema Apresentado
&lt;/h2&gt;

&lt;p&gt;O ponto de partida deste projeto foi um desenho simples, mas que representa um cenário extremamente comum no domínio de apostas esportivas: uma aposta acumulada (multi-selection) composta por dois eventos distintos, ambos ainda pendentes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhyd1320i99z5hyu4je8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhyd1320i99z5hyu4je8m.png" alt=" " width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nesse exemplo, o usuário realizou uma aposta (BetId 123) composta por:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Seleção 1: Liverpool vs Manchester City&lt;/li&gt;
&lt;li&gt;Seleção 2: Real Madrid vs Barcelona&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ambos os jogos estão em andamento e, portanto, cada seleção e a aposta como um todo encontram-se no estado pendente.&lt;/p&gt;

&lt;p&gt;Quando os jogos terminam, chega ao sistema a informação do resultado final via &lt;em&gt;WEBHOOK&lt;/em&gt;. Após processar cada evento, o sistema deverá ser capaz de:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;atualizar as seleções relacionadas;&lt;/li&gt;
&lt;li&gt;recalcular o status da aposta como um todo;&lt;/li&gt;
&lt;li&gt;disparar notificações;&lt;/li&gt;
&lt;li&gt;acionar o fluxo de pagamento em caso de vitória.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O desenho mostrava o estado inicial e o estado final esperado — após ambos os jogos terminarem, a aposta deveria ser marcada como vencida.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requisitos do sistema
&lt;/h2&gt;

&lt;p&gt;Além do desenho, foram apresentados requisitos de desempenho e resiliência que definem a complexidade do problema.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O banco de dados contém mais de 500 milhões de apostas pendentes, referentes a múltiplos eventos.&lt;/li&gt;
&lt;li&gt;O sistema deve ser capaz de processar 500 mil liquidações por minuto (liquidação = resultado recebido para um evento).&lt;/li&gt;
&lt;li&gt;Deve ser capaz de enviar 500 mil notificações de pagamento por minuto para o serviço de Pagamentos.&lt;/li&gt;
&lt;li&gt;Deve atualizar 500 mil apostas por minuto no banco de dados.&lt;/li&gt;
&lt;li&gt;A latência total por aposta deve ser inferior a 1 minuto.&lt;/li&gt;
&lt;li&gt;Degradações do serviço devem ser consideradas (degradações só podem aumentar a latência, nunca causar perdas operacionais ou lacunas no processamento).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  O Gatilho Inicial — O Webhook de Resultado
&lt;/h3&gt;

&lt;p&gt;No nosso fluxo, o webhook é o gatilho externo que informa que um jogo terminou.&lt;br&gt;
Ele chega ao sistema como uma requisição HTTP enviada automaticamente pelo provedor com:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"matchExternalId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"987654321"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"homeScore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"awayScore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FINISHED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"providerEventId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prov-1"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;É a partir desse ponto que toda a liquidação começa.&lt;/p&gt;

&lt;h3&gt;
  
  
  O problema: o provedor envia o webhook apenas uma vez
&lt;/h3&gt;

&lt;p&gt;Durante a entrevista, foi destacado um ponto crítico:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;O provedor não reenviará o webhook em caso de falha.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ou seja, se não garantirmos o armazenamento ou encaminhamento desse dado, &lt;em&gt;o sistema corre o risco de perder o resultado do jogo&lt;/em&gt;, gerando apostas não liquidadas e inconsistência financeira.&lt;/p&gt;

&lt;h3&gt;
  
  
  Como o sistema garante que o evento não seja perdido
&lt;/h3&gt;

&lt;p&gt;A minha estratégia para resolver esse problema consiste em tentar publicar no Kafka &lt;code&gt;matches.result.v1&lt;/code&gt; e, em caso de falha, seja por:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;indisponibilidade temporária do broker,&lt;/li&gt;
&lt;li&gt;timeout de rede,&lt;/li&gt;
&lt;li&gt;erro de serialização,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;o serviço entra em modo de retry:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tentamos reenviar &lt;em&gt;N vezes&lt;/em&gt;, &lt;/li&gt;
&lt;li&gt;com intervalo incremental entre tentativas (&lt;em&gt;exponential backoff&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso resolve a maior parte dos problemas momentâneos.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nesse momento, o entrevistador questionou: Mas e se mesmo assim o sistema continuar falhando?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Se, mesmo após todas as tentativas, o envio não for possível, adicionaremos um circuit breaker, pararemos de tentar enviar ao Kafka por um tempo e ativaremos o mecanismo de fallback. Nesse caso, iremos persistir o evento em uma tabela específica. Essa técnica é conhecida como padrão &lt;em&gt;Outbox&lt;/em&gt; — ela garante que, ao gravarmos algo no banco de dados, o evento correspondente também seja registrado de forma atômica, mesmo que o broker de mensagens esteja indisponível. Com isso, protegemos o sistema de si próprio e evitando o volume excessivo de erros.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fallback: gravamos o webhook em um banco Postgres
&lt;/h3&gt;

&lt;p&gt;Quando o circuit breaker abre, persistimos o evento em uma tabela específica:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;o payload completo do webhook;&lt;/li&gt;
&lt;li&gt;o timestamp de recebimento.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso garante que:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;mesmo que o Kafka esteja fora do ar ou inatingível, o evento está preservado e poderá ser reprocessado mais tarde.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A tabela sugerida para armazenar os dados é bem simples.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;webhook_events_fallback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;         &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;payload&lt;/span&gt;    &lt;span class="n"&gt;JSONB&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="n"&gt;TIMESTAMPTZ&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt;     &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'PENDING'&lt;/span&gt;  &lt;span class="c1"&gt;-- PENDING | RESENT | FAILED&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depois, um job/worker pode tentar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ler os registros PENDING desta tabela,&lt;/li&gt;
&lt;li&gt;republicar webhook.events.received.v1,&lt;/li&gt;
&lt;li&gt;marcar o evento como RESENT ou FAILED.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para implementar o worker e tentar reenviar a mensagem, podemos usar o &lt;code&gt;@Scheduled&lt;/code&gt; do próprio Spring Boot para realizar esse trabalho.&lt;/p&gt;

&lt;p&gt;Basicamente, ele precisará ir até o banco de dados e buscar por eventos com status PENDING. Só que isso pode gerar um problema se não tomarmos cuidado. Como poderemos ter muitas instâncias dessa aplicação rodando simultaneamente, o mesmo worker pode pegar eventos repetidos e acabar enviando o mesmo dado repetidas vezes.&lt;/p&gt;

&lt;p&gt;Para resolver isso, podemos basicamente executar a query da seguinte maneira:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;webhook_events_fallback&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'PENDING'&lt;/span&gt;
&lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;asc&lt;/span&gt;
&lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;limit&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;skip&lt;/span&gt; &lt;span class="n"&gt;locked&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essa consulta irá:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;FOR UPDATE&lt;/em&gt;: bloquear os registros selecionados para escrita, impedindo que outro processo selecione as mesmas linhas.&lt;br&gt;
&lt;em&gt;SKIP LOCKED&lt;/em&gt;: se outra thread/worker já tiver pegado alguma linha, esta consulta ignora essas linhas bloqueadas e pega as próximas disponíveis.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;O objetivo aqui é:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;evitar deadlocks,&lt;/li&gt;
&lt;li&gt;impedir workers competindo pela mesma linha,&lt;/li&gt;
&lt;li&gt;reduzir o risco de processamento duplicado.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O desenho final da solução, até esse momento, ficou dessa forma:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5yr51bzezgngemwxcuqo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5yr51bzezgngemwxcuqo.png" alt=" " width="800" height="644"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Encerramos aqui a primeira parte da solução. Vou continuar a próxima etapa no próximo post. Nos vemos em breve!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dev-rodrigues/bet-platform/tree/main/services/result-ingestion-service" rel="noopener noreferrer"&gt;Github do projeto&lt;/a&gt;&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>distributedsystems</category>
      <category>springboot</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Como descobri o Pessimistic Locking e por que isso mudou minha forma de pensar sobre concorrência</title>
      <dc:creator>Carlos Henrique dos Santos</dc:creator>
      <pubDate>Sun, 20 Apr 2025 23:15:32 +0000</pubDate>
      <link>https://forem.com/devrodrigues_94/como-descobri-o-pessimistic-locking-e-por-que-isso-mudou-minha-forma-de-pensar-sobre-concorrencia-1g28</link>
      <guid>https://forem.com/devrodrigues_94/como-descobri-o-pessimistic-locking-e-por-que-isso-mudou-minha-forma-de-pensar-sobre-concorrencia-1g28</guid>
      <description>&lt;p&gt;Recentemente participei de uma entrevista técnica do tipo &lt;strong&gt;whiteboard&lt;/strong&gt;, e saí dela com algo muito mais valioso do que um possível “sim” ou “não”: &lt;strong&gt;aprendizado real, técnico e profundo&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Foi a partir de uma sugestão do próprio avaliador que entrei em contato com o conceito de &lt;strong&gt;Pessimistic Locking&lt;/strong&gt;, e desde então mergulhei no tema para entender os riscos que sistemas financeiros enfrentam quando se trata de concorrência de dados.&lt;/p&gt;

&lt;p&gt;Neste artigo, quero compartilhar esse processo — tanto o desafio apresentado, quanto os conceitos que aprendi depois, e como &lt;strong&gt;ter bons avaliadores faz toda a diferença para o crescimento profissional&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  O desafio apresentado
&lt;/h2&gt;

&lt;p&gt;Em um determinado momento da entrevista, o avaliador decidiu simular um &lt;strong&gt;caso real&lt;/strong&gt; de construção de produto. A ideia era propor a criação de um &lt;strong&gt;POC - Proof of Concept (Prova de Conceito).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;O projeto precisava ser simples e direto, pois a equipe estava em fase de validação do produto e procisava de algo funcional para testar o modelo de negócio com usuários o quanto antes.&lt;/p&gt;

&lt;p&gt;Dentro desse contexto, me foi solicitado que o projetasse alguns endpoints essenciais para o sistema bancário proposto:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GET /contas/{id}/extrato&lt;/code&gt; Para consultar o extrato e saldo de uma conta&lt;/p&gt;

&lt;p&gt;&lt;code&gt;POST /transferencias&lt;/code&gt; Para permitir que um usuário envie dinheiro da conta A para a conta B.&lt;/p&gt;

&lt;p&gt;A arquitetura deveria ser mínima, funcional e segura, e deveria atender a três pilares fundamentais:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Segurança dos dados e operações&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Boas práticas REST para API&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistência transacional&lt;/strong&gt;, mesmo em um sistema ainda em fase inicial&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Minhas escolhas e justificativas:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Usei &lt;code&gt;GET&lt;/code&gt; para a leitura do extrato, pois é um verbo seguro e sem efeitos colateriais.&lt;/li&gt;
&lt;li&gt;Usei &lt;code&gt;POST&lt;/code&gt; para a criação da transferência, pois trata-se de uma operação que modifica o estado do sistema.&lt;/li&gt;
&lt;li&gt;Os caminhos foram estruturados de forma RESTful, facilitando a compreensão e escalabilidade da API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A proposta do avaliador era clara: mesmo sendo uma POC, o ssitema deveria se comportar como algo confiável, pois a ideia era colocar a prova um produto real, ainda que em fase de testes.&lt;/p&gt;

&lt;p&gt;Até então, tudo parecia bem. Mas foi nesse momento que veio &lt;strong&gt;uma das perguntas mais importantes da entrevista&lt;/strong&gt;, que elevou o nível do desafio:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Como você garantiria que duas transferências simultâneas não fariam com que o mesmo saldo fosse usado duas vezes?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;E foi a partir dessa provocação que a entrevista tomou um novo rumo, um que me levou a buscar aprender mais sobre concorrência.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é Race Condition?
&lt;/h2&gt;

&lt;p&gt;Uma race condition acontece quando duas ou mais operações acessam o mesmo recurso ao mesmo tempo e o resultado depende da ordem de execução&lt;/p&gt;

&lt;p&gt;Por exemplo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A conta de João possui R$ 100.00&lt;/li&gt;
&lt;li&gt;Duas transferências de R$ 100.00 são disparadas ao mesmo tempo&lt;/li&gt;
&lt;li&gt;Ambas leem o saldo, consideram suficiente e processam a transação&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nesse caso, João acaba transferindo R$ 200.00 com apenas R$ 100.00 de saldo&lt;/p&gt;

&lt;p&gt;→ O sistema foi enganado por falta de controle de concorrência.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como funcionam os locks no banco de dados?
&lt;/h2&gt;

&lt;p&gt;Para evitar situações como a descrita acima, os bancos de dados implementam mecanismos de locks (bloqueio), que controlam o acesso concorrente a dados sensíveis.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é um lock?
&lt;/h2&gt;

&lt;p&gt;Um lock impede que múltiplas transações leiam ou alterem os mesmo dados ao mesmo tempo. Ele pode ser aplicado em diferentes níveis e tipos:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Shared lock (Leitura)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Permite múltiplas leituras simultâneas&lt;/li&gt;
&lt;li&gt;Bloqueia a escrita enquanto está ativo.&lt;/li&gt;
&lt;li&gt;Ideal para operações de leitura que exigem consistência&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Exclusive lock (Escrita)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Exclusivo: impede qualquer leitura ou escrita concorrente&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Lock de linha vs Lock de tabela&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Linha: bloqueia apenas o registro necessário. Permite maior concorrência&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tabela: bloqueia a tabela inteira. Mais seguro, porém menos eficiente.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Locks e transações
&lt;/h2&gt;

&lt;p&gt;Locks geralmente fazem parte de transações, que seguem as regras ACID:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Atomicidade&lt;/li&gt;
&lt;li&gt;Consistência&lt;/li&gt;
&lt;li&gt;Isolamento&lt;/li&gt;
&lt;li&gt;Durabilidade&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dessa forma, os locks acabam garantindo o isolamento da transação, impedindo que operações concorrentes interfiram nos dados enquanto a transação está em andamento.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é Pessimistic Locking?
&lt;/h2&gt;

&lt;p&gt;É uma abordagem onde os dados são bloqueados no momento da leitura, assumindo que há risco real de conflito.&lt;/p&gt;

&lt;p&gt;SQL Exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;FROM&lt;/span&gt; &lt;span class="nx"&gt;contas&lt;/span&gt; &lt;span class="nx"&gt;WHERE&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="nx"&gt;FOR&lt;/span&gt; &lt;span class="nx"&gt;UPDATE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="nx"&gt;realiza&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;transferência&lt;/span&gt;
&lt;span class="nx"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse comando bloqueia a linha da conta até que a transação finalize, impedindo que qualquer outra operação use esses dados simultaneamente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exemplo prático com Kotlin + Spring Boot
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1. Entidade
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.persistence.Entity&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.persistence.Id&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.math.BigDecimal&lt;/span&gt;

&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Conta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;saldo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Repositório com lock pessimista
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;br.com.pessimistic_locking.entity.Conta&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.persistence.LockModeType&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.data.jpa.repository.JpaRepository&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.data.jpa.repository.Lock&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.data.jpa.repository.Query&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.data.repository.query.Param&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.stereotype.Repository&lt;/span&gt;

&lt;span class="nd"&gt;@Repository&lt;/span&gt;
&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ContaRepository&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Conta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LockModeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PESSIMISTIC_WRITE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT c FROM Conta c WHERE c.id = :id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;findByIdForUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Conta&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Serviço de transferência
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;br.com.pessimistic_locking.repository.ContaRepository&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.transaction.Transactional&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.beans.factory.annotation.Autowired&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.stereotype.Service&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.math.BigDecimal&lt;/span&gt;

&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ContaService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;contaRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ContaRepository&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Transactional&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;transferir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;origemId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destinoId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;origem&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contaRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByIdForUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;origemId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;destino&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contaRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByIdForUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;destinoId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;origem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;saldo&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Saldo insuficiente"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;origem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;saldo&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;valor&lt;/span&gt;
        &lt;span class="n"&gt;destino&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;saldo&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;valor&lt;/span&gt;

        &lt;span class="n"&gt;contaRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;origem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;contaRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;destino&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Controller
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Controller&lt;/span&gt;
&lt;span class="nd"&gt;@RequestMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/contas"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ContaController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ContaService&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/{id}/extrato"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Conta&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extrato&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Controller&lt;/span&gt;
&lt;span class="nd"&gt;@RequestMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/transferencias"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TransferenciasController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ContaService&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@PostMapping&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;RequestTransferencia&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transferir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;origemId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;origemId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;destinoId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;destinoId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;valor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;valor&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;RequestTransferencia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;origemId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;destinoId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;

&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Carga de contas na base de dados
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nc"&gt;INSERT&lt;/span&gt; &lt;span class="nc"&gt;INTO&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;conta&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;saldo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;VALUES&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;INSERT&lt;/span&gt; &lt;span class="nc"&gt;INTO&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;conta&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;saldo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;VALUES&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Consultado extrato
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;curl&lt;/span&gt; &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="nc"&gt;GET&lt;/span&gt; \
  &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//localhost:8080/contas/1/extrato \&lt;/span&gt;
  &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;User-Agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;insomnia&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;11.0.0&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Executando transferência
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;!/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bash&lt;/span&gt;

&lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s"&gt;"Iniciando teste de concorrência com duas transferências simultâneas..."&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nc"&gt;Requisição&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;em&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;curl&lt;/span&gt; &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="nc"&gt;POST&lt;/span&gt; \
  &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//localhost:8080/transferencias \&lt;/span&gt;
  &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; \
  &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;User-Agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;insomnia&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;11.0.0&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; \
  &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"origemId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"destinoId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"valor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;span class="nc"&gt;PID1&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nc"&gt;Requisição&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;em&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;curl&lt;/span&gt; &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="nc"&gt;POST&lt;/span&gt; \
  &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//localhost:8080/transferencias \&lt;/span&gt;
  &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; \
  &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;User-Agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;insomnia&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;11.0.0&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; \
  &lt;span class="p"&gt;--&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"origemId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"destinoId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"valor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;span class="nc"&gt;PID2&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nc"&gt;Aguardar&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;duas&lt;/span&gt; &lt;span class="n"&gt;finalizarem&lt;/span&gt;
&lt;span class="n"&gt;wait&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nc"&gt;PID1&lt;/span&gt;
&lt;span class="n"&gt;wait&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nc"&gt;PID2&lt;/span&gt;

&lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s"&gt;"Teste finalizado."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Limitações do Pessimistic Locking
&lt;/h2&gt;

&lt;p&gt;Apesar de poderoso essa abordagem possui seus trade-offs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Desempenho impactado

&lt;ol&gt;
&lt;li&gt;Transações concorrentes esperam o lock ser liberado&lt;/li&gt;
&lt;li&gt;Isso pode gerar lenditão em horários de pico&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Risco de deadlock

&lt;ol&gt;
&lt;li&gt;Quando duas transações travam recursos em ordem diferentes, pode haver impasse.&lt;/li&gt;
&lt;li&gt;O banco precisa cancelar uma das transações para liberar o sistema.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Acoplamento ao banco

&lt;ol&gt;
&lt;li&gt;Estratégia dependente de como o banco lida com transações&lt;/li&gt;
&lt;li&gt;Pode dificultar migrações para bancos NoSQL ou cloud-native&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  Alternativas para sistema maiores
&lt;/h2&gt;

&lt;p&gt;Em sistema com alta demanda, é possível utilizar estratégias diferentes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Optimistic Locking - usa versionamento e detecta conflitos antes de salvar&lt;/li&gt;
&lt;li&gt;Filas com mensageria (Kafka, RabbitMQ) - serializa operações por recurso&lt;/li&gt;
&lt;li&gt;Event Sourcing e CQRS - separa leitura e escrita, com controle por eventos.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  O que essa experiência me ensinou
&lt;/h2&gt;

&lt;p&gt;Eu não conhecia essa abordagem a fundo antes da entrevista. Mas, graças ao avaliador que soube conduzir com clareza, paciência e provocações construtiva, saí mais preparado tecnicamente do que entrei.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Mais importante do que sair de uma entrvista com um “sim” ou "não”, é sair com uma certeza de que você cresceu como profissional.&lt;/p&gt;

&lt;p&gt;Essa experiência me ensinou sobre concorrência, sobre engenharia e também sobre humildade: não saber algo é normal - o que define você é &lt;strong&gt;o que você faz depois disso&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Repositório do projeto → &lt;a href="https://github.com/dev-rodrigues/Pessimistic-Locking" rel="noopener noreferrer"&gt;aqui 😀&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
