<?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>DEV Community: Jordi Henrique Silva</title>
    <description>The latest articles on DEV Community by Jordi Henrique Silva (@jordihofc).</description>
    <link>https://dev.to/jordihofc</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%2F890829%2F4bba66b7-3066-4263-b9f0-ae0272436838.png</url>
      <title>DEV Community: Jordi Henrique Silva</title>
      <link>https://dev.to/jordihofc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jordihofc"/>
    <language>en</language>
    <item>
      <title>Como utilizar o Type aliases do Kotlin para facilitar o Design de Código</title>
      <dc:creator>Jordi Henrique Silva</dc:creator>
      <pubDate>Mon, 30 Sep 2024 15:59:24 +0000</pubDate>
      <link>https://dev.to/jordihofc/como-utilizar-o-type-aliases-do-kotlin-para-facilitar-o-design-de-codigo-5baj</link>
      <guid>https://dev.to/jordihofc/como-utilizar-o-type-aliases-do-kotlin-para-facilitar-o-design-de-codigo-5baj</guid>
      <description>&lt;p&gt;No último ano pude trabalhar em diferentes projetos que utilizaram Kotlin como linguagem principal, nisso aprendi diferentes funcionalidades que impactaram em maior facilidade de escrita de código mais legível e manutenível. Uma das funcionalidades que me chamaram a atenção foi &lt;a href="https://kotlinlang.org/docs/type-aliases.html" rel="noopener noreferrer"&gt;&lt;em&gt;Type aliases&lt;/em&gt;&lt;/a&gt; que permite a nomeação para outros tipos já existentes, facilitando o uso em qualquer ponto do sistema. Como, por exemplo, quando você tem dados como Email e CPF mapeados como tipo primitivo &lt;em&gt;string&lt;/em&gt;, mas que poderiam ter seus próprios tipos dado suas características específicas. &lt;/p&gt;

&lt;h2&gt;
  
  
  Como uso o &lt;em&gt;Type aliases&lt;/em&gt; para melhorar meu código?
&lt;/h2&gt;

&lt;p&gt;Bom imagine que você tenha que representar uma Pessoa no sistema, e a mesma deve ter nome, cpf e email como dados. Abaixo tem um exemplo de como poderia ser modelada a classe Pessoa.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&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;cpf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&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;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este mapeamento já é suficiente para que o sistema funcione, porém, no desenvolvimento do projeto, outros diversos pontos do sistema, iram trabalhar com conceito de email e cpf. Então utilizaremos o &lt;em&gt;type aliases&lt;/em&gt; para definir um tipo para cada um dos conceitos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;typealias&lt;/span&gt; &lt;span class="nc"&gt;Email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="k"&gt;typealias&lt;/span&gt; &lt;span class="nc"&gt;Cpf&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora em qualquer ponto do sistema é possível declarar os tipos de &lt;em&gt;Email&lt;/em&gt; e &lt;em&gt;Cpf&lt;/em&gt;. Veja como fica a declaração da classe pessoa com os novos tipos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&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;cpf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Cpf&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;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Legal né? Isso abre muitas possibilidades de uso, até mesmo para aplicação de Polimorfismo. &lt;/p&gt;

&lt;h3&gt;
  
  
  Olhá só o que aconteceu comigo...
&lt;/h3&gt;

&lt;p&gt;Estava realizando a correção de um débito técnico, na situação era um sistema em Spring Boot com Kotlin, e a tarefa era fazer com que uma API de atualização existente fosse mais dinâmica. O que quero dizer com mais dinâmica, é que a API no momento recebia um contrato com N critérios de atualização, porém, conforme a quantidade de critérios crescia, o código tornava-se mais difícil e longo de ser lido. E para piorar, o comportamento da função não era alterado!!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UpdatePersonService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;EntityManager&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;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;personId&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;updateModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UpdatePersonModel&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;person&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;personId&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;updateModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasName&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
          &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;updateModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&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;updateModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasCpf&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
          &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpf&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;updateModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpf&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;updateModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasEmail&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
          &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;updateModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&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;p&gt;O código acima ilustra a situação, onde a função &lt;code&gt;update()&lt;/code&gt; tem a responsabilidade de atualizar os dados da entidade &lt;em&gt;Pessoa&lt;/em&gt;, cada dado para ser atualizado, deve atender o seu critério, como, por exemplo, para atualizar o cpf, a propriedade &lt;code&gt;hasCpf&lt;/code&gt; deverá ser verdadeira. e assim sucessivamente até o fim dos critérios.  O comportamento desta função é garantir que o contrato de atualização entre &lt;code&gt;Person&lt;/code&gt; e &lt;code&gt;UpdatePersonModel&lt;/code&gt; esta sendo cumprido.&lt;/p&gt;

&lt;h3&gt;
  
  
  Foi aí que vi a oportunidade de combinar as coisas...
&lt;/h3&gt;

&lt;p&gt;Ao observar o código podemos visualizar cada condição com o seguinte contrato de função.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updateModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UpdatePersonModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E após visualizá-los, percebi que o código poderia ser refatorado para possibilitar o crescimento de critérios sem causar mais alterações no código da função &lt;code&gt;update()&lt;/code&gt;. Então, o primeiro passo foi transformar cada condição em uma função, e organizá-las em um arquivo chamado &lt;code&gt;UpdatePersonFunction.kt&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;updateName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updateModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UpdatePersonModel&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;updateModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasName&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;updateModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="p"&gt;}&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;updateDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updateModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UpdatePersonModel&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;updateModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasDocument&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;updateModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;
    &lt;span class="p"&gt;}&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;updateAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updateModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UpdatePersonModel&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;updateModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasAddress&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
          &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;updateModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&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;p&gt;Em seguida, utilizei o &lt;em&gt;type aliases&lt;/em&gt; do kotlin para definir o contrato da função como tipo &lt;code&gt;UpdatePersonAction&lt;/code&gt;, que representa uma função que receba dois argumentos, um do tipo &lt;code&gt;Person&lt;/code&gt; e outro do tipo &lt;code&gt;UpdatePersonModel&lt;/code&gt;, e não possui retorno.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;typealias&lt;/span&gt; &lt;span class="nc"&gt;UpdatePersonAction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updateModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UpdatePersonModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após a definição do tipo, bastou criar uma coleção de funções do tipo &lt;code&gt;UpdatePersonAction&lt;/code&gt;, e adicionar a referência de cada uma delas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UpdatePersonService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="nd"&gt;@PersistenceContext&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt;   &lt;span class="py"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;EntityManager&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;updatePersonFunctions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UpdatePersonAction&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
      &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;updateName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;updateDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;updateAddress&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;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;personId&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;updateModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UpdatePersonModel&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;person&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;personId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="n"&gt;updatePersonFunctions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;updateAction&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;{&lt;/span&gt;
          &lt;span class="n"&gt;updateAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updateModel&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="p"&gt;}&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E por fim, alterar o código da função &lt;code&gt;update()&lt;/code&gt;, para percorrer a coleção, e fazer a invocação da cada função com os argumentos. Desta forma, às funções que atenderem seus critérios serão aplicadas e as demais não. &lt;/p&gt;

&lt;p&gt;Espero que este artigo sirva de alguma inspiração para você explorar novas funções e cada vez mais escrever códigos que sejam fáceis de serem lidos e entendidos.&lt;/p&gt;

&lt;p&gt;E você já utilizou o &lt;em&gt;type aliases&lt;/em&gt; em algum projeto? Deixa nos cometários como foi sua experiência!&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>desginofcode</category>
      <category>oop</category>
      <category>objectoriented</category>
    </item>
    <item>
      <title>Evoluindo sua estratégia de Testes para FileUpload com Spring Test e Amazon S3</title>
      <dc:creator>Jordi Henrique Silva</dc:creator>
      <pubDate>Wed, 10 Jul 2024 11:15:01 +0000</pubDate>
      <link>https://dev.to/jordihofc/evoluindo-sua-estrategia-de-testes-para-fileupload-com-spring-test-e-amazon-s3-22ap</link>
      <guid>https://dev.to/jordihofc/evoluindo-sua-estrategia-de-testes-para-fileupload-com-spring-test-e-amazon-s3-22ap</guid>
      <description>&lt;p&gt;Em meu último artigo, discutimos como pode ser implementado um serviço de file upload utilizando Spring Boot e Amazon S3. Lá entendemos quais são as preocupações e ferramentas necessárias para permitir que você customize as regras de como gerenciar seus arquivos. Se você não leu ainda, recomendo que o faça, antes de ler este artigo.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/jordihofc" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F890829%2F4bba66b7-3066-4263-b9f0-ae0272436838.png" alt="jordihofc"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/jordihofc/desvendando-o-segredo-como-implementar-file-upload-com-spring-boot-e-amazon-s3-1jd1" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Desvendando o Segredo: Como Implementar File Upload com Spring Boot e Amazon S3&lt;/h2&gt;
      &lt;h3&gt;Jordi Henrique Silva ・ Jun 20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#aws&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#spring&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#java&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#amazons3&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Após publicar o artigo, percebi que diversas pessoas tinham dúvidas de quais os possíveis caminhos para escrever testes que tragam segurança e confiabilidade para o File Upload. &lt;/p&gt;

&lt;p&gt;Dado a isso, hoje vamos bater um papo sobre como podemos evoluir a estratégia de testes, partindo dos testes unitários até os queridos testes de integração. &lt;/p&gt;

&lt;p&gt;OBS: A escolha da estratégia de testes pode variar dado contexto, como, por exemplo, familiaridade da equipe com tecnologia, conhecimento técnico, limitação do ambiente, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Escrevendo Testes de Unidade
&lt;/h2&gt;

&lt;p&gt;Essa é de longe a estratégia mais utilizada pelos Dev's e Empresas, e seu principal objetivo é validar se cada pedaço de código isolado funciona como esperado. Então é comum que nessa técnica as características estruturais do código sejam validadas, esta técnica é indicada para sistemas que possuem logicas complexas, como, por exemplo: cálculo de juros, folha de ponto, e qualquer outro sistema que possua logica com alto uso de estrutura de decisão e repetição.&lt;/p&gt;

&lt;p&gt;Outro ponto interessante sobre os testes de unidade, é que como seu objetivo é validar a menor unidade de código possível, normalmente quando a classe ou método que esta sendo testado possui dependências internas ou externas costuma-se simular esse comportamento, e isso é o que chamamos de Mock's ou Dublê. Normalmente quando se escreve testes desta categoria, utilizamos ferramentas como JUnit, Hamcrast, assertJ e Mockito. &lt;/p&gt;

&lt;h3&gt;
  
  
  Vamos ver como funciona na prática
&lt;/h3&gt;

&lt;p&gt;Antes de iniciar a construção do teste, vamos relembrar o código de produção, que foi divido em duas classes, a classe &lt;em&gt;FileUpload&lt;/em&gt; representa o arquivo que será armazenado no S3, e &lt;em&gt;FileStorageService&lt;/em&gt; que representa o serviço que se integrará com Amazon S3 e fará o upload dos arquivos.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;


&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;FileUpload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MultipartFile&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;){}&lt;/span&gt;

&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FileStorageService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;S3Template&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"myBucketName"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FileUpload&lt;/span&gt; &lt;span class="n"&gt;fileUpload&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fileUpload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getInputStream&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;randomUUID&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="nc"&gt;S3Resource&lt;/span&gt; &lt;span class="n"&gt;uploaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Não foi possivel realizar o upload do documento"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;O código acima pode ser resumido em, inicialmente, o método &lt;code&gt;upload()&lt;/code&gt; recebe uma instância da classe &lt;em&gt;FileUpload&lt;/em&gt; que carrega um &lt;em&gt;MultipartFile&lt;/em&gt; recebido mediante uma chamada HTTP. Em seguida, o arquivo a ser salvo no bucket, é aberto dentro do &lt;em&gt;&lt;strong&gt;try-with-resources&lt;/strong&gt;&lt;/em&gt;, caso alguma exceção seja lançada na abertura do arquivo, a mesma é tratada, e a streaming de dados é fechada automaticamente, causando a interrupção da execução do método. Caso contrário é criado uma chave de acesso ao arquivo, e envia respectivamente o arquivo para o Amazon S3, finalizando o método retornando sua chave de acesso.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@ExtendWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MockitoExtension&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FileStorageServiceUnitTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Mock&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;S3Template&lt;/span&gt; &lt;span class="n"&gt;s3Template&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Mock&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;MultipartFile&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Mock&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;S3Resource&lt;/span&gt; &lt;span class="n"&gt;uploaded&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;bucketName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"myBucketName"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="nd"&gt;@DisplayName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Deve fazer o upload de um arquivo"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;t0&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//cenario&lt;/span&gt;
        &lt;span class="nc"&gt;FileUpload&lt;/span&gt; &lt;span class="n"&gt;fileUpload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FileUpload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;FileStorageService&lt;/span&gt; &lt;span class="n"&gt;fileStorageService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FileStorageService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s3Template&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s3Template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="o"&gt;())).&lt;/span&gt;&lt;span class="na"&gt;thenReturn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uploaded&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;//acao&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;acesseKeyFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fileStorageService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileUpload&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;//validacao&lt;/span&gt;
        &lt;span class="n"&gt;assertNotNull&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acesseKeyFile&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Iniciamos a escrita dos testes unitários, definindo a classe que será responsável por agrupar os casos de testes, a mesma deve ser preparada para oferecer suporte a execução do Mockito junto a ferramenta JUnit. Dado a isso, o primeiro passo será anotar a classe com &lt;code&gt;@ExtendWith(MockitoExtension.class)&lt;/code&gt;, que permitirá a definição de dubles (&lt;em&gt;Mocks&lt;/em&gt;) através das anotações &lt;code&gt;@Mock&lt;/code&gt; e &lt;code&gt;@Spy&lt;/code&gt;.  Em seguida cuidamos de provisionar as dependências do &lt;em&gt;S3Template&lt;/em&gt; e &lt;em&gt;MultipartFile&lt;/em&gt;, como não iremos provisionar o contexto do Spring, precisaremos definir os comportamentos das mesmas, então definimos as mesmas como &lt;em&gt;Mocks&lt;/em&gt;, anotando previamente cada atributo com &lt;code&gt;@Mock&lt;/code&gt;. Também iremos precisar de simular a resposta do &lt;em&gt;S3Template&lt;/em&gt;, que retorna um objeto do tipo &lt;em&gt;S3Resource&lt;/em&gt;, então também declaramos um atributo previamente anotado com &lt;code&gt;@Mock&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;O próximo passo é a definição do nosso caso de teste, então separamos o testes em 3 etapas, sendo elas: &lt;strong&gt;cenário&lt;/strong&gt;, &lt;strong&gt;ação&lt;/strong&gt; e &lt;strong&gt;validação&lt;/strong&gt;. Na etapa &lt;strong&gt;cenário&lt;/strong&gt; vamos definir as dependências que o teste precisa para executar, então é inicialmente criado uma instância do &lt;em&gt;FileUpload&lt;/em&gt;, que recebe o duble do &lt;em&gt;MultipartFile&lt;/em&gt; que definimos anteriormente. Como o &lt;em&gt;FileStorageService&lt;/em&gt; executa o método &lt;code&gt;upload()&lt;/code&gt; do &lt;code&gt;S3Template&lt;/code&gt; precisamos definir este comportamento através da API de &lt;em&gt;When&lt;/em&gt;  do Mockito, que cria um gatilho para que &lt;strong&gt;Quando&lt;/strong&gt; o método &lt;code&gt;s3Template.upload()&lt;/code&gt; seja chamado, com qualquer entrada, o objeto &lt;em&gt;S3Resource&lt;/em&gt; seja retornado na resposta. Por fim, instanciamos a &lt;em&gt;FileStorageService&lt;/em&gt; que recebe via construtor o nome do bucket e o duble do &lt;em&gt;S3Template&lt;/em&gt;.  &lt;/p&gt;

&lt;p&gt;Já na etapa de &lt;strong&gt;ação&lt;/strong&gt;, é onde vamos executar o nosso código de produção para colher o resultado e aplicar uma validação de modo a garantir que o resultado é equivalente ao esperado. Então simplesmente invocamos o método &lt;code&gt;fileStorageService.upload()&lt;/code&gt; e armazenamos sua resposta a fim de executar as validações na próxima etapa. Na etapa de &lt;strong&gt;validação&lt;/strong&gt;, vamos validar se realmente existe uma chave de acesso ao recurso criado, e para isso utilizaremos o &lt;code&gt;assertNotNull&lt;/code&gt; já que a criação da chave de acesso não está em nosso controle.&lt;/p&gt;
&lt;h3&gt;
  
  
  Ai eu te pergunto, como esse teste me ajuda a garantir que o &lt;em&gt;FileUpload&lt;/em&gt; funciona como deveria?
&lt;/h3&gt;

&lt;p&gt;Em suma, esse teste ajuda muito pouco a trazer segurança e qualidade ao desenvolvimento da funcionalidade, já que o mesmo, busca cobrir apenas características estruturais do código, como operadores If, Else, For, Try, entre outros. Sem falar que um teste de unidade, toda execução é feita em memória, o que torna o extremamente rápido, porém, como nem tudo são flores, deixamos de validar toda a configuração da integração com Spring Cloud AWS e Amazon S3. Sem falar que a integração propriamente dita nem é executada, podendo ocasionar em erros não esperados durante a execução em produção.&lt;/p&gt;

&lt;p&gt;A solução para o problema descrito acima, é aproximar o cenário do teste ao ambiente de produção, e a boa notícia é que o Spring Test fornece uma ampla gama de ferramentas que permitem executar o contexto do framework e se integrar a containers e dependências externas, como eu disse em: &lt;br&gt;
 &lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/jordihofc" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F890829%2F4bba66b7-3066-4263-b9f0-ae0272436838.png" alt="jordihofc"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/jordihofc/3-dicas-para-criar-uma-estrategia-moderna-de-testes-para-microsservicos-spring-boot-49a5" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;3 dicas para criar uma estratégia moderna de Testes para Microsserviços Spring Boot&lt;/h2&gt;
      &lt;h3&gt;Jordi Henrique Silva ・ Feb 19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#spring&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#integrationtests&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#microservices&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  Evoluindo para um Teste de Integração com Spring Test, Test Containers e LocalStack
&lt;/h3&gt;

&lt;p&gt;Antes de sair modificando o teste, precisamos adequar nossas dependências, então já adicione em sua ferramenta de build, as dependências de Spring Test Containers, TestContainers JUnit e LocalStack.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;
&lt;span class="nd"&gt;@Testcontainers&lt;/span&gt;
&lt;span class="nd"&gt;@ActiveProfiles&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FileStorageServiceTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;S3Template&lt;/span&gt; &lt;span class="n"&gt;s3Template&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${s3.bucket.name}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"classpath:uploads/file.txt"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;FileStorageService&lt;/span&gt; &lt;span class="n"&gt;fileStorageService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Container&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;LocalStackContainer&lt;/span&gt; &lt;span class="no"&gt;LOCALSTACK_CONTAINER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LocalStackContainer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;DockerImageName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"localstack/localstack"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;withServices&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;S3&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@DynamicPropertySource&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;registerS3Properties&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DynamicPropertyRegistry&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"spring.cloud.aws.endpoint"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;LOCALSTACK_CONTAINER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEndpointOverride&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;S3&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"spring.cloud.aws.region.static"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;LOCALSTACK_CONTAINER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRegion&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"spring.cloud.aws.credentials.access-key"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;LOCALSTACK_CONTAINER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAccessKey&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"spring.cloud.aws.credentials.secret-key"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;LOCALSTACK_CONTAINER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSecretKey&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@BeforeEach&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;s3Template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createBucket&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@AfterEach&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;tearDown&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;s3Template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deleteBucket&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="nd"&gt;@DisplayName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Deve fazer o upload de um arquivo"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;t0&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//cenario&lt;/span&gt;
        &lt;span class="nc"&gt;MockMultipartFile&lt;/span&gt; &lt;span class="n"&gt;fileRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MockMultipartFile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInputStream&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="nc"&gt;FileUpload&lt;/span&gt; &lt;span class="n"&gt;fileUpload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FileUpload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileRequest&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;//acao&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;keyAcessResource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fileStorageService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileUpload&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;//validacao&lt;/span&gt;
        &lt;span class="n"&gt;assertTrue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s3Template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;objectExists&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyAcessResource&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;s3Template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deleteObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyAcessResource&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A primeiro momento, precisamos adaptar nossa classe de testes, para suportar a inicialização do &lt;em&gt;ApplicationContext&lt;/em&gt; do Spring. Também é necessário que as configurações referentes ao ambiente de teste sejam aplicadas, então anotaremos a classe com &lt;code&gt;@SpringBootTest&lt;/code&gt; e &lt;code&gt;@ActiveProfiles("test")&lt;/code&gt;. Em seguida, vamos indicar para o JUnit que essa classe de teste faz o uso de containers gerenciados pelo TestContainers, então adicionamos a anotação &lt;code&gt;@Testcontainers&lt;/code&gt; sobre a assinatura da classe.&lt;/p&gt;

&lt;p&gt;O próximo passo, é indicar as dependências que utilizaremos durante a escrita do teste, para este caso, iremos precisar obter do &lt;em&gt;ApplicationContext&lt;/em&gt; instâncias do &lt;em&gt;S3Template&lt;/em&gt;, &lt;em&gt;FileStorageService&lt;/em&gt;, então utilizaremos a injeção de dependências via campo, anotando os atributos previamente com &lt;code&gt;@Autowired&lt;/code&gt;. E para finalizar esta etapa, colheremos a propriedade: &lt;code&gt;s3.bucket.name&lt;/code&gt; para obter o nome do bucket. &lt;br&gt;
E também iremos colher o arquivo que será enviado ao bucket, então anotaremos os campos, &lt;em&gt;file&lt;/em&gt; e &lt;em&gt;bucketName&lt;/em&gt; respectivamente com a anotação &lt;code&gt;@Value&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Na etapa atual, nosso objetivo é criar a infraestrutura necessária para que o JUnit seja capaz de instanciar um container da LocalStack, expondo o serviço do Amazon S3, e em seguida integrando o mesmo no &lt;em&gt;ApplicationContext&lt;/em&gt;. A primeiro momento iremos definir o container, através da abstração &lt;em&gt;LocalStackContainer&lt;/em&gt;, como desejamos que o JUnit gerencie o ciclo de vida do container, anotaremos este atributo com &lt;code&gt;@Container&lt;/code&gt;. Por fim, não menos importante, vamos conectar o S3 a Aplicação através do método &lt;code&gt;registerS3Properties()&lt;/code&gt; que cuida, de atualizar os valores das properties de conexão com os dados do container.&lt;/p&gt;

&lt;p&gt;Após preparar a aplicação para simular o cenário mais próximo de produção possível, podemos partir para escrita dos testes. Lembramos que uma característica essencial dos testes é que devem garantir o isolamento um dos outros. Então como boa prática, foi definido os métodos &lt;code&gt;setUp()&lt;/code&gt; e &lt;code&gt;tearDown()&lt;/code&gt;, que irão auxiliar na construção e destruição do ambiente para cada caso de teste. O método &lt;code&gt;setUp()&lt;/code&gt; está previamente anotado com &lt;code&gt;@BeforeEach&lt;/code&gt; para ser executado antes de cada teste e crie o bucket com nome definido. Já o método &lt;code&gt;tearDown()&lt;/code&gt; está anotado com &lt;code&gt;@AfterEach&lt;/code&gt; para ser executado ao fim de cada teste, destruindo o bucket criado anteriormente. &lt;/p&gt;

&lt;p&gt;Por fim, iniciaremos a construção do nosso teste de integração, então ao dividiremos o teste, nas queridas etapas de &lt;strong&gt;cenário&lt;/strong&gt;, &lt;strong&gt;ação&lt;/strong&gt; e &lt;strong&gt;validação&lt;/strong&gt;. No &lt;strong&gt;cenário&lt;/strong&gt;, criaremos um objeto do tipo &lt;em&gt;MockMultipartFile&lt;/em&gt; informando o nome do campo, e o file obtido no &lt;em&gt;classpath&lt;/em&gt; anteriormente. Por fim, instanciamos um &lt;em&gt;FileUpload&lt;/em&gt; recebendo o &lt;em&gt;MultipartFile&lt;/em&gt; criado. Na etapa de &lt;strong&gt;ação&lt;/strong&gt;, invocamos o serviço de storage e realizamos o upload do arquivo, recebendo como retorno a sua chave de acesso. já na etapa de &lt;strong&gt;validação&lt;/strong&gt;, podemos validar se o arquivo realmente existe no bucket, garantindo assim o comportamento esperado nosso serviço, através da validação do efeito colateral.  Você pode ter mais detalhes do código neste &lt;a href="https://github.com/JordiHOFC/sample-storage-file-with-spring-aws-s3" rel="noopener noreferrer"&gt;repositório&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Durante este artigo podemos observar que existem diversas estratégias para escrita de testes em um sistema que realizar o upload de arquivos em um bucket do S3. E que ao fazer escolha de uma estratégia de testes, devemos avaliar antes, as limitações que o ambiente nós trazemos, o nível de competência da equipe com as ferramentas e até mesmo o tempo disponível para uma determinada entrega. &lt;/p&gt;

&lt;p&gt;Também foi discutido como os testes unitários favorecem uma cobertura de características relacionadas a estrutura do código, e são ótimos validadores da escrita de lógicas que se baseiam em estruturas de decisão, controle, repetição. Porém, são péssimos candidatos a cobertura de características que estejam relacionadas as operações de I/O, já que por natureza todo teste de unidade é rodado em memória e normalmente possuem alto uso de mocks, que evitam o comportamento real do sistema.&lt;/p&gt;

&lt;p&gt;Sendo assim, uma estratégia interessante para contexto onde a maioria das operações são de I/O é o uso de testes de integração, já que os mesmos, se aproximam do cenário de produção, permitindo a antecipação de erros e bugs, que poderiam ser descobertos nas etapas de CI e CD e pelo usuário final do sistema.&lt;/p&gt;

&lt;p&gt;Agora me diz você, qual estratégia faz mais sentido em seu contexto? Deixe nos comentários.&lt;/p&gt;

&lt;p&gt;Não se esqueça de me seguir nas redes sociais para receber mais dicas sobre Engenharia de Software e Desenvolvimento Backend.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/jordihofc" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/JordihSilva" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.instagram.com/silvah.jordi/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>spring</category>
      <category>aws</category>
      <category>amazons3</category>
    </item>
    <item>
      <title>Desvendando o Segredo: Como Implementar File Upload com Spring Boot e Amazon S3</title>
      <dc:creator>Jordi Henrique Silva</dc:creator>
      <pubDate>Thu, 20 Jun 2024 23:29:16 +0000</pubDate>
      <link>https://dev.to/jordihofc/desvendando-o-segredo-como-implementar-file-upload-com-spring-boot-e-amazon-s3-1jd1</link>
      <guid>https://dev.to/jordihofc/desvendando-o-segredo-como-implementar-file-upload-com-spring-boot-e-amazon-s3-1jd1</guid>
      <description>&lt;p&gt;Não é de hoje que empresas e também pessoas tem necessidade de armazenar e gerenciar arquivos de forma segura e eficiente. Um bom exemplo disso é que até hoje existe uma alta procura por produtos como SDD, Pendrives, HDD externos. Sem falar que empresas como Amazon, Google, Apple, Microsoft vendem armazenamento em cloud em diferentes escalas e preços. &lt;/p&gt;

&lt;p&gt;Storage de arquivo pode atender clientes em diversas categorias, desde os usuários comuns por meio de produtos como Google Drive, One Drive e ICloud. Até mesmo empresas de todos os tamanhos, com a necessidade de utilizar sistemas customizados para gerenciar seus arquivos.  &lt;/p&gt;

&lt;p&gt;Os clientes da Cloud Amazon Web Service, podem escrever suas aplicações para se integrar ao &lt;a href="https://aws.amazon.com/pt/s3/" rel="noopener noreferrer"&gt;Amazon Simple Storage Service (S3)&lt;/a&gt;, o serviço de armazenamento distribuído seguro e escalável, que pode ser acessado de diferentes aplicativos e plataformas por meio de API's.&lt;/p&gt;

&lt;p&gt;Hoje é possivel escrever aplicações nas mais diversas linguagens e plataformas, utilizando o SDK da Amazon para gerenciar seus arquivos. E para nós javeiros não é diferente, podemos nos basear no SDK ou utilizar algum framework como Micronaut, Quarkus e Spring, para nos auxiliar neste processo.&lt;/p&gt;

&lt;h2&gt;
  
  
  E a pergunta que não quer calar? Como Implemento um FileUploader com Spring Boot e Amazon S3?
&lt;/h2&gt;

&lt;p&gt;A solução deste problema é dividida em duas etapas, a primeira etapa é onde iremos realizar a configuração da aplicação para utilizar o Amazon S3 em um projeto Spring. E por fim, será construído o serviço responsável por fazer o upload dos arquivos no bucket.&lt;/p&gt;

&lt;h3&gt;
  
  
  1 - Configurando a Aplicação para Utilizar o Amazon S3
&lt;/h3&gt;

&lt;p&gt;Então, vamos lá! O &lt;a href="https://docs.awspring.io/spring-cloud-aws/docs/3.0.0/reference/html/index.html#using-amazon-web-services" rel="noopener noreferrer"&gt;Spring Cloud AWS&lt;/a&gt; é um módulo construído para abstrair o uso de serviços da AWS para aplicativos Spring Boot. Inclusive existe um starter para aplicações que utilizaram apenas o modulo para uso do Amazon S3, se trata do &lt;a href="https://docs.awspring.io/spring-cloud-aws/docs/3.0.0/reference/html/index.html#spring-cloud-aws-s3" rel="noopener noreferrer"&gt;Spring Cloud AWS S3&lt;/a&gt;, que oferecem a diferentes formas de acessar e utilizar o Bucket no sistema, e também conta com a abstração &lt;code&gt;S3Template&lt;/code&gt; que torna simples e intuitivo o ato de executar operações de consulta, inserção e deleção no S3. Então o primeiro passo é adicioná-la nas dependências do projeto. &lt;/p&gt;

&lt;p&gt;Em seguida, iremos trabalhar na configuração do Spring Cloud AWS S3, a fim de permitir que o mesmo faça o acesso ao bucket no S3, para isso devemos informar o Endpoint de acesso ao bucket, a região que o bucket esta disponível, e as credenciais de acesso à conta da AWS. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;

&lt;span class="py"&gt;spring.cloud.aws.endpoint&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${AWS_ENDPOINT:http://s3.localhost.localstack.cloud:4566} &lt;/span&gt;
&lt;span class="py"&gt;spring.cloud.aws.region.static&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${AWS_REGION:us-east-1}&lt;/span&gt;
&lt;span class="py"&gt;spring.cloud.aws.credentials.access-key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${AWS_ACCESS_KEY:localstackAccessKeyId}&lt;/span&gt;
&lt;span class="py"&gt;spring.cloud.aws.credentials.secret-key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${AWS_SECRET_KEY:localstackSecretAccessKey}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;No código acima, iniciamos definindo o endpoint de acesso ao bucket, através da propriedade &lt;code&gt;spring.cloud.aws.endpoint&lt;/code&gt;. Em seguida, definimos que a região em que o bucket se encontrar por via da propriedade &lt;code&gt;spring.cloud.aws.region.static&lt;/code&gt;, por fim definimos as credenciais de acesso à conta através das propriedades &lt;code&gt;spring.cloud.aws.credentials.access-key&lt;/code&gt; e &lt;code&gt;spring.cloud.aws.credentials.secret-key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Os demais detalhes do projeto pode ser consultados no repositorio abaixo:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/JordiHOFC" rel="noopener noreferrer"&gt;
        JordiHOFC
      &lt;/a&gt; / &lt;a href="https://github.com/JordiHOFC/sample-storage-file-with-spring-aws-s3" rel="noopener noreferrer"&gt;
        sample-storage-file-with-spring-aws-s3
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      This repository is about how to implemented FileUpload Application with Spring Boot and Amazon S3
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  2 - Escrevendo o Serviço de Upload de Arquivo
&lt;/h3&gt;

&lt;p&gt;Nesta etapa vamos definir o código responsável por receber um arquivo e fazer o upload dele no Amazon S3. Antes de iniciar a construção é preciso entender como funciona a postagem de um arquivo. No Amazon S3 você pode definir um bucket. Eu ouvi certo? Um balde? Sim, no S3 o bucket é um tipo de diretório que irá armazenar seus objetos, que podem ser arquivos, imagens, musicas, e qualquer outro tipo de objeto independente do seu  tipo de mídia. Esse objeto deve ter uma chave única de acesso (Key), que irá ser utilizada para geração de links de download, atualização e deleção do objeto.&lt;/p&gt;

&lt;p&gt;Então iremos iniciar o processo, definindo a abstração responsável por representar um Arquivo. Em seguida, definiremos o &lt;code&gt;bean&lt;/code&gt; &lt;code&gt;FileUploadService&lt;/code&gt; que será responsável por gerenciar as operações sobre os Arquivos. E por fim, iremos construir o método responsável por fazer Upload no bucket.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;FileUpload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MultipartFile&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;){}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;O código apresentado acima, define uma abstração que representa um arquivo que irá ser persistido no storage de arquivos. O FileUpload tem um único campo do tipo &lt;a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/multipart/MultipartFile.html" rel="noopener noreferrer"&gt;&lt;code&gt;MultipartFile&lt;/code&gt;&lt;/a&gt; que é uma abstração do Spring Web para representar um arquivo que foi submetido através de uma requisição web.&lt;/p&gt;

&lt;p&gt;Após a definição da abstração seguiremos para definição do serviço de upload. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FileStorageService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;S3Template&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"myBucketName"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FileUpload&lt;/span&gt; &lt;span class="n"&gt;fileUpload&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fileUpload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getInputStream&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;randomUUID&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="nc"&gt;S3Resource&lt;/span&gt; &lt;span class="n"&gt;uploaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Não foi possivel realizar o upload do documento"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;O código pode ser resumido da seguinte forma, primeiro definimos a classe &lt;strong&gt;FileStorageService&lt;/strong&gt; como um &lt;code&gt;bean&lt;/code&gt; de serviço, através da anotação &lt;code&gt;@Service&lt;/code&gt;, em seguida, injetamos o S3Template através do campo template previamente anotado com &lt;code&gt;@Autowired&lt;/code&gt;. Em seguida, é definido o método upload, que retorna a String referente a chave de consulta do arquivo no Bucket, e recebe o FileUpload que será persistido.  &lt;/p&gt;

&lt;p&gt;A lógica de upload do arquivo é um pouco sensível, dado que manipular arquivos em Java exigem que o arquivo seja fechado, o que é muito fácil de ser esquecido, e pequenos erros, sutis como este, levam a problemas de vazamento de memória que podem prejudicar o funcionamento da aplicação.&lt;/p&gt;

&lt;p&gt;Dado a esta exigência, foi utilizado a instrução &lt;a href="https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html" rel="noopener noreferrer"&gt;try-with-resource&lt;/a&gt;, que tenta abrir o arquivo dentro da cláusula try, caso tenha sucesso, o mesmo é fornecido para uso dentro do escopo da instrução e fechá-lo automaticamente quando a execução for finalizada. Caso contrario podemos fazer um tratamento de exceção personalizado com as instruções catch. No escopo do bloco try, é onde a mágica acontece, então definimos uma chave de acesso única para arquivo, e utilizamos o método upload do S3Template, informando o bucket de destino, a chave de acesso, e o inputStream do arquivo, caso nenhuma IOException  não seja lançada, retornamos a chave de acesso.&lt;/p&gt;

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

&lt;p&gt;Armazenar e gerenciar arquivos é uma necessidade de muitas pessoas e empresas, não é à toa que os grandes players do mercado como Amazon, Google, Apple, Microsoft e outros, vem lucrando com a venda dos seus produtos de Storage.&lt;/p&gt;

&lt;p&gt;É comum que empresas de todos os tamanhos precisem definir processos e logicas customizadas para o armazenamento e uso de seus arquivos, e uma boa solução é o desenvolvimento de um software que cuide de garantir as regras e comportamentos para o gerenciamento dos seus arquivos, que serão armazenados em um serviço storage distribuído como Amazon S3.&lt;/p&gt;

&lt;p&gt;O Spring Cloud AWS é um módulo focado em abstrair o uso de serviços da Cloud AWS para aplicações Spring, e contém um starter que facilita a integração com os buckets do S3. A construção de um serviço que manipule arquivos deve ser feita com cuidado, já que conforme os arquivos são abertos, os mesmos devem ser fechados, para não ocasionar vazamentos de memória. Porém, podemos mitigar isso, utilizando o try-with-resource, que abstrai o processo de fechamento do arquivo.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>spring</category>
      <category>java</category>
      <category>amazons3</category>
    </item>
    <item>
      <title>3 dicas para criar uma estratégia moderna de Testes para Microsserviços Spring Boot</title>
      <dc:creator>Jordi Henrique Silva</dc:creator>
      <pubDate>Mon, 19 Feb 2024 20:42:52 +0000</pubDate>
      <link>https://dev.to/jordihofc/3-dicas-para-criar-uma-estrategia-moderna-de-testes-para-microsservicos-spring-boot-49a5</link>
      <guid>https://dev.to/jordihofc/3-dicas-para-criar-uma-estrategia-moderna-de-testes-para-microsservicos-spring-boot-49a5</guid>
      <description>&lt;p&gt;A verdade é que uma entrega de software com qualidade, é formada por uma base de código que tenha bom design, e que permita o crescimento e evolução do software com simplicidade, qualidade e segurança durante seu ciclo de vida. Junto há uma bateria de testes que agregue segurança na evolução e manutenção do software. E que também ofereça a oportunidade de antecipar o encontro de bugs e anomalias.&lt;/p&gt;

&lt;p&gt;E no contexto de arquiteturas distribuídas ou altamente distribuídas, como no caso de Microsserviços, onde majoritariamente os desafios estão concentrados em garantir que os diferentes sistemas comuniquem-se bem entre-si. Estas dificuldades também se estendem para os testes, e caso optemos por utilizar apenas testes de unidade, deixaremos de captar falhas referentes a processos básicos do nosso sistema, como serialização, desserialização, mapeamento dos clientes HTTP e até mesmos que nossas operações juntos a bancos de dados, brokers MQ sejam feitas como eu já disse em: &lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/jordihofc" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F890829%2F4bba66b7-3066-4263-b9f0-ae0272436838.png" alt="jordihofc"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/jordihofc/3-motivos-do-porque-testes-unitarios-nao-sao-suficientes-para-microservices-com-spring-boot-33lk" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;3 motivos do porquê testes unitários não são suficientes para Microservices com Spring Boot&lt;/h2&gt;
      &lt;h3&gt;Jordi Henrique Silva ・ Dec 14 '22&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#spring&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#java&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#microservices&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Pensando nisto, separei 3 dicas para te ajudar, a escrever testes que aumentem a segurança e corretude dos sistemas em que trabalha.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Entenda o contexto e conheças as dependências da sua solução
&lt;/h3&gt;

&lt;p&gt;O primeiro passo para escrever um codigo de teste,  é ter bom entendimento sobre o código de produção, principalmente quando nos apoiamos em frameworks de IoC/DI como Spring, onde utilizamos outros beans em composição com nossa lógica de negócio. Então aqui aparecem ferramentas de cache, bancos de dados relacionais e não relacionais, clientes HTTP, gRPC e outros. &lt;/p&gt;

&lt;p&gt;Entender como as dependências interagem entre-si lhe permite identificar o que é necessário para o seu cenário de teste. Desde quais dependências precisaram ser simuladas e também quais são as entradas necessárias para o teste.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Utilize ferramentas de testes modernas como TestContainers e WireMock para simular suas dependências
&lt;/h3&gt;

&lt;p&gt;Essa dica é como a cereja do bolo, traz refinamento a sua suíte de testes, pois, com uso destas ferramentas, você pode simular de maneira fidedigna os serviços que serão integrados em produção. Ao aproximar a execução dos testes ao cenário de produção, conseguiremos validar detalhes como, conexão com a ferramenta, configuração dos beans via &lt;em&gt;properties&lt;/em&gt; ou programaticamente. Ao utilizar ferramentas como TestContainers podemos fazer com que nosso testes executem seus cenários que exigem o Redis como Cache Distribuído, através de containers gerenciados pelo JUnit. Ou até mesmo serviços da AWS como S3, SQS, SNS e DynamoDB. &lt;/p&gt;

&lt;p&gt;Já quando você opta por utilizar Wiremock, você ganhará confiança em suas integrações REST, pois, Wiremock permite que você simule a resposta de Servidores HTTP, através de seus contratos. Desta forma, aquele cliente que você mapeou a chamada a uma API, será executado, e ainda na execução dos testes você terá validações se o contrato é respeitado, ou se o seu código de produção exige que modificações sejam feitas.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Escreva seus testes nas etapas de Cenário, Ação e Validação ou AAA Pattern.
&lt;/h3&gt;

&lt;p&gt;A ideia do aqui é basicamente separar seu teste em 3 estágios, cenário, ação e validação, ou como a literatura diz em &lt;a href="https://www.amazon.com.br/Unit-Testing-Principles-Practices-Patterns/dp/1617296279" rel="noopener noreferrer"&gt;Unit Testing Principles, Practices, and Patterns&lt;/a&gt;, Arrange, Act e Assert. &lt;/p&gt;

&lt;p&gt;O primeiro estágio é onde nos preocupamos em provisionar o &lt;em&gt;Cenário&lt;/em&gt; necessário para execução da nossa solução. Este estágio é onde criamos os objetos de domínio que serão utilizados como massa para o teste, caso nossa solução utilize dependências externas, devemos provisioná-las ou Mockar as mesmas, de modo a definir seus comportamentos. &lt;/p&gt;

&lt;p&gt;Não existe segredo para o estágio de &lt;em&gt;Ação&lt;/em&gt;, nele é onde invocamos a execução do nosso código de produção que desejamos testar, e armazenamos a resposta para ser utilizada na próxima etapa. E por fim, iremos cuidar da etapa de &lt;em&gt;Validação&lt;/em&gt; do teste, escrevendo um conjunto de sentenças que tragam garantia que o comportamento executado pelo código de produção, entrega o esperado, utilizaremos os &lt;em&gt;asserts&lt;/em&gt; para validar se os resultados correspondem ao planejado.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vamos ver na prática?
&lt;/h2&gt;

&lt;p&gt;Para entender um pouco melhor vamos imaginar a seguinte situação, dado um fluxo de um sistema que realiza venda de produtos, é necessário que você implemente um serviço que calcule o frete para uma determinada venda. Neste calculo será necessário que você se integre a API da transportadora para calcular uma simulação de frete. Para executar a simulação, será necessário que obtenha o peso total do pedido, e o código postal, do destino, para seja calculado um valor e estimativa de data de entrega para o frete. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;

POST www.suatransportadora.com/v1/frete
{
    "origem": "32569-455",
    "destino": "39856-151",
    "valor": 45.5,
    "peso": 784,
    "comprimento": 25,
    "altura": 10,
    "largura": 4
}


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A chamada HTTP acima, corresponde ao contrato que deve ser utilizado para se integrar a REST API de fretes da transportadora. E abaixo você encontrará uma implementação do serviço de fretes em Spring Boot.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CalculaFreteService&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ProdutoRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;TransportadoraClient&lt;/span&gt; &lt;span class="n"&gt;freteClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${default.cep.origem}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;cepOrigem&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;readOnly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Frete&lt;/span&gt; &lt;span class="nf"&gt;calcular&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SolicitacaoCompra&lt;/span&gt; &lt;span class="n"&gt;solicitacaoCompra&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Produto&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;produtos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findAllById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;solicitacaoCompra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;produtos&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;altura&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;produtos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alturaComparator&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getAltura&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;largura&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;produtos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;larguraComparator&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getLargura&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;comprimento&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;produtos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comprimentoComparator&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getComprimento&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;valor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;produtos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Produto:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getPreco&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;reduce&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ZERO&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;BigDecimal:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;peso&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;produtos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Produto:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getPeso&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;reduce&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ZERO&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;BigDecimal:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;freteRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FreteSimulationRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;altura&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;largura&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;comprimento&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;peso&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;cepOrigem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;solicitacaoCompra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cepDestino&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;valor&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;simulacao&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;freteClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;simulate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;freteRequest&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Frete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;simulacao&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FeignException&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FeignClientException&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BadRequest&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidIntegrationException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Entradas nao suportadas"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;O codigo apresentado acima, pode ser resumido em, o sistema recebe o cep de destino e os identificadores dos produtos que serão entregues. Em seguinda os produtos são buscados no banco de dados, e as caracteristicas de largura, altura e comprimento do recipiente que armazenará os produtos, junto ao peso, e o valor do produto são obtidos,  para serem utilizados como entrada da integração da API de fretes. Após obter estes dados, uma chamda HTTP é feita a API da Transportadora para calcular o custo do frete. Caso a requisição seja executada com sucesso, os dados do frete são retornados, caso contrário uma resposta de erro será retornada.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aplicando o conhecimento
&lt;/h3&gt;

&lt;p&gt;Agora que conhecemos nosso serviço, e também aprendemos sobre como extrair melhor valor de ferramentas modernas de testes, podemos somar com conhecimento de Spring Test e JUnit para construir teste que aproximem a execução do serviço em produção. &lt;/p&gt;

&lt;p&gt;Então antes da escrita do primeiro teste,  é necessário identificar  e provisionar as dependências exigidas para execução no serviço, conforme discutimos isso em Entenda o contexto e conheças as depêndencias da sua solução. Conhecer os pontos de integração do sistema, permite que você habilite em seu sistema, a capacidade de fornecer as mesmas.  Então em primeiro lugar habilitaremos novas dependências ao sistema, como o starter do &lt;a href="https://cloud.spring.io/spring-cloud-contract/2.1.x/multi/multi__spring_cloud_contract_wiremock.html" rel="noopener noreferrer"&gt;Spring Cloud Contract WireMock&lt;/a&gt; em nosso projeto, atravrés  de seu starter. Também é necessário adicionar as depêndecias os starters do &lt;a href="https://spring.io/blog/2023/06/23/improved-testcontainers-support-in-spring-boot-3-1/" rel="noopener noreferrer"&gt;Spring TestContainers&lt;/a&gt;, JUnit Test Containers. Como nosso sistema se integrar a um PostgreSQL, também será necessário adicionar o TestContainers Postrgres.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;
&lt;span class="nd"&gt;@ActiveProfiles&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@AutoConfigureWireMock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="c1"&gt;//habilita que uma porta aletoria seja utilizada no WireMockServer&lt;/span&gt;
&lt;span class="nd"&gt;@Testcontainers&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CalculaFreteServiceTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Container&lt;/span&gt;
    &lt;span class="nd"&gt;@ServiceConnection&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;PostgreSQLContainer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PostgreSQLContainer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;DockerImageName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"postgres:latest"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;CalculaFreteService&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ProdutoRepository&lt;/span&gt; &lt;span class="n"&gt;produtoRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@BeforeEach&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;produtoRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deleteAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;


    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;deveCalcularOFrete&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;JsonProcessingException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//cenario&lt;/span&gt;
        &lt;span class="nc"&gt;Produto&lt;/span&gt; &lt;span class="n"&gt;produto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Produto&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"Joystick Xbox One"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Joystick wireless"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TEN&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Caracteristica&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"17.7"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"17.8"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"7.2"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"300"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;produtoRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;produto&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;stubFor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;urlPathMatching&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/v1/frete"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;willReturn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;aResponse&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withStatus&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CREATED&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withHeader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"content-type"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;APPLICATION_JSON_VALUE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withBody&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toJson&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getSimulacaoDoFrete&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
                &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;


        &lt;span class="c1"&gt;//acao&lt;/span&gt;
        &lt;span class="nc"&gt;Frete&lt;/span&gt; &lt;span class="n"&gt;frete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;calcular&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SolicitacaoCompra&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;produto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;()),&lt;/span&gt; &lt;span class="s"&gt;"32604189"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="c1"&gt;//validacao&lt;/span&gt;
        &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"42.35"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;frete&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValor&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;assertNotNull&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frete&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEstimativaDeEntrega&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"32605125"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frete&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrigem&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"32604189"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frete&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDestino&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;O primeiro passo deste teste é habilitar o uso do &lt;em&gt;ApplicationContext&lt;/em&gt; na execução do seu teste, através da anotação &lt;code&gt;@SpringBootTest&lt;/code&gt;, dado a isso, o processo de startup do sistema será iniciado, instanciando todos os beans necessários para a execução do sistema. Esta anotação faz com que durante a execução do teste uma instância do Servidor seja criada, então caso alguma configuração de beans feita programaticamente ou através de properties esteja incorreta, você será capaz de perceber já que para o &lt;em&gt;ApplicationContext&lt;/em&gt; seja iniciado, todas &lt;em&gt;BeansFactorys&lt;/em&gt; devem ser criadas com sucesso, caso contrario o startup será abortado. &lt;/p&gt;

&lt;p&gt;Em seguida habilitamos que as configurações referentes ao profile de test seja utilizada, e também habilitamos a capacidade do Test de Usar WireMock Servers através das respectivas anotações: &lt;code&gt;@ActiveProfiles("test")&lt;/code&gt; e &lt;code&gt;@AutoConfigureWireMock(port = 0)&lt;/code&gt;. E para encerrar as configurações habilitamos a classe de teste a capacidade de utilizar containers docker através de TestContainers, com a anotação &lt;code&gt;@TestContainers&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Após finalizar as configurações, iremos provisionar as dependências necessárias ao nosso teste, iniciaremos provisionando um container do PostgreSQL. Utilizamos a anotação &lt;code&gt;@Container&lt;/code&gt; para habilitar que o TestContainers utilize seu orquestrador para instância o containers do PostgreSQL. Também adicionaremos a anotação &lt;code&gt;@ServiceConnection&lt;/code&gt; para crie o bean de conexão com JDBC e integre o container ao contexto durante a execução do teste. E por fim, não menos importante, fornecemos instâncias do &lt;em&gt;CalculaFreteService&lt;/em&gt;, &lt;em&gt;ObjectMapper&lt;/em&gt; e o &lt;em&gt;ProdutoRepository&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Ainda da escrita do teste, precisamos garantir que os testes rodem de maneira independente, isto significa, que o contexto de execução de um teste, nunca deverá interferir sobre a execução de outro método de teste, então isto torna necessário que antes que cada teste execute o estado do banco seja o mesmo, justificando a utilização do método &lt;code&gt;deleteAll()&lt;/code&gt; do &lt;code&gt;ProdutoRepository&lt;/code&gt;. E então estamos prontos para implementar os nossos testes.&lt;/p&gt;

&lt;p&gt;O caso de teste implementado visa cobrir o caminho feliz ou caminho sem erros do código, então sua responsabilidade é garantir que uma simulação de frete seja calculada. Neste momento é onde nos conectamos com as dicas I e  II. Iniciamos utilizando a etapa de cenário para fornecer as ambiente necessário para execução do teste, então adicionados ao banco de dados, o registro de um produto. Em seguida provisionados um MockServer com WireMock que irá atender o contrato de resposta da REST API de Frete da Transportadora.  Na etapa de ação, iremos executar uma chamada ao método &lt;code&gt;calcular&lt;/code&gt; do &lt;code&gt;CalculaFreteService&lt;/code&gt; e armazenar a resposta, para o produto e código postal de destino informado. E para finalizar iremos validar, se o valor, cep de origem e destino são iguais ao esperado, e se existe uma estimativa de entrega. Você pode ter mais detalhes do código neste &lt;a href="https://github.com/JordiHOFC/sales-simullator" rel="noopener noreferrer"&gt;repositório&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Trabalhar em arquiteturas altamente distribuídas é um grande desafio por diferentes motivos, como garantir que a rede estará disponível, se existe banda, ou baixa latência, e também que os sistemas presentes na arquitetura se comuniquem de forma correta. &lt;/p&gt;

&lt;p&gt;Dado a isso, a escrita de testes para estes serviços não pode ser baseadas apenas em requisitos funcionais do sistema, mas também devem garantir que os requisitos não funcionais, como operações em rede, operações em disco, serialização e etc, sejam, executados conforme esperado. E isto exige uma boa habilidade de escrita de testes. Visando isso aprendemos que escrever testes que se aproxime a execução do software em produção, proporciona a capacidade de executar validações que só seriam feitas no, startup da aplicação ou na execução do código em produção, antecipando o encontro de bugs e anomalias.&lt;/p&gt;

&lt;p&gt;Na primeira e segunda dica aprendemos que conhecer as dependências do código que será testado, facilita o encontro de ferramentas que auxiliem a prover as dependências necessárias para execução do sistema, durante a execução do teste. Já na terceira dica aprendemos que dividir o teste em etapas pode, lhe ajudar a planejar melhor seu teste.&lt;/p&gt;

&lt;p&gt;Quando combinamos as dicas, criamos uma estratégia poderosa e moderna de testes, proporcionando que o software seja mantido e evoluído com segurança e qualidade software.&lt;/p&gt;

</description>
      <category>spring</category>
      <category>testing</category>
      <category>integrationtests</category>
      <category>microservices</category>
    </item>
    <item>
      <title>3 Estratégias para sincronizar cache na camada de aplicação</title>
      <dc:creator>Jordi Henrique Silva</dc:creator>
      <pubDate>Tue, 09 May 2023 02:26:07 +0000</pubDate>
      <link>https://dev.to/jordihofc/3-estrategias-para-sincronizar-cache-na-camada-de-aplicacao-1hn1</link>
      <guid>https://dev.to/jordihofc/3-estrategias-para-sincronizar-cache-na-camada-de-aplicacao-1hn1</guid>
      <description>&lt;p&gt;No último artigo começamos a discutir sobre Cache, falamos sobre quais são os perigos e obstáculos de soluções de cache local em ambientes clusterizados. Também conversamos sobre o módulo Spring Cache, que oferecer abstrações para uso de caching na camada de aplicação em projetos Spring Boot.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/jordihofc" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F890829%2F4bba66b7-3066-4263-b9f0-ae0272436838.png" alt="jordihofc"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/jordihofc/turbinando-seus-microsservicos-com-spring-cache-e-redis-4p1f" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Turbinando seus Microsserviços com Spring Cache e Redis&lt;/h2&gt;
      &lt;h3&gt;Jordi Henrique Silva ・ Feb 28 '23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#spring&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#cache&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#java&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#redis&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Hoje iremos voltar a discutir sobre cache, porém, vamos nos concentrar em estratégias para manter o cache e a camada de dados sincronizadas. &lt;/p&gt;

&lt;h2&gt;
  
  
  Cache e sua importância
&lt;/h2&gt;

&lt;p&gt;A verdade é que sistemas enterprise são automações que auxiliam no processo de escalada de um negócio. Isto significa que quanto mais o negócio crescer, quantos mais clientes o negócio atender, maior será o requisito de performance do sistema. O que quero dizer com isso é que otimizações precisaram ser feitas, e existem inúmeras técnicas que podem ser aplicadas, inclusive inserir uma camada de Caching.&lt;/p&gt;

&lt;p&gt;A partir do momento que implementamos uma camada de cache, podemos usufruir de diversos benefícios, como diminuição do custo de consulta para &lt;code&gt;O(1)&lt;/code&gt;.E caso o banco de dados fique indisponível, o sistema continuará disponível para solicitações de leituras. &lt;/p&gt;

&lt;p&gt;Uma camada de cache bem implementada deve se garantir que, leituras obsoletas não sejam permitidas. E que os dados estejam sincronizados com a fonte da verdade, no caso o banco de dados. &lt;/p&gt;

&lt;p&gt;Os provedores de cache tem diversas formas para invalidação, entre elas as mais comuns são por tempo de duração em cache. E por limite de armazenamento, sendo possível definir uma capacidade máxima de objetos ou uma capacidade máxima em bytes.&lt;/p&gt;

&lt;p&gt;Contudo, o que viemos discutir hoje são técnicas que você poderá aplicar para garantir que os dados presentes em cache não são obsoletos em relação à camada de dados.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estratégias para sincronização de Cache
&lt;/h2&gt;

&lt;p&gt;A primeira estratégia que vamos conhecer é conhecida como Cache a-side, nesta estratégia a camada de aplicação é responsável por gerenciar as camadas de dados e cache.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache a side
&lt;/h3&gt;

&lt;p&gt;Nesta estratégia a camada de aplicação gerencia manualmente o cache e o banco de dados. Isto significa que quando um registro é buscado no banco, o mesmo é inserido no cache, o tornando disponível na próxima solicitação de leitura. O mesmo se aplica para quando um dado é atualizado.&lt;/p&gt;

&lt;p&gt;Para entender melhor como, observe o diagrama abaixo, que demostra o comportamento da aplicação em uma consulta de dados.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Foaidsqxbcwklubb3u46m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Foaidsqxbcwklubb3u46m.png" alt="Diagrama de fluxo, sobre comportamento da estratégia cache-a-side"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O diagrama demostra que primeiro o dado é procurado em cache, como o mesmo obtêm na resposta que o dado não existe, é feito a consulta no banco de dados, em seguida o dado retornado do banco é inserido no cache.&lt;/p&gt;

&lt;p&gt;Está é uma implementação simples, porém, misturar a lógica de negócio com lógica de acesso a cache, quebra o Princípio da Responsabilidade Única (SRP) do &lt;a href="https://www.zup.com.br/blog/design-principle-solid" rel="noopener noreferrer"&gt;SOLID&lt;/a&gt;. Então a boa prática é que seja utilizado interceptors de programação orientada a aspectos &lt;a href="https://docs.spring.io/spring-framework/docs/4.3.15.RELEASE/spring-framework-reference/html/aop.html" rel="noopener noreferrer"&gt;AOP&lt;/a&gt;, para esconder a lógica de acesso à camada de cache, como, por exemplo, a API do Spring Cache.&lt;/p&gt;

&lt;h3&gt;
  
  
  Read Through
&lt;/h3&gt;

&lt;p&gt;Nesta estratégia a camada de aplicação, irá se comunicar apenas com a API de Cache, e toda a lógica de gerenciamento de banco de dados será de total responsabilidade da camada de cache. As vantagens desta estratégia é que agora existe apenas um ponto de acesso a dados, então se torna mais simples a escrita e manutenção do código.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ftdsmdh3endmw2z8n7xgj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ftdsmdh3endmw2z8n7xgj.png" alt="Diagrama de fluxo, sobre comportamento da estratégia read-through"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O diagrama apresentado acima, descreve o comportamento do caso de cache miss. Nesta situação o sistema, iniciará consultando se os dados existem em cache, caso não existam os dados são buscados no banco de dados, e inseridos em cache. &lt;/p&gt;

&lt;h3&gt;
  
  
  Write Behind
&lt;/h3&gt;

&lt;p&gt;Esta estratégia é recomendada para situações onde &lt;strong&gt;NÃO&lt;/strong&gt; é necessário ter uma forte consistência, pois as atualizações são feitas em lotes. Isto significa que as operações serão enfileiras, e executadas de uma só vez.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fms3mswg03msxke9n22n4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fms3mswg03msxke9n22n4.png" alt="Diagrama de fluxo, sobre comportamento da estratégia write-behind"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No diagrama a cima, é resumido o comportamento da estratégia de atualização de Caching para escrita. Nesta estratégia as operações de atualização são enfileiradas, até que o &lt;code&gt;flush()&lt;/code&gt; for executado, liberando todas as operações ao fim da transação.&lt;/p&gt;

&lt;p&gt;O contexto de persistência da JPA, utiliza write-behind para garantir que todas as transições de estado das entidades sejam liberadas no fim da Transação em execução, ou antes, de uma consulta.&lt;/p&gt;

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

&lt;p&gt;Implementar uma camada de caching em um projeto permite o sistema ganhe desempenho e disponibilidade, já que o custo de processamentos complexos são reduzidos, proporcionando uma maior capacidade de atender solicitações simultâneas. &lt;/p&gt;

&lt;p&gt;Porém, antes de gozar das vantagens é necessário entender quais são as melhores práticas para manter a camada da cache sincronizada com o banco de dados. Existem diversas estratégias que podem ser utilizadas, desde fazer com que a aplicação gerêncie o cache e o banco de dados como é o caso de cache a side. Caso trabalhar com duas fontes de dados seja trabalhoso, outra solução é definir uma API de Caching que gerencie a camada de dados, implementando Read-Through. E caso não seja necessária uma consistência forte, a camada de caching poderá enfileira as solicitações e dispará-las de uma única vez, como é Write-Behind. &lt;/p&gt;

&lt;p&gt;Lembrando que decisões como essas são complexas, e devem ser tomadas de acordo com contexto do time, projeto e tecnologias utilizadas.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>caching</category>
      <category>java</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Turbinando seus Microsserviços com Spring Cache e Redis</title>
      <dc:creator>Jordi Henrique Silva</dc:creator>
      <pubDate>Tue, 28 Feb 2023 12:11:14 +0000</pubDate>
      <link>https://dev.to/jordihofc/turbinando-seus-microsservicos-com-spring-cache-e-redis-4p1f</link>
      <guid>https://dev.to/jordihofc/turbinando-seus-microsservicos-com-spring-cache-e-redis-4p1f</guid>
      <description>&lt;p&gt;No decorrer da última década vivenciamos a grande ascensão da arquitetura de microsserviços, antes os times de tecnologia optavam por desenvolver grandes sistemas responsáveis por todas as funcionalidades do negócio. E hoje a principal solução é dividir o domínio do negócio em vários escopos menores e criar pequenos sistemas independentes que os atendam. As vantagens da arquitetura de microsserviços variam desde proporcionar que diferentes times desenvolvam software em ritmos isolados, em até escalar o desempenho de determinadas funcionalidades. &lt;/p&gt;

&lt;p&gt;Na busca pela melhora de desempenho uma das ferramentas utilizadas é o cache que permite aumentar a velocidade de consumo a dados frequentemente acessados. Uma das principais estratégias para o uso de cache é armazenar os dados na memória local do servidor a fim de evitar consultas regulares no banco de dados, APIs externas ou até mesmo processamento de cálculos custosos.&lt;/p&gt;

&lt;p&gt;Ao tratar de microsserviços é comum que dado um requisito de disponibilidade ou desempenho seja necessário mais de uma instância dando vida a um cluster. Junto ao ambiente clusterizado vem problemas na implementação de cache local, já que cada uma das instâncias é um processo independente, e a memória não é compartilhada, ocasionando que o cache fique inconsistente entre uma instância e outra. Neste tipo de situação a estratégia de cache local não é suficiente, pois agora quando um dado é armazenado em cache por uma instância, a outra instância deverá ter acesso em sua próxima consulta.  Então se faz necessário a utilização de um provedor de cache distribuído como &lt;a href="https://hazelcast.com/open-source-projects/" rel="noopener noreferrer"&gt;Hazelcast&lt;/a&gt; e &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Redis é um banco de dados não relacional de estrutura de chave (key) e valor (value). Sua principal característica é a alta velocidade de acesso devido a sua implementação em memória. E uma das suas principais utilizações é como cache distribuído, já que é possível ter diversas conexões paralelas. Ao optar por utilizar Redis como provedor de cache em uma aplicação é necessário que seja utilizada suas APIs na base de código, gerando uma grande dependência, já que se em algum momento se torne necessário a troca de provedor, haveria um alto custo em manutenção. Problemas como esse são resolvidos adotando abstrações, independente se são construídas por você ou não. Em uma aplicação Spring Boot é possível se apoiar no módulo do Spring Cache.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://spring.io/guides/gs/caching/" rel="noopener noreferrer"&gt;Spring Cache&lt;/a&gt; é um módulo de extensão do Spring Boot que abstrai a maneira como os dados são inseridos, atualizados e removidos do cache através da sua API no modelo de anotações. Spring Cache oferece suporte ao caching de objetos, então facilita que em diferentes pontos da aplicação compartilhem o mesmo caching.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spring Cache com Redis
&lt;/h2&gt;

&lt;p&gt;Para colher os benefícios do uso do Spring Cache com Redis é necessário que uma série de procedimentos de configuração da ferramenta, me refiro desde a inserção de dependências no pom.xml ou qualquer outro gerenciador de dependências que você utilize. Até a definição de beans e properties. Mas não se preocupe! O intuito deste artigo é demostrar como você pode configurar e utilizar o Redis como cache distribuído através das abstrações Spring Cache.&lt;/p&gt;

&lt;p&gt;Inicialmente é necessário adicionar a Dependência de um módulo do Spring Data para o Redis, em seguida configuraremos a aplicação para trabalhar com cache e definiremos o Redis como provedor. Após as configurações de conexão e definição do Redis como provedor é necessário que definir as configurações responsáveis por limpar o cache, e qual serializer será utilizado durante a comunicação.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configurando a Aplicação
&lt;/h3&gt;

&lt;p&gt;Iniciaremos adicionando a dependência do &lt;a href="https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/" rel="noopener noreferrer"&gt;Spring Data Redis&lt;/a&gt; no projeto, esta dependência ira abstrair o uso do Redis na aplicação, algumas vantagens são client e conector predefinido, serializers personalizados para o tráfego de dados na rede, etc. Abaixo tem um exemplo utilizando Maven.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-data-redis&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O próximo passo é definir as properties de conexão com Redis, e também definir o redis como provedor de cache no arquivo application.yaml.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${HOST:localhost}&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PORT:6379}&lt;/span&gt;
        &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PASSWORD:password}&lt;/span&gt;
    &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
        &lt;span class="na"&gt;cache-names&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;user_payment_methods&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após a definição do Redis como provedor de cache é necessário que configuramos algumas propriedades como o tempo de vida de objetos no cache, e qual será a forma como os objetos serão serializadas. Por padrão no Spring Data Redis é utilizado o &lt;code&gt;JdkSerializationRedisSerializer&lt;/code&gt; que utiliza a serialização nativa da JVM, e esta biblioteca nativa é conhecida por permitir a execução de código remoto, que injetam bytecode não verificado que pode causar a execução de código indesejado.  Dado a isso, a recomendação é que outros serializers sejam utilizados, como, por exemplo, o serializer para Json da biblioteca Jackson.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@EnableCaching&lt;/span&gt;
&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RedisConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;RedisCacheConfiguration&lt;/span&gt; &lt;span class="nf"&gt;defaultCacheConfiguration&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;RedisCacheConfiguration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;defaultCacheConfig&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entryTtl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofMinutes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="c1"&gt;// 2 horas&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;disableCachingNullValues&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;serializeValuesWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fromSerializer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GenericJackson2JsonRedisSerializer&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Iniciamos o código acima, utilizando ao nível de classe a anotação &lt;code&gt;@EnableCaching&lt;/code&gt; responsável por habilitar o uso do módulo de cache em aplicações Spring Boot. Em seguida é utilizado a anotação &lt;code&gt;@Configuration&lt;/code&gt; para definir que a classe fornecerá bens para contexto do Spring. E o bean que ela fornecerá é o responsável por definir as configurações padrões de cache. Durante o método &lt;code&gt;defaultCacheConfiguration()&lt;/code&gt; é definido que os objetos irão permanecer no cache em até 2 horas, e também é definido que valores nulos não serão cacheados, e por fim que os objetos sejam serializados em Json através do &lt;code&gt;GenericJackson2JsonRedisSerializer&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cacheando Resultados
&lt;/h2&gt;

&lt;p&gt;Ao finalizar as configurações estamos prontos para provisionar o uso do cache na aplicação. Então para melhor entendimento, imagine um Market Place, onde um usuário pode ter diferentes formas de pagamento por estabelecimento. E o sistema deve oferecer uma forma de consultar quais são as formas de pagamento deste usuário. Então na primeira vez em que um usuário consultar as formas, o resultado deverá ser armazenado em cache para usos futuros. Veja um exemplo de implementação no código abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentMethodsUserService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Cacheable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user_payment_methods"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#userId"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PaymentMethod&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getPaymentMethodsToUserById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User not exists"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPaymentMethods&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;defaultMethods&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPaymentMethods&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PaymentMethod&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;defaultMethods&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PaymentMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cash"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Money"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para habilitar o cache no método &lt;code&gt;getPaymentMethodsToUserById(UUID userId)&lt;/code&gt; é necessário que anotação &lt;code&gt;@Cacheble&lt;/code&gt; seja utilizada ao nível de método e que os argumentos value e key sejam definidos. O argumento value é responsável por identificar qual cache sera consultado. Já o argumento key é responsável por definir como a chave do cache será criada. Por padrão o primeiro argumento é utilizado, porém, caso necessário é possível criar chaves mais elaboradas através de Spring Expression Language (SpEL).  Para saber mais sobre como as keys são geradas consulte a &lt;a href="https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/cache.html#cache-annotations-cacheable-default-key" rel="noopener noreferrer"&gt;documentação&lt;/a&gt;. Dado que um método é anotado com &lt;code&gt;@Cacheable&lt;/code&gt; o seguinte comportamento é incorporado, primeiro é feito uma consulta se existe a chave no cache, o valor é retornado e o código do corpo do método não é executado. Caso contrario, o código do corpo do método é executado e seu resultado no fim é armazenado em cache para consultas futuras.&lt;/p&gt;

&lt;h3&gt;
  
  
  Invalidando o Cache
&lt;/h3&gt;

&lt;p&gt;Nosso projeto já consegue armazenar novas informações no cache, e realizar consultas, porém, também precisamos identificar a situação onde o cache deve ser invalidado, para que as informações sejam atualizadas. Imagine agora que o sistema está evoluindo e começou a aceitar novas formas de pagamento, e agora o usuário poderá adicionar as mesmas em sua coleção. Então, sempre que o usuário adicionar uma nova forma de pagamento, o seu registro de formas no cache deverá ser apagado. Veja um exemplo de implementação no código abaixo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentMethodsUserService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@CacheEvict&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user_payment_methods"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#userId"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;addPaymentMethods&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PaymentMethod&lt;/span&gt; &lt;span class="n"&gt;paymentMethod&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User not exists"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentMethod&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se observamos no método &lt;code&gt;addPaymentMethods()&lt;/code&gt; foi utilizado a anotação &lt;code&gt;@CacheEvict&lt;/code&gt; que possui os mesmo argumentos que a anotação &lt;code&gt;@Cachable&lt;/code&gt;. Quando um método é anotado &lt;code&gt;@CacheEvict&lt;/code&gt;  o mesmo se torna um gatilho para invalidação do cache, e então caso sua execução seja realizada sem interrupção de alguma exceção o cache é sera invalidado. Caso contrario o cache não sofre alterações.&lt;/p&gt;

&lt;p&gt;Então agora é possível utilizar este cache em diversos pontos do sistema, basta apenas que as anotações e o contrato dos métodos sejam equivalentes a estes. E a aplicação poderá rodar em diversas instâncias e compartilhar o mesmo cache, mantendo a consistência dos dados.&lt;/p&gt;

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

&lt;p&gt;A utilização de cache permite que o aumento de desempenho em um sistema, já que dados que tem alta frequência de acesso podem ser recuperados mais rapidamente, já que não é necessário um processamento custoso, ou aguardar a finalização de operações de I/O. A utilização de cache em memória local é uma estratégia interessante quando apenas uma instância do serviço será executada, pois, a partir do momento em que o ambiente se torna clusterizado a memória de cache não é compartilhada deixando os dados inconsistentes. O que justifica a utilização de um provedor de cache distribuído. Redis é um provedor de cache distribuído que facilita o compartilhamento de dados na rede, e oferece suporte para uso em diferentes linguagens.&lt;/p&gt;

&lt;p&gt;A utilização de cache em uma aplicação Spring Boot pode ser feita através da Spring Cache que permite que o uso de cache seja abstraído por anotações. Spring Cache oferece suporte ao Redis como provedor, e através de configurações é possível rapidamente utilizá-lo como cache distribuído. A anotação &lt;code&gt;@Cacheable&lt;/code&gt; permite que um método faça a consulta por objetos no cache e &lt;code&gt;@CacheEvict&lt;/code&gt; permite que invalidar o cache. Tão importante como inserir, remover e atualizar os dados no cache é definir o tempo de duração para não ser surpreendido com problemas indesejáveis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#redis:serializer" rel="noopener noreferrer"&gt;Spring Data Redis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/cache.html" rel="noopener noreferrer"&gt;Spring Cache 3.2, aqui fornece mais detalhes que a versão atual&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache-annotations-put" rel="noopener noreferrer"&gt;Spring Cache atual&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.javacodemonk.com/custom-ttl-for-spring-data-redis-cache-6b38c550" rel="noopener noreferrer"&gt;Customizer TTL Redis Cache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.baeldung.com/spring-boot-redis-cache" rel="noopener noreferrer"&gt;Spring Cache With Redis: Baeldung&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/jordisilvazup/2ac70a22f0644ed2c73ce5aeee2a7feb" rel="noopener noreferrer"&gt;Principais Aprendizados Spring Cache Redis&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>postgres</category>
      <category>mysql</category>
      <category>database</category>
    </item>
    <item>
      <title>Escrevendo Testes de Integração para Producers Kafka Com Spring Boot e EmbeddedBroker</title>
      <dc:creator>Jordi Henrique Silva</dc:creator>
      <pubDate>Tue, 07 Feb 2023 18:29:03 +0000</pubDate>
      <link>https://dev.to/jordihofc/escrevendo-testes-de-integracao-para-producers-kafka-com-spring-boot-e-embeddedbroker-5b94</link>
      <guid>https://dev.to/jordihofc/escrevendo-testes-de-integracao-para-producers-kafka-com-spring-boot-e-embeddedbroker-5b94</guid>
      <description>&lt;p&gt;Alguns meses atrás foi delegado a mim uma tarefa na qual eu não possuía muita experiencia e familiaridade com tema, se tratava da escrita de um produtor (Producer) do Apache Kafka em uma aplicação Java. &lt;/p&gt;

&lt;p&gt;Minha primeira ideia foi utilizar as próprias bibliotecas do Kafka para implementar este Producer. Porém, após trocar algumas ideias com meus pares chegamos na conclusão que a melhor maneira era utilizar os frameworks do ecossistema Spring que visam facilitar a escrita de serviços de mensageria como o &lt;a href="https://docs.spring.io/spring-kafka/reference/html/#introduction" rel="noopener noreferrer"&gt;&lt;strong&gt;Spring For Apache Kafka&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ao utilizar o Spring for Apache Kafka ganhamos diversos benefícios como a configuração de beans através arquivo de configuração independente se é através de YAML ou Properties. E as abstrações que facilitam a inserção e o consumo de mensagens em tópicos. &lt;/p&gt;

&lt;p&gt;Outra vantagem oferecida junto ao starter Spring For Kafka são as abstrações do &lt;a href="https://docs.spring.io/spring-kafka/reference/html/#testing" rel="noopener noreferrer"&gt;Spring Kafka Test&lt;/a&gt; que permitem que instâncias do Zookeeper e Kafka Embedded sejam levantadas para que você teste de forma integrada seus produtores e consumidores.&lt;/p&gt;

&lt;p&gt;Spring Kafka Test prove ferramentas que auxiliam a escrita de abstrações para facilitar como times escrevam testes integrados junto ao brooker embedded.&lt;/p&gt;

&lt;p&gt;Para melhor entendimento de como escrever bons testes utilizando a ferramenta, imagine que em um ecomerce é necessário que os dados das vendas sejam integrados a outros sistemas através de um tópico  utilizando Apache Kafka.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="nd"&gt;@Validated&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VendaProducer&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${spring.kafka.producer.topic}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;KafkaTemplate&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Venda&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;templateMessage&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;produzir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Valid&lt;/span&gt; &lt;span class="nc"&gt;Venda&lt;/span&gt; &lt;span class="n"&gt;venda&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
        &lt;span class="n"&gt;templateMessage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;venda&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Os demais detalhes do código acima estão disponíveis no seguinte &lt;a href="https://github.com/JordiHOFC/spring-kafka-test-sample" rel="noopener noreferrer"&gt;repositório&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Acima está o código do Producer implementado, que tem a responsabilidade de receber os dados relativos da venda, aplicar validações, e caso nenhuma restrição seja violada a mensagem é inserida no tópico.&lt;/p&gt;

&lt;p&gt;Agora nos concentraremos em configurar o ambiente de testes com Spring Kafka Test para utilizar &lt;code&gt;KafkaEmbeddedBroker&lt;/code&gt; para escrita dos testes de integração.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configurando o &lt;code&gt;KafkaEmbeddedBroker&lt;/code&gt; para fazer Testes Integrados
&lt;/h2&gt;

&lt;p&gt;Então inciaremos criando uma classe de teste para o &lt;code&gt;VendaProducer&lt;/code&gt;, definiremos algumas propriedades para habilitar o uso do EmbeddedBroker. Inicialmente iremos definir:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tópico que sera utilizado&lt;/li&gt;
&lt;li&gt;Numero de partições&lt;/li&gt;
&lt;li&gt;Endereço que o broker esta disponível.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As configurações acima devem ser feitas através da anotação &lt;code&gt;@EmbeddedKafka&lt;/code&gt; ao nível de classe de teste. O tópico a ser utilizado pode ser informado via variável de ambiente com Spring Expresion Language (SpEL) , ou texto hardcoded. Como teremos um único broker levantado junto ao Context do Spring a sugestão é que possuam uma única partição. E quanto ao endereço o Spring Boot oferece uma propriedade que faz o bind do endereço de forma automática para você, basta definir como: &lt;code&gt;spring.kafka.bootstrap-servers&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Veja o exemplo abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;
&lt;span class="nd"&gt;@EmbeddedKafka&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;topics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"vendas"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;partitions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bootstrapServersProperty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"spring.kafka.bootstrap-servers"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VendaProducerTest&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora precisaremos de uma instância do broker para escrever um consumidor para o tópico que iremos publicar uma mensagem. Uma das formas de disponibilizar acesso à abstração do broker embedded é fazer uso da Injeção de Dependência.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;
&lt;span class="nd"&gt;@EmbeddedKafka&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;topics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"vendas"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;partitions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bootstrapServersProperty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"spring.kafka.bootstrap-servers"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VendaProducerTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;EmbeddedKafkaBroker&lt;/span&gt; &lt;span class="n"&gt;broker&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O próximo passo é construir junto a abstração &lt;code&gt;KafkaTestUtils&lt;/code&gt; um Consumidor para o tópico que publicaremos as mensagens. Vamos dividir esta etapa nas seguintes fases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Definição das propriedades do Consumer&lt;/li&gt;
&lt;li&gt;Criação da DefaultConsumerFactory&lt;/li&gt;
&lt;li&gt;Criação do Consumer&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Definição das propriedades do Consumer
&lt;/h3&gt;

&lt;p&gt;Nesta etapa iremos definir configurações básicas do consumidor, como se trabalhará em modo auto_comit, quais são os Deserializer para chave (key) e value (valor) das nossas mensagens, e também quais os pacotes onde o Deserializer escolhido ira se basear para encontrar as classes que representam mensagens no projeto.&lt;/p&gt;

&lt;p&gt;Outra configuração que deve ser feita, é definir a política de leitura de offset, como queremos que todas as mensagens enviadas em um casos de teste sejam consumidas, o ideal é que a propriedade &lt;code&gt;ConsumerConfig.AUTO_OFFSET_RESET_CONFIG&lt;/code&gt; seja definida como &lt;code&gt;earliest&lt;/code&gt; conforme sugerido pela própria &lt;a href="https://docs.spring.io/spring-kafka/docs/3.0.2/reference/html/#junit" rel="noopener noreferrer"&gt;documentação&lt;/a&gt; da Spring Kafka Test.&lt;/p&gt;

&lt;p&gt;Após definir as propriedades, iremos utilizar a abstração &lt;code&gt;DefaultKafkaConsumerFactory&lt;/code&gt;, para obter a instância do consumidor que será utilizado nos testes.&lt;/p&gt;

&lt;p&gt;Veja o código correspondendo na classe &lt;code&gt;VendaProducerTest&lt;/code&gt; demostrada abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;
&lt;span class="nd"&gt;@EmbeddedKafka&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;topics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"vendas"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;partitions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bootstrapServersProperty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"spring.kafka.bootstrap-servers"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VendaProducerTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;EmbeddedKafkaBroker&lt;/span&gt; &lt;span class="n"&gt;broker&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Consumer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;createConsumer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;classType&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;consumerProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;KafkaTestUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;consumerProps&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TOPIC&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;broker&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;consumerProps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonDeserializer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TRUSTED_PACKAGES&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;consumerProps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ConsumerConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;AUTO_OFFSET_RESET_CONFIG&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"earliest"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;DefaultKafkaConsumerFactory&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;consumerFactory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DefaultKafkaConsumerFactory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;consumerProps&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StringDeserializer&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JsonDeserializer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;()&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;consumerFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createConsumer&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora estamos prontos para escrever testes integrados para Producers Kafka.&lt;/p&gt;

&lt;p&gt;Então, voltamos a classe &lt;code&gt;VendaProducerTest&lt;/code&gt;, e criaremos o nosso teste, que cuidará de publicar uma mensagem no tópico de vendas, e em seguida utilizaremos o &lt;code&gt;KafkaEmbeddedBroker&lt;/code&gt; e nosso método &lt;code&gt;createConsumer()&lt;/code&gt; para validar se a mensagem foi inserida como deveria.&lt;/p&gt;

&lt;p&gt;Na construção do teste, iremos dividir em 3 etapas: cenário, ação e validação. No cenário, cuidaremos de disponibilizar o ambiente necessário para o caso de teste executar, nesta etapa, instanciaremos um objeto que represente a mensagem de venda, e habilitaremos para que um Consumer criado atenda as mensagens do tópico especificado. No bloco de ação, iremos disparar o método produz do VendaProducer. E por fim, utilizaremos nosso Consumer para verificar se a mensagem foi inserida e se corresponde a mensagem esperada.&lt;/p&gt;

&lt;p&gt;É importante ressaltar que hoje o EmbeddedKafkaBroker não possui ferramentas para limpeza do topico, então é necessário que o &lt;code&gt;TestContext&lt;/code&gt; seja reconstruído após cada método, a fim de garantir que os testes sejam independentes um aos outros em sua execução. Então adicionaremos ao nível de classe de teste a anotação &lt;code&gt;@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;
&lt;span class="nd"&gt;@EmbeddedKafka&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;topics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"venda"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;partitions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bootstrapServersProperty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"spring.kafka.bootstrap-servers"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@DirtiesContext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;classMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DirtiesContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ClassMode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;AFTER_EACH_TEST_METHOD&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VendaProducerTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;EmbeddedKafkaBroker&lt;/span&gt; &lt;span class="n"&gt;broker&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;VendaProducer&lt;/span&gt; &lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="nd"&gt;@DisplayName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"deve consumir uma mensagem na fila"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;t1&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//cenario&lt;/span&gt;
        &lt;span class="nc"&gt;Consumer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Venda&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;consumer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;createConsumer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Venda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;broker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;consumeFromEmbeddedTopics&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;consumer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"venda"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Venda&lt;/span&gt; &lt;span class="n"&gt;venda&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Venda&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PlayStation 5"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valueOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="c1"&gt;//acao&lt;/span&gt;
        &lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;produzir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;venda&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;//validacao&lt;/span&gt;
        &lt;span class="nc"&gt;ConsumerRecords&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Venda&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;KafkaTestUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRecords&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;consumer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasSize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allMatch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;venda&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Consumer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;createConsumer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;classType&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;consumerProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;KafkaTestUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;consumerProps&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TOPIC&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;broker&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;consumerProps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonDeserializer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TRUSTED_PACKAGES&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;consumerProps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ConsumerConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;AUTO_OFFSET_RESET_CONFIG&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"earliest"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;DefaultKafkaConsumerFactory&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;consumerFactory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DefaultKafkaConsumerFactory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;consumerProps&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StringDeserializer&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JsonDeserializer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;()&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;consumerFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createConsumer&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Testes de integração possibilitam que características relacionadas a integração do código criado com o Spring Context sejam validadas. Caso existam bugs e anomalias devido à má configuração e definição de beans através das properties ou de forma programática, os mesmos terão sua identificação antecipada na etapa de testes.&lt;/p&gt;

&lt;p&gt;Ao testar de forma integrada os Producers estamos favorecendo que a aplicação se comporte o mais próximo possível do contexto de execução real, possibilitando que mais características referentes a integração da lógica e Apache Kafka sejam validadas, antecipando a identificação de bugs. &lt;/p&gt;

&lt;p&gt;E por fim, não menos importante, os testes integrados permitem a validação dos efeitos colaterais do servidor, oferecendo garantia que a mensagem foi inserida no tópico como deveria, ao contrário de Mocks, onde fazemos todas as validações em memória. &lt;/p&gt;

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

&lt;p&gt;Durante este artigo exploramos diversos benefícios das APIs do Spring Kafka Test. Estas APIs nos possibilitam com mínimo de configuração expor uma instância do &lt;code&gt;ZookeeperEmbedded&lt;/code&gt; e &lt;code&gt;KafkaEmbeddedBroker&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;O &lt;code&gt;KafkaEmbeddedBroker&lt;/code&gt; permite que os testes sejam executados mais próximos do cenário de uso real da aplicação. Desta forma, podemos validar características e configurações a fim de antecipar o encontro de bugs e anomalias.&lt;/p&gt;

</description>
      <category>spring</category>
      <category>kafka</category>
      <category>testing</category>
    </item>
    <item>
      <title>3 motivos do porquê testes unitários não são suficientes para Microservices com Spring Boot</title>
      <dc:creator>Jordi Henrique Silva</dc:creator>
      <pubDate>Wed, 14 Dec 2022 13:17:58 +0000</pubDate>
      <link>https://dev.to/jordihofc/3-motivos-do-porque-testes-unitarios-nao-sao-suficientes-para-microservices-com-spring-boot-33lk</link>
      <guid>https://dev.to/jordihofc/3-motivos-do-porque-testes-unitarios-nao-sao-suficientes-para-microservices-com-spring-boot-33lk</guid>
      <description>&lt;p&gt;Escrever testes automatizados já é rotina para maioria dos times de tecnologia, porém, o que não te contaram é que testar microsserviços Spring Boot confiando em apenas de testes de unidade pode diminuir a qualidade do seu sistema. E hoje vou mostrar para você 3 motivos que demostram que apenas testes unitários não são suficientes para microsserviços com Spring Boot.&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;primeiro motivo&lt;/strong&gt; é que testes de unidade costumam ser escritos utilizando Mocks, e se você esta instanciando seus Beans manualmente esta deixando de validar uma série de comportamentos e configurações relacionados ao contexto do Spring.&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;segundo motivo&lt;/strong&gt; é que ao mockar as dependências dos seus Beans, você esta renunciando a validar o comportamento entre a integração de um componente e outro. E isto pode fazer com que, falhas de conexão entre seu banco de dados relacional, mapeamentos de entidade da JPA/Hibernate e controle transacional nunca sejam executados. O impacto disto é que se houver algum erro em alguma destas tarefas o seu cliente/usuario é quem vai encontrar o bug em produção.&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;terceiro motivo&lt;/strong&gt; é que você esta deixando de validar os efeitos colaterais do seu componente de código, isto significa, que você não tem nenhuma garantia que inseriu um registro no banco, uma mensagem na fila, ou um arquivo no storage.&lt;/p&gt;

&lt;p&gt;Para entender melhor, observe o código abaixo, neste temos uma API REST que recebe informações necessárias para o cadastro de um produto na base de dados.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CadastraProdutoController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ProdutoRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt; &lt;span class="no"&gt;LOGGER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CadastraProdutoController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CadastraProdutoController&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ProdutoRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;repository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/produtos"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Transactional&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cadastrar&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nd"&gt;@Valid&lt;/span&gt; &lt;span class="nc"&gt;ProdutoRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;existsBySku&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSku&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="no"&gt;LOGGER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Já existe um produto cadastrado para este sku {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ResponseStatusException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;UNPROCESSABLE_ENTITY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Produto ja cadastrado"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nc"&gt;Produto&lt;/span&gt; &lt;span class="n"&gt;novoProduto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toModel&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;novoProduto&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="no"&gt;LOGGER&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Produto Cadastrado {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;novoProduto&lt;/span&gt;&lt;span class="o"&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="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CREATED&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Um caso de teste de unidade para este Controller, focaria em validar se o contrato esta sendo respeitado, o que significa que instanciaríamos um objeto do tipo ProdutoRequest, e executaríamos a função cadastrar, e por fim iremos validar se a resposta HTTP contém um Status 201 CREATED. Para realizar este teste é necessário que utilizaremos o Mockito para criar um duble do ProdutoRepository, dando comportamento para os métodos: existsBySku e save. Abaixo tem o código correspondente a este caso de teste.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ExtendWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MockitoExtension&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CadastraProdutoControllerUnitTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Mock&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ProdutoRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;CadastraProdutoController&lt;/span&gt; &lt;span class="n"&gt;produtoController&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@BeforeEach&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;produtoController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CadastraProdutoController&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="nd"&gt;@DisplayName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"deve cadastrar um Produto"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;t1&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//Cenario&lt;/span&gt;
        &lt;span class="nc"&gt;ProdutoRequest&lt;/span&gt; &lt;span class="n"&gt;produtoRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProdutoRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"PlayStation 5"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Console e 2 controles"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TEN&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"123567"&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;existsBySku&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;produtoRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSku&lt;/span&gt;&lt;span class="o"&gt;())).&lt;/span&gt;&lt;span class="na"&gt;thenReturn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Produto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;))).&lt;/span&gt;&lt;span class="na"&gt;thenReturn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;produtoRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toModel&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="c1"&gt;//acao&lt;/span&gt;
        &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;produtoController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cadastrar&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;produtoRequest&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// validacao&lt;/span&gt;
        &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CREATED&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStatusCode&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por mais que pareça que o caso de teste é completo, ele não valida características essências da nossa API. Como se ela atende, ao verbo POST, e a URI de "/produtos". Não valida se as informações recebidas no body da requisição HTTP são desserializadas em um objeto Java. Também não é validado se as informações referentes ao produto são registradas no Banco de Dados.&lt;/p&gt;

&lt;p&gt;A verdade é que diversos comportamentos indispensáveis para o funcionamento do software são ignorados no caso de teste. E caso não funcionem como esperado, não serão detectados durante a execução.&lt;/p&gt;

&lt;p&gt;Um teste bem escrito para este Controller consideraria a integração com Application Context do Spring Boot, o que garantiria que Beans de todas as camadas seriam instanciados, e se houver erros de configuração e mapeamento o teste já falharia imediatamente. Outra característica essencial é que a lógica de negócio execute de maneira similar a execução do servidor, isto significa que a API deve ser exposta na Web, e que durante o teste uma requisição HTTP deve ser feita, o que garantiria que um banco de dados seria utilizado na execução, ou seja, como efeito colateral da requisição, um registro deve ser criado na base de dados. Abaixo tem o código correspondente a este caso de teste.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;
&lt;span class="nd"&gt;@ActiveProfiles&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@AutoConfigureMockMvc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;printOnlyOnFailure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CadastraProdutoControllerIntegrationTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ProdutoRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;MockMvc&lt;/span&gt; &lt;span class="n"&gt;mockMvc&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@BeforeEach&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deleteAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="nd"&gt;@DisplayName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"deve cadastrar um Produto"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;t1&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//Cenario&lt;/span&gt;
        &lt;span class="nc"&gt;ProdutoRequest&lt;/span&gt; &lt;span class="n"&gt;produtoRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProdutoRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"PlayStation 5"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Console e 2 controles"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TEN&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"123456"&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;toJson&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;produtoRequest&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;MockHttpServletRequestBuilder&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/produtos"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpHeaders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ACCEPT_LANGUAGE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;//Acao&lt;/span&gt;
        &lt;span class="nc"&gt;ResultActions&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mockMvc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;perform&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;//Validacao&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;andExpectAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isCreated&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                &lt;span class="s"&gt;"deveria conter apenas um registro de produto"&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Como visto anteriormente testes de integração são mais assertivos que testes unitários, pois, testes integrados favorecem que os diversos pontos de integração sejam exercitados. Exercitar a integração com contexto do Spring, favorece que validamos configuração das propriedades e Beans. Também favorece a validação do comportamento de integração as camadas referente a Rede e Banco de Dados.&lt;/p&gt;

&lt;p&gt;No contexto de sistemas distribuídos e microsserviços onde temos pequenas bases de código e a maioria das operações são referentes a entrada e saída (I/O) favorecer testes de unidades não será suficientes para garantir o comportamento do seu software.&lt;/p&gt;

&lt;p&gt;E se você ainda tem dúvida do que leu até aqui, remova todas as anotações de um Controller e rode seus testes de unidade, e se nenhum teste quebrar me conta aqui nos comentários.&lt;/p&gt;

</description>
      <category>spring</category>
      <category>java</category>
      <category>testing</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Criando Sistemas de Reservas Com Versionless Optimistic Locking, Spring Boot e JPA/Hibernate</title>
      <dc:creator>Jordi Henrique Silva</dc:creator>
      <pubDate>Wed, 17 Aug 2022 22:00:00 +0000</pubDate>
      <link>https://dev.to/jordihofc/criando-sistemas-de-reservas-com-versionless-optmistic-locking-spring-boot-e-jpahibernate-7pe</link>
      <guid>https://dev.to/jordihofc/criando-sistemas-de-reservas-com-versionless-optmistic-locking-spring-boot-e-jpahibernate-7pe</guid>
      <description>&lt;p&gt;Como visto no &lt;a href="https://dev.to/jordihofc/criando-sistemas-de-reservas-consistentes-com-optimistic-locking-spring-boot-e-jpahibernate-2h8b"&gt;artigo anterior&lt;/a&gt; o &lt;strong&gt;&lt;em&gt;Optmistic Locking&lt;/em&gt;&lt;/strong&gt; é uma opção para lidar com &lt;a href="https://vladmihalcea.com/race-condition/" rel="noopener noreferrer"&gt;Race Conditions&lt;/a&gt; para cenários com baixo e/ou moderado índice de conflitos. O principal motivo para o uso de bloqueio otimista nestes cenários é porque não haverá um número exagerado de transações conflitantes e o seu banco de dados (BD) não será sobrecarregado com uma alta quantidade de &lt;strong&gt;&lt;em&gt;rollbacks&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Caso seu sistema necessite atualizar atributos distintos de uma entidade simultaneamente o &lt;strong&gt;&lt;em&gt;Optmistic Locking&lt;/em&gt;&lt;/strong&gt; poderá se tornar um empecilho dado que o mecanismo de bloqueio otimista  encara as atualizações como tudo ou nada, ou seja, caso a versão da entidade em memória não corresponda a presente ao BD a transação será abortada independente se os campos atualizados não sejam sobrepostos.&lt;/p&gt;

&lt;p&gt;Para entender melhor, dado a entidade Mesa descrita no código abaixo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Mesa&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;quantidadeDeLugares&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;disponivelParaReserva&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Version&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;//construtores e metodos omitidos&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Imagine que duas transações simultâneas desejem atualizar os atributos &lt;code&gt;quantidadeDeLugares&lt;/code&gt; e &lt;code&gt;disponivelParaReserva&lt;/code&gt;. Ambas consultem o registro e recebam a mesma versão da entidade &lt;code&gt;Mesa&lt;/code&gt;. A primeira transação (&lt;strong&gt;&lt;em&gt;Tx1&lt;/em&gt;&lt;/strong&gt;) atualiza a quantidade de lugares, fazendo com que a versão da mesa seja incrementada. Enquanto a segunda transação (&lt;strong&gt;&lt;em&gt;Tx2&lt;/em&gt;&lt;/strong&gt;) tenta atualizar o status da mesa para indisponível para reserva, porém, dado que &lt;strong&gt;&lt;em&gt;Tx1&lt;/em&gt;&lt;/strong&gt;  incrementou a versão da entidade, agora a versão está obsoleta, portanto, &lt;strong&gt;&lt;em&gt;Tx2&lt;/em&gt;&lt;/strong&gt; sera abortada. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F149cnmgmcv3unqirpzzr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F149cnmgmcv3unqirpzzr.png" alt="Conflito em atualizações com atributos não sobrepostos" width="721" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Em casos de uso onde é necessário que atualizações com atributos não sobrepostos não conflitem, ou quando alterar o schema de dados para inserir um atributo de versão seja caro independente do motivo o Hibernate oferece suporte ao controle de concorrência com mecanismo de &lt;strong&gt;&lt;em&gt;Versionless Optmistic Locking&lt;/em&gt;&lt;/strong&gt; ou bloqueio otimista sem versão em português.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;&lt;em&gt;Version-less optimistic locking&lt;/em&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Os bloqueios otimistas estão relacionados a um atributo incremental para controle de versão da entidade, e é através deste que a integridade e consistência são mantidas em atualizações concorrentes. &lt;/p&gt;

&lt;p&gt;Existem sistemas onde o custo de utilizar uma estratégia de &lt;strong&gt;&lt;em&gt;Optimistic Locking&lt;/em&gt;&lt;/strong&gt; é alto, pois não é simples inserir uma nova coluna na tabela dado que o impacto desta mudança poderá causar anomalias em outras funcionalidades do sistema, ou para realizar mudanças no &lt;em&gt;schema&lt;/em&gt; exija aprovações em um sistema altamente burocrático, ou você trabalha em um sistema legado onde alterar o schema não é uma opção. &lt;/p&gt;

&lt;p&gt;Para estes casos é possível criar um versionamento da entidade a partir do próprio estado do recurso, seja através de uma coluna atualizada ou da junção de todas colunas.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;&lt;em&gt;Versionless Optmistic Locking&lt;/em&gt;&lt;/strong&gt; com Hibernate
&lt;/h3&gt;

&lt;p&gt;Hibernate oferece suporte ao controle de concorrência com bloqueio otimista sem versão, para habilitar o mesmo devemos utilizar anotação &lt;code&gt;@OptimisticLocking&lt;/code&gt; ao nível de entidade, esta anotação recebe como argumento a estratégia de bloqueio otimista descrita por um &lt;code&gt;OptimisticLockType&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;O Hibernate oferece suporte as seguintes estratégias de bloqueio otimista.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;OptimisticLockType.NONE&lt;/code&gt;: indica que o mecanismo de bloqueio otimista está desabilitado;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OptimisticLockType.VERSION&lt;/code&gt;: indica que o mecanismo de bloqueio otimista utilizará o atributo anotado com &lt;code&gt;@Version&lt;/code&gt; como versão da entidade;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OptimisticLockType.DIRTY&lt;/code&gt;: indica que o mecanismo de bloqueio otimista utilizará apenas os atributos atualizados pela transação como versão da entidade;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OptimisticLockType.ALL&lt;/code&gt;: indica que o mecanismo de bloqueio otimista utilizar todos os atributos da entidade como versão;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Como o mecanismo de controle de concorrência precisa inserir condições adicionais em sua &lt;code&gt;Query&lt;/code&gt; de atualização, é necessário que a entidade seja anotado com &lt;code&gt;@DynamicUpdate&lt;/code&gt; ao nível de entidade.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@OptimisticLocking&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OptimisticLockType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@DynamicUpdate&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Mesa&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;//Demais campos, construtores e metodos omitidos  &lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Controle de Concorrência com &lt;code&gt;OptimisticLockType.ALL&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Dado que a estratégia de bloqueio otimista &lt;code&gt;OptimisticLockType.ALL&lt;/code&gt; esteja habilitada no momento de uma atualização o Hibernate irá verificar se o estado de cada atributo da entidade corresponde em memória ao estado presente no BD.&lt;/p&gt;

&lt;p&gt;Para melhor entendimento observe o código abaixo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;atualizarQuantidadeDeLugares&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;qtdLugares&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
   &lt;span class="nc"&gt;Mesa&lt;/span&gt; &lt;span class="n"&gt;mesa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entityManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Mesa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
   &lt;span class="n"&gt;mesa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setQuatidadeDeLugares&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qtdLugares&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hibernate gera o seguinte &lt;strong&gt;&lt;em&gt;SQL&lt;/em&gt;&lt;/strong&gt;:&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;mesa&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;mesaId&lt;/span&gt;

   &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;mesa&lt;/span&gt;
      &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;quantidadeLugares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;qtdLugares&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;medaId&lt;/span&gt; 
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;quantidadeLugares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;disponivelParaReserva&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No SQL acima é possível visualizar que todos os campos se uniram e convergiram em um único atributo de versão global. Esta estratégia é utilizada quando é impossível inserir um atributo para versionamento da entidade. Em contrapartida, as atualizações com atributos não sobrepostos conflitam. &lt;/p&gt;

&lt;h4&gt;
  
  
  Controle de Concorrência com &lt;code&gt;OptimisticLockType.DIRTY&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;A estratégia de bloqueio otimista &lt;code&gt;OptimisticLockType.DIRTY&lt;/code&gt; favorece que apenas os campos "sujos" na atualização sejam  utilizados como versão da entidade, ou seja, agora atualizações com atributos não sobrepostos não conflitam mais.&lt;/p&gt;

&lt;p&gt;Observe o comportamento do seguinte método de atualização.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;atualizarQuantidadeDeLugares&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;qtdLugares&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
   &lt;span class="nc"&gt;Mesa&lt;/span&gt; &lt;span class="n"&gt;mesa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entityManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Mesa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
   &lt;span class="n"&gt;mesa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setQuatidadeDeLugares&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qtdLugares&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hibernate gera o seguinte &lt;strong&gt;&lt;em&gt;SQL&lt;/em&gt;&lt;/strong&gt;:&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;mesa&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;mesaId&lt;/span&gt;

   &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;mesa&lt;/span&gt;
      &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;quantidadeLugares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;qtdLugares&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;medaId&lt;/span&gt; 
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;quantidadeLugares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O &lt;strong&gt;&lt;em&gt;SQL&lt;/em&gt;&lt;/strong&gt; apresentado acima demostra que apenas o atributo &lt;code&gt;quantidadeLugares&lt;/code&gt; foi utilizado na condição do &lt;strong&gt;&lt;code&gt;WHERE&lt;/code&gt;&lt;/strong&gt;, ou seja, quando a estratégia de bloqueio otimista &lt;code&gt;DIRTY&lt;/code&gt; é utilizada apenas os campos atualizados são utilizados como versão. O sistema permite que atualizações a atributos distintos não conflitem evitando &lt;strong&gt;&lt;code&gt;OptimisticLockException&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9honl8lb78d0h0ktf58n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9honl8lb78d0h0ktf58n.png" alt="Atualizações não sobrepostas" width="721" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O diagrama acima pode ser resumido em:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A primeira transação (&lt;strong&gt;&lt;em&gt;Tx1&lt;/em&gt;&lt;/strong&gt;) realiza a consulta de leitura da entidade Mesa com id igual a 1. &lt;/li&gt;
&lt;li&gt;A segunda transação (&lt;strong&gt;&lt;em&gt;Tx2&lt;/em&gt;&lt;/strong&gt;) simultaneamente também realiza a consulta de leitura da entidade Mesa com id igual a 1&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Tx1&lt;/em&gt;&lt;/strong&gt; altera a quantidade de lugares na mesa.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Tx2&lt;/em&gt;&lt;/strong&gt; altera o status de disponibilidade da mesa&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Construindo uma API REST para reserva de mesas com &lt;strong&gt;&lt;em&gt;Versionless Optimistic Locking&lt;/em&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Ao nível de &lt;strong&gt;&lt;em&gt;controller&lt;/em&gt;&lt;/strong&gt; não existem mudanças a &lt;a href="https://dev.to/jordihofc/criando-sistemas-de-reservas-consistentes-com-optimistic-locking-spring-boot-e-jpahibernate-2h8b"&gt;implementação do artigo anterior&lt;/a&gt; dado que nossas mudanças afetam apenas as entidades.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/mesas/{id}/reservas"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;reservar&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@PathVariable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;ReservaMesaRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;UriComponentsBuilder&lt;/span&gt; &lt;span class="n"&gt;uriBuilder&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Mesa&lt;/span&gt; &lt;span class="n"&gt;mesa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mesaRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ResponseStatusException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;NOT_FOUND&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Mesa não cadastrada"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="nc"&gt;Usuario&lt;/span&gt; &lt;span class="n"&gt;usuario&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usuarioRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUsuarioId&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ResponseStatusException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UNPROCESSABLE_ENTITY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Usuario não cadastrado"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="n"&gt;dataDaReserva&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDataReserva&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reservaRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;existsMesaByIdAndReservadoParaIs&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataDaReserva&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ResponseStatusException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UNPROCESSABLE_ENTITY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Horario indisponivel para reserva"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;Reserva&lt;/span&gt; &lt;span class="n"&gt;reserva&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mesa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reservar&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usuario&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataDaReserva&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;reservaRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reserva&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;


    &lt;span class="no"&gt;URI&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uriBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/mesas/{id}/reservas/{reservaId}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;buildAndExpand&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mesa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;reserva&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toUri&lt;/span&gt;&lt;span class="o"&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="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Independente da abordagem de bloqueio otimista sem versão é indispensável a construção de um Exception Handler para &lt;code&gt;ObjectOptimisticLockingFailureException&lt;/code&gt;, quando atualizações conflitarem é essencial que a resposta contenham um &lt;code&gt;Http Status Conflict&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestControllerAdvice&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeafultExceptionHandler&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;    
 &lt;span class="nd"&gt;@ExceptionHandler&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ObjectOptimisticLockingFailureException&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;optmisticLock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ObjectOptimisticLockingFailureException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Aconteceu um conflito em sua atualização, tente novamente."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;409&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                        &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mensagem"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Construir mecanismo para controle de concorrência é uma tarefa complexa dado que cada sistema possui suas restrições. O mecanismo de &lt;strong&gt;&lt;em&gt;Versionless Optimistic Locking&lt;/em&gt;&lt;/strong&gt; do Hibernate oferece flexibilidade para implementação da estratégia de detecção de conflitos para diversos casos de uso.&lt;/p&gt;

&lt;p&gt;Caso o conflito de atualizações estejam causando integridade aos dados em um sistema legado ou quando alterar o &lt;em&gt;schema&lt;/em&gt; seja custoso o &lt;code&gt;OptimisticLockType.ALL&lt;/code&gt; se torna uma ótima solução dado que seu comportamento é semelhante ao &lt;strong&gt;&lt;em&gt;Optimistic Locking&lt;/em&gt;&lt;/strong&gt; tradicional.&lt;/p&gt;

&lt;p&gt;Porém, se existe a necessidade de que atualizações a atributos não sobrepostos não conflitem a estratégia de &lt;code&gt;OptimisticLockType.DIRTY&lt;/code&gt; se torna uma solução já que apenas os atributos "sujos" são utilizados como versão da entidade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://vladmihalcea.com/how-to-prevent-optimisticlockexception-using-hibernate-versionless-optimistic-locking/" rel="noopener noreferrer"&gt;How to prevent OptimisticLockException with Hibernate versionless optimistic locking&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://vladmihalcea.com/books/high-performance-java-persistence/?utm_source=blog&amp;amp;utm_medium=banner&amp;amp;utm_campaign=banner" rel="noopener noreferrer"&gt;Chapter: Concurrency Control - High-Performance Java Persistence&lt;br&gt;
Book&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>jpa</category>
      <category>concurrency</category>
      <category>spring</category>
    </item>
    <item>
      <title>Criando Sistemas de Reservas consistentes com Optimistic Locking, Spring Boot e JPA/Hibernate</title>
      <dc:creator>Jordi Henrique Silva</dc:creator>
      <pubDate>Thu, 11 Aug 2022 17:42:00 +0000</pubDate>
      <link>https://dev.to/jordihofc/criando-sistemas-de-reservas-consistentes-com-optimistic-locking-spring-boot-e-jpahibernate-2h8b</link>
      <guid>https://dev.to/jordihofc/criando-sistemas-de-reservas-consistentes-com-optimistic-locking-spring-boot-e-jpahibernate-2h8b</guid>
      <description>&lt;p&gt;No &lt;a href="https://dev.to/jordihofc/criando-sistemas-de-reservas-consistentes-com-pessimistic-locking-spring-boot-e-jpahibernate-1320"&gt;artigo anterior&lt;/a&gt; foi utilizado a estratégia de &lt;strong&gt;&lt;em&gt;Pessimistic Locking&lt;/em&gt;&lt;/strong&gt; para construção de uma API REST para reserva de mesas de bar. Como visto no artigo existem situações onde empregar bloqueios físicos pode ser custoso visando que se diversas linhas estejam bloqueadas haverá uma diminuição de produtividade nas transações de banco de dados.&lt;/p&gt;

&lt;p&gt;Caso você se encontre em uma destas situações, existem outras estratégias para lidar com Race Conditions, como é o caso do  &lt;strong&gt;&lt;em&gt;Optimistic Locking&lt;/em&gt;&lt;/strong&gt; ou Bloqueio Otimista em português.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Optimistic Locking&lt;/em&gt;&lt;/strong&gt; é uma estratégia de detecção de conflitos,  ao contrário do &lt;code&gt;Pessimistic Locking&lt;/code&gt; que seu foco é  evitar conflitos, o bloqueio otimista deixa os conflitos acontecerem e ao identificá-los reverte as transações detentoras dos mesmos. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conhecendo o &lt;strong&gt;&lt;em&gt;Optimistic Locking&lt;/em&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A ideia principal do bloqueio otimista é reduzir o período de tempo em que as transações detêm bloqueios físicos afim prover melhorias no desempenho da aplicação. Antes ao utilizar o bloqueio pessimista uma transação obtia um bloqueio garantindo  exclusividade para alterar o recurso. Já no bloqueio otimista diversas transações podem tentar alterar, porém, somente uma ira conseguir e as demais serão abortadas.&lt;/p&gt;

&lt;p&gt;Para identificar os conflitos a estrategia utiliza o estado do recurso como um critério de versionamento, permitindo que apenas as transações que contenham o recurso com versão equivalente a presente no banco de dados sejam confirmadas (COMMIT).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;&lt;em&gt;Optimistic Locking&lt;/em&gt;&lt;/strong&gt; com JPA/Hibernate
&lt;/h3&gt;

&lt;p&gt;JPA/Hibernate oferece suporte para estratégia de &lt;strong&gt;&lt;em&gt;Optimistic Locking&lt;/em&gt;&lt;/strong&gt;, para habilita-lo devemos inserir um campo na &lt;strong&gt;entidade&lt;/strong&gt; para representar a versão. Este campo deverá ser anotado com &lt;code&gt;@Version&lt;/code&gt; para ser gerenciado pela JPA/Hibernate. O atributo de versão deve ser mapeado para &lt;code&gt;java.sql.Timestamp&lt;/code&gt; ou qualquer tipo que represente números inteiros, por exemplo, &lt;code&gt;Long, long, int, Integer, short e Short&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Mesa&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@Version&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;//Demais campos, construtores e metodos omitidos  &lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;OBS: É recomendado que o versionamento de entidades seja através de atributos inteiros dado que a versão devera sempre crescer monotonicamente. O perigo de utilizar um atributos de data e hora é que por causa de uma sincronização do  &lt;strong&gt;&lt;em&gt;Network Time Protocol&lt;/em&gt;&lt;/strong&gt; as datas podem retroceder.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Observe o comportamento ao persistir uma entidade.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt; 
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;cadastrar&lt;/span&gt;&lt;span class="o"&gt;(){&lt;/span&gt;
     &lt;span class="nc"&gt;Mesa&lt;/span&gt; &lt;span class="n"&gt;mesa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Mesa&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
     &lt;span class="n"&gt;entityManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;persist&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mesa&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O SQL gerado pela JPA/Hibernate.&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;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;mesa&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;quantidadeDeLugares&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;disponivelParaReserva&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&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;4&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;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dado que o mecanismo de bloqueio otimista está habilitado para uma entidade para cada operação de atualização é disparado uma checagem para verificar se a versão da entidade em memória corresponde a versão da entidade no banco de dados, caso corresponda atualização é confirmada e a versão da entidade é incrementada. Caso contrario a transação sera revertida.&lt;/p&gt;

&lt;p&gt;Observe abaixo um exemplo de atualização bem sucedida.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt; 
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;atualizarQuantidadeLugares&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;novaQuantidadeLugares&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
     &lt;span class="nc"&gt;Mesa&lt;/span&gt; &lt;span class="n"&gt;mesa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entityManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Mesa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
     &lt;span class="n"&gt;mesa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;atualizarQuantidadeDeLugares&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;novaQuantidadeLugares&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O Hibernate gera a seguinte saída:&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;mesa&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
 &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;mesa&lt;/span&gt;
   &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;quantidadeDeLugares&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;qtdLugeres&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
 &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caso o retorno da operação &lt;code&gt;UPDATE&lt;/code&gt; seja  a contagem de 1 linha atualizada a operação &lt;strong&gt;&lt;em&gt;SQL&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;UPDATE&lt;/code&gt; sera commitada. Caso seja 0 sera lançada uma Exceção do tipo &lt;strong&gt;&lt;code&gt;OptimisticLockException&lt;/code&gt;&lt;/strong&gt; e automaticamente a transação sera revertida.&lt;/p&gt;

&lt;p&gt;Para entender melhor, imagine que duas transações desejem atualizar informações da mesma entidade de maneira simultânea, ambas as transações iram obter a mesma versão de entidade. A primeira transação (Tx1) a terminar, irá persistir suas alterações, e incrementar a versão da entidade. A segunda transação (Tx2) não tem ciência que a versão da entidade foi incrementada, e dispara sua atualização, porém, a versão de presente em memória precede a versão presente no banco, portanto a segunda transação é abortada.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fmzlipqpoof8jyu3el18i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fmzlipqpoof8jyu3el18i.png" alt="Evitando uma perda de atualização com Optimistic Locking"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Construindo uma API REST para reserva de mesas com  &lt;strong&gt;&lt;em&gt;Optimistic Locking&lt;/em&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Para se beneficiar do mecanismo de &lt;strong&gt;&lt;em&gt;Optimistic Locking&lt;/em&gt;&lt;/strong&gt; em nossa API de reserva, devemos implementar algumas mudanças em comparação a &lt;a href="https://github.com/JordiHOFC/reserva-de-mesa-bar/blob/master/src/main/java/br/com/github/jordihofc/reserva/mesa/ReservarMesaController.java" rel="noopener noreferrer"&gt;implementação anterior&lt;/a&gt;. Antes onde era adquirido um bloqueio pessimista através da consulta &lt;code&gt;findByIdWithPessimisticLock( )&lt;/code&gt; agora não é mais necessário dado que uma checagem de versão sera realizada pela JPA/Hibernate. Então podemos substituir a consulta pelo método default &lt;code&gt;findById( )&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Um possível implementação para lógica descrita acima é:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/mesas/{id}/reservas"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;reservar&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@PathVariable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;ReservaMesaRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;UriComponentsBuilder&lt;/span&gt; &lt;span class="n"&gt;uriBuilder&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Mesa&lt;/span&gt; &lt;span class="n"&gt;mesa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mesaRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ResponseStatusException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;NOT_FOUND&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Mesa não cadastrada"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="nc"&gt;Usuario&lt;/span&gt; &lt;span class="n"&gt;usuario&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usuarioRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUsuarioId&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ResponseStatusException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UNPROCESSABLE_ENTITY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Usuario não cadastrado"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="n"&gt;dataDaReserva&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDataReserva&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reservaRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;existsMesaByIdAndReservadoParaIs&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataDaReserva&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ResponseStatusException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UNPROCESSABLE_ENTITY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Horario indisponivel para reserva"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;Reserva&lt;/span&gt; &lt;span class="n"&gt;reserva&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mesa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reservar&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usuario&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataDaReserva&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;reservaRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reserva&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;


    &lt;span class="no"&gt;URI&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uriBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/mesas/{id}/reservas/{reservaId}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;buildAndExpand&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mesa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;reserva&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toUri&lt;/span&gt;&lt;span class="o"&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="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sabemos que se uma operação de atualização &lt;code&gt;UPDATE&lt;/code&gt; falhar no mecanismo de checagem de versão uma &lt;code&gt;ObjectOptimisticLockingFailureException&lt;/code&gt;será lançada, e se o sistema não estive preparado um erro inesperado sera retornado ao cliente, então é importante que a exceção seja tratada e uma mensagem amigável e coerente ao caso seja dada ao cliente.&lt;/p&gt;

&lt;p&gt;Segundo a &lt;a href="https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.8" rel="noopener noreferrer"&gt;RFC 7231&lt;/a&gt; quando uma solicitação resulta em erro dado ao estado atual do recurso no servidor de destino é o indicado que a resposta a solicitação contenha o &lt;code&gt;HTTP Status 409 Conflict&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Exemplo de Exception Handler com Spring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestControllerAdvice&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeafultExceptionHandler&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;    

    &lt;span class="nd"&gt;@ExceptionHandler&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ObjectOptimisticLockingFailureException&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;optmisticLock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ObjectOptimisticLockingFailureException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Aconteceu um conflito em sua atualização, tente novamente."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;409&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                        &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mensagem"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;O mecanismo de bloqueio otimista da JPA/Hibernate trabalha com a estratégia de detecção de conflitos. Isto que significa que o tempo em que o registro fica bloqueado é reduzido. Antes um bloqueio era adquirido no momento da leitura e era liberado ao fim da transação. Agora diversas transações simultâneas podem tentar atualizar o registro, mas apenas uma conseguirá.&lt;/p&gt;

&lt;p&gt;Estrategia de bloqueio otimista pode trazer uma melhoria em performance para seu sistema como um todo na maioria dos casos, porém, não se engane, caso o índice de conflitos seja alto o  &lt;strong&gt;&lt;em&gt;Optimistic Locking&lt;/em&gt;&lt;/strong&gt; pode ser mais custoso que um bloqueio pessimista, dado que será necessário um alto esforço do banco de dados para reverter transações. Outro fator que corrobora uma possível perda de performance é que bloqueios otimistas bem implementados requerem retries o que pode aumentar o tempo de resposta da sua aplicação.&lt;/p&gt;

&lt;p&gt;Enfim, se seu sistema possui um índice de concorrência baixo ou moderado o uso de &lt;strong&gt;&lt;em&gt;Optimistic Locking&lt;/em&gt;&lt;/strong&gt; é uma ótima opção, pois oferece ganhos de consistência sem sacrificar a performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vladmihalcea.com/optimistic-locking-version-property-jpa-hibernate/" rel="noopener noreferrer"&gt;Optimistic Locking - Vlad Mihalcea&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.baeldung.com/jpa-optimistic-locking" rel="noopener noreferrer"&gt;Optimistic Locking - Baeldung&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://eugene-eeo.github.io/blog/optlock.html" rel="noopener noreferrer"&gt;Optimistic Locking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vladmihalcea.com/optimistic-vs-pessimistic-locking/" rel="noopener noreferrer"&gt;Pessimistic vs Optimistic Locking - Vlad Mihalcea&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.baeldung.com/exception-handling-for-rest-with-spring" rel="noopener noreferrer"&gt;Exception Handler&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>spring</category>
      <category>jpa</category>
      <category>concurrency</category>
    </item>
    <item>
      <title>Criando Sistemas de Reservas consistentes com Pessimistic Locking, Spring Boot e JPA/Hibernate</title>
      <dc:creator>Jordi Henrique Silva</dc:creator>
      <pubDate>Mon, 01 Aug 2022 15:31:00 +0000</pubDate>
      <link>https://dev.to/jordihofc/criando-sistemas-de-reservas-consistentes-com-pessimistic-locking-spring-boot-e-jpahibernate-1320</link>
      <guid>https://dev.to/jordihofc/criando-sistemas-de-reservas-consistentes-com-pessimistic-locking-spring-boot-e-jpahibernate-1320</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;OBS: Objetivo deste artigo  não é ensinar a construir um sistema de reservas para produção, mas sim introduzir conceitos de controle de concorrência.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Uma cultura bastante popular em diversas cidades no mundo é se encontrar para bater papo e tomar alguns drinks em uma mesa de bar. Em alguns Bares a procura por mesas é tão grande que as pessoas precisam se adiantar para não ficar de fora e isto causa descontentamento aos clientes.&lt;/p&gt;

&lt;p&gt;Uma possível solução para os donos de bares é prover um mecanismo para que todos os clientes possam reservar suas mesas antecipadamente, porém, dado a alta concorrência pelas mesas, já se pode deduzir que construir um sistema de reserva não é uma tarefa trivial, pois caso o desenvolvedor não se atente aos requisitos não funcionais, mais de um cliente poderá reservar a mesma mesa, ocasionado o que conhecemos como &lt;em&gt;Anomalia da Atualização Perdida&lt;/em&gt; (&lt;a href="https://vladmihalcea.com/a-beginners-guide-to-database-locking-and-the-lost-update-phenomena/" rel="noopener noreferrer"&gt;Lost Update Anomaly&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Anomalia de Atualização Perdida&lt;/em&gt; é causada por &lt;strong&gt;&lt;em&gt;Race Conditions&lt;/em&gt;&lt;/strong&gt; ou Condições de Corrida em português. &lt;strong&gt;&lt;em&gt;Race Conditions&lt;/em&gt;&lt;/strong&gt; é um problema classico da Ciência da Computação que indica que se dois ou mais processos, tentam acessar um recurso simultaneamente, após varios acessos não podemos definir o estado do recurso, o que causa um problema de inconsistência.&lt;/p&gt;

&lt;p&gt;Para identificar &lt;strong&gt;&lt;em&gt;Race Conditions&lt;/em&gt;&lt;/strong&gt; devemos nos atentar se o algoritmo da sua solução segue o seguinte padrão: &lt;strong&gt;ler - processar- escrever&lt;/strong&gt;.  Isto porque durante a janela de tempo entre ler e escrever, outro processo pode atualizar o estado do recurso, causando inconsistência. &lt;/p&gt;

&lt;p&gt;Para entender melhor vamos realizar o seguinte exercício de imaginação. Imagine que dado o modelo de domínio abaixo, dois usuários tentem reservar a mesma mesa paralelamente.&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%2F74d14tzx72pg3d82jdoa.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%2F74d14tzx72pg3d82jdoa.png" alt="Image description" width="350" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Usuario 1&lt;/code&gt; realiza a leitura do registro de uma mesa, e realizaria o processamento da lógica de reserva. &lt;code&gt;Usuario 2&lt;/code&gt; também executa a leitura para o mesmo registro de mesa, e obtêm que a mesa ainda está disponível para reserva. &lt;code&gt;Usuario 1&lt;/code&gt;   conclui a lógica de reserva, e o &lt;code&gt;Usuario 2&lt;/code&gt; não é notificado que o status de disponibilidade da mesa mudou, portanto, a  instância em memória da mesa que o  &lt;code&gt;Usuario 2&lt;/code&gt; possui ainda indica que a mesa está disponível para reserva e dado a isso ele também consegue reservar a mesa.&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%2Fy3702mf8asnhtys4xwkw.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%2Fy3702mf8asnhtys4xwkw.png" alt="fluxo de reserva com race conditions" width="731" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O diagrama acima pode ser resumido da seguinte forma:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Usuario 1&lt;/code&gt; faz a consulta pela mesa 1, e recebe como resposta que a mesa esta disponivel.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Usuario 2&lt;/code&gt; também consulta a mesa 1, e recebe que a mesa esta disponivel.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Usuario 1&lt;/code&gt; procede com fluxo de reserva, e consegue reservar a mesa.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Usuario 2&lt;/code&gt; contém uma versão em memória que indica que a mesa está disponível, por tanto consegue progredir na reserva, agora tanto &lt;code&gt;Usuario 1&lt;/code&gt; quanto &lt;code&gt;Usuario 2&lt;/code&gt; tem uma reserva para mesa 1.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Existem diversas soluções para o problema de &lt;strong&gt;&lt;em&gt;Race Conditions&lt;/em&gt;&lt;/strong&gt;, uma delas é a aplicação do algoritmo de &lt;a href="https://en.wikipedia.org/wiki/Critical_section" rel="noopener noreferrer"&gt;sessão critica&lt;/a&gt;, que pode ser resumido em, um processo obtem um bloqueio (lock)  modifica o recurso, e libera o bloqueio para que outro processo consiga executar suas alterações.&lt;/p&gt;

&lt;p&gt;No mundo Java uma solução popular para &lt;strong&gt;&lt;em&gt;Race Conditions&lt;/em&gt;&lt;/strong&gt; é a fazer com que  a lógica de reserva fique encapsulada em um método com a palavra-chave &lt;a href="https://www.baeldung.com/java-synchronized" rel="noopener noreferrer"&gt;&lt;code&gt;synchronized&lt;/code&gt;&lt;/a&gt;. O que proveria a sincronização de acesso ao recurso ao nível de Thread da JVM. O problema desta abordagem é que por requisitos de escalabilidade ou disponibilidade podem existir mais de uma instância da aplicação, indicando a necessidade de um mecanismo de lock distribuído.&lt;/p&gt;

&lt;p&gt;Ferramentas como &lt;a href="https://zookeeper.apache.org/" rel="noopener noreferrer"&gt;Zookeeper&lt;/a&gt;, &lt;a href="https://www.mongodb.com/" rel="noopener noreferrer"&gt;MongoDb&lt;/a&gt; e &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt; provem mecanismos para obter bloqueios distribuídos, porém, se em sua infraestrutura já exista um banco de dados relacional, e caso todas as instâncias da sua aplicação acessem este banco é possível se favorecer dos mecanismos de controle de concorrência do banco.&lt;/p&gt;

&lt;p&gt;Os Bancos de dados relacionais oferecem suporte a bloqueios exclusivos, adquiridos para evitar conflitos entre operações de escrita. Dado que um bloqueio é adquirido por uma transação, as demais transações que tentem acessar o mesmo recurso serão obrigadas a aguardar que a transação detentora do bloqueio seja confirmada (COMMIT) ou revertida (ROLLBACK).  &lt;/p&gt;

&lt;p&gt;Ao inserir um bloqueio exclusivo em uma consulta é utilizado a estratégia de &lt;strong&gt;&lt;code&gt;Pessimistic Locking&lt;/code&gt;&lt;/strong&gt; ou Bloqueio Pessimista em português. &lt;/p&gt;

&lt;h3&gt;
  
  
  Obtendo &lt;strong&gt;&lt;code&gt;Pessimistic Locking&lt;/code&gt;&lt;/strong&gt; em seu banco de dados relacional
&lt;/h3&gt;

&lt;p&gt;Os bloqueios pessimistas podem ser adquiridos de maneira implícita ou explicita. Implícita quando alguma instrução  &lt;em&gt;SQL&lt;/em&gt;  &lt;code&gt;UPDATE&lt;/code&gt; ou &lt;code&gt;DELETE&lt;/code&gt; são disparadas, para que o estado do registro não seja modificado um bloqueio é adquirido. Explicita é quando de maneira declarativa o  usuário do &lt;em&gt;SGBD&lt;/em&gt; solicita que a instrução  &lt;em&gt;SQL&lt;/em&gt; &lt;code&gt;SELECT&lt;/code&gt; se comporte como uma operação &lt;code&gt;UPDATE&lt;/code&gt;, desta forma outras transações são impedidas de escrever, deletar ou adquirir um bloqueio para o registro.&lt;/p&gt;

&lt;p&gt;Para solicitar um bloqueio exclusivo em um PostgreSQL basta escrever uma operação &lt;em&gt;SQL&lt;/em&gt; &lt;code&gt;SELECT&lt;/code&gt; com as cláusulas &lt;code&gt;FOR UDPATE&lt;/code&gt;.&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;mesa&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
   &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&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="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Existem alguns bancos como postgreSQL que oferecem suporte a um bloqueio compartilhado, este bloqueio permite que apenas a transação dentetora do bloqueio solicite um bloqueio exclusivo.&lt;br&gt;
Para solicitar um bloqueio compartilhado em um PostgreSQL basta escrever uma operação &lt;em&gt;SQL&lt;/em&gt; &lt;code&gt;SELECT&lt;/code&gt; com as cláusulas &lt;code&gt;FOR SHARE&lt;/code&gt;.&lt;/p&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;mesa&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&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="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;SHARE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Construindo o mecanismo de bloqueio exclusivo com Spring Data e JPA/Hibernate
&lt;/h3&gt;

&lt;p&gt;O modulo Spring Data JPA oferece suporte a especificação JPA e utiliza Hibernate como implementação padrão, isto significa que nós podemos nos aproveitar dos mecanismos de controle de concorrência da JPA/Hibernate.&lt;/p&gt;

&lt;p&gt;Os tipos de bloqueios suportados pela JPA/Hibernate estão descritos no enum &lt;code&gt;LockModeType&lt;/code&gt;. Abaixo está descrito quais são as propriedades responsáveis por solicitar bloqueio exclusivo ou compartilhado:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;LockModeType.PESSIMISTIC_READ&lt;/code&gt;: esta propriedade indica que um bloqueio compartilhado deve ser adquirido.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LockModeType.PESSIMISTIC_WRITE&lt;/code&gt;: esta propriedade indica que um bloqueio exclusivo deve ser adquirido.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;EntityManager&lt;/code&gt; oferece a possibilidade de realizar um &lt;em&gt;bloqueio&lt;/em&gt;  através da consulta por id. O método &lt;code&gt;find()&lt;/code&gt; oferece suporte para através do argumento opcional lockMode que recebe um tipo de lock através do &lt;code&gt;LockModeType&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Mesa&lt;/span&gt; &lt;span class="n"&gt;mesa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entityManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Mesa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;LockModeType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PESSIMISTIC_WRITE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observe o sql gerado.&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="n"&gt;m&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Mesa&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Outras formas de solicitar um lock através da &lt;code&gt;EntityManager&lt;/code&gt; são pelo método &lt;code&gt;lock()&lt;/code&gt;, e outra alternativa é ao criar uma  &lt;code&gt;Query&lt;/code&gt; criada através do método &lt;code&gt;createQuery()&lt;/code&gt; informar a estratégia através do método &lt;code&gt;setLockMode()&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Caso você utilize os repositories (Repository) da Spring Data um lock pode ser solicitado por uma consulta através da anotação &lt;strong&gt;&lt;code&gt;@Lock(lockMode = LockModeType.PESSIMISTIC_WRITE)&lt;/code&gt;&lt;/strong&gt;. No exemplo a baixo é implementado uma consulta por id obtendo um bloqueio exclusivo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;MesaRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Mesa&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Lock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LockModeType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PESSIMISTIC_WRITE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT m FROM Mesa m WHERE m.id = :id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Mesa&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findByIdWithPessimisticLock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quando optamos por construir nosso controle de concorrência utilizando um bloqueio exclusivo estamos implementando a estratégia de &lt;strong&gt;&lt;code&gt;Pessimistic Locking&lt;/code&gt;&lt;/strong&gt; ou Bloqueio Pessimista em português.  Isso se deve a estratégia de bloqueio que acredita que a melhor forma de lidar com conflitos é garantindo que eles não existam.&lt;/p&gt;

&lt;h3&gt;
  
  
  Construindo o Serviço para Reserva
&lt;/h3&gt;

&lt;p&gt;Durante a construção do serviço de reserva é o momento onde iremos implementar uma sessão critica através da estratégia de  &lt;code&gt;Pessimistic Locking&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;A primeiro momento é necessário que esteja bem claro que cada transação é um processo independente, e que o problema de &lt;strong&gt;&lt;em&gt;Race Condition&lt;/em&gt;&lt;/strong&gt; acontece entre a leitura do registro e a persistência da operação, ou seja, durante o período que uma transação esta processando suas  operações outra transação pode alterar o estado do registro.&lt;br&gt;
Então o que precisa ser feito é que dado um contexto transacional é necessário que o registro da Mesa a ser reservada obtenha um bloqueio exclusivo.&lt;/p&gt;

&lt;p&gt;Dado isso uma possível implementação para esta logica é:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReservarMesaController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;MesaRepository&lt;/span&gt; &lt;span class="n"&gt;mesaRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;UsuarioRepository&lt;/span&gt; &lt;span class="n"&gt;usuarioRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ReservaRepository&lt;/span&gt; &lt;span class="n"&gt;reservaRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ReservarMesaController&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MesaRepository&lt;/span&gt; &lt;span class="n"&gt;mesaRepository&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;UsuarioRepository&lt;/span&gt; &lt;span class="n"&gt;usuarioRepository&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ReservaRepository&lt;/span&gt; &lt;span class="n"&gt;reservaRepository&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mesaRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mesaRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;usuarioRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usuarioRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reservaRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reservaRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/mesas/{id}/reservas"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Transactional&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;reservar&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nd"&gt;@PathVariable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;ReservaMesaRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;UriComponentsBuilder&lt;/span&gt; &lt;span class="n"&gt;uriBuilder&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Mesa&lt;/span&gt; &lt;span class="n"&gt;mesa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mesaRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findByIdWithPessimisticLock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ResponseStatusException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;NOT_FOUND&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Mesa não cadastrada"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="nc"&gt;Usuario&lt;/span&gt; &lt;span class="n"&gt;usuario&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usuarioRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUsuarioId&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ResponseStatusException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UNPROCESSABLE_ENTITY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Usuario não cadastrado"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="n"&gt;dataDaReserva&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDataReserva&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reservaRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;existsMesaByIdAndReservadoParaIs&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mesaId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataDaReserva&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ResponseStatusException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UNPROCESSABLE_ENTITY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Horario indisponivel para reserva"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nc"&gt;Reserva&lt;/span&gt; &lt;span class="n"&gt;reserva&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mesa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reservar&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usuario&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataDaReserva&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;reservaRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reserva&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="no"&gt;URI&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uriBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/mesas/{id}/reservas/{reservaId}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;buildAndExpand&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mesa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;reserva&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toUri&lt;/span&gt;&lt;span class="o"&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="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Os demais detalhes do código acima estão disponíveis no seguinte &lt;a href="https://github.com/JordiHOFC/reserva-de-mesa-bar" rel="noopener noreferrer"&gt;repositório&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A estratégia de &lt;strong&gt;&lt;em&gt;Pessimistic Locking&lt;/em&gt;&lt;/strong&gt; também pode trazer algumas desvantagens, caso sua tabela tenha um alto volume de acesso para operações de atualização e/ou remoção, existe a chance de que varias linhas fiquem bloqueadas, e o impacto disto é que diversas transações ficaram aguardando para serem executadas, o que resultaria em gargalos no banco que se propagariam por toda a aplicação.&lt;/p&gt;

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

&lt;p&gt;Sistemas que envolvem reservas de recursos são fortes candidatos a sofrem com condições de corrida, isto porquê o espaço de tempo entre a leitura de um recurso e a escrita da solução é suficiente para que outro processo altere o estado do recurso causando inconsistência.&lt;/p&gt;

&lt;p&gt;Problema de &lt;strong&gt;&lt;em&gt;Race Conditions&lt;/em&gt;&lt;/strong&gt; podem ser solucionados através da sincronização de acesso ao recurso. Para sincronizar o acesso a um determinado recurso podemos implementar uma sessão critica, que defina que apenas um processo detenha permissão suficiente para alterar o estado do recurso por vez.&lt;/p&gt;

&lt;p&gt;Os bancos de dados relacionais oferecem mecanismos para controle de concorrência, que podem ser utilizados através das APIs da  JPA/Hibernate. Sobre tudo implementar um mecanismo de controle de concorrência utilizando a estratégia de &lt;strong&gt;&lt;em&gt;Pessimistic Lock&lt;/em&gt;&lt;/strong&gt; é uma tarefa que exige atenção e cuidado, pois o uso de bloqueios exclusivos de maneira imatura podem causar uma baixa produtividade de transações o que afetaria a performance da aplicação.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://blog.avenuecode.com/how-to-avoid-race-conditions-in-your-microservice-application" rel="noopener noreferrer"&gt;Race conditions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vladmihalcea.com/hibernate-locking-patterns-how-do-pessimistic_read-and-pessimistic_write-work/" rel="noopener noreferrer"&gt;Pessimistic Locking com Vlad Mihalcea&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.baeldung.com/jpa-pessimistic-locking" rel="noopener noreferrer"&gt;Pessimistic Locking com Baeldung&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/9.0/sql-select.html#SQL-FOR-UPDATE-SHARE" rel="noopener noreferrer"&gt;Select FOR Update e For Share em PostgreSQL Doc&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>java</category>
      <category>spring</category>
      <category>jpa</category>
      <category>concurrency</category>
    </item>
    <item>
      <title>Criando Controllers seguros do Mass Assignment Cheat Sheet</title>
      <dc:creator>Jordi Henrique Silva</dc:creator>
      <pubDate>Mon, 25 Jul 2022 17:30:00 +0000</pubDate>
      <link>https://dev.to/jordihofc/criando-controllers-seguros-do-mass-assignment-cheat-sheet-5gmf</link>
      <guid>https://dev.to/jordihofc/criando-controllers-seguros-do-mass-assignment-cheat-sheet-5gmf</guid>
      <description>&lt;p&gt;Se você esta iniciando sua carreira como Desenvolvedor(a) Java e utiliza Spring MVC para construir suas APIs REST já deve ter se  perguntado o por quê não é utilizado a própria entidade para receber dados no &lt;strong&gt;&lt;em&gt;Controller&lt;/em&gt;&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Para responder essa pergunta preciso te contar uma história bizarra que aconteceu com o GitHub.  Em 2012 um hacker conseguiu explorar uma falha em um formulário de entrada, que permitiu ele se associar a organização do &lt;a href="https://github.com/rails" rel="noopener noreferrer"&gt;Ruby on Rails&lt;/a&gt; através de uma &lt;a href="https://docs.github.com/pt/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent" rel="noopener noreferrer"&gt;chave ssh publica&lt;/a&gt;. Após se associar este usuário possuía permissão necessária para fazer alterações nos repositórios da organização.&lt;/p&gt;

&lt;p&gt;Isto aconteceu porquê provavelmente ele conseguiu persistir a sua chave de acesso na coleção de chaves permitidas da organização. Quando um usuário consegue alterar valores de objetos de maneira indesejada através de parâmetros de uma requisição HTTP, chamamos de &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Mass_Assignment_Cheat_Sheet.html" rel="noopener noreferrer"&gt;Vulnerabilidade de Atribuição de Massa&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Para entender melhor imagine que um usuário mal-intencionado descobre que é possível cadastrar uma conta bancaria com um valor de saldo aprovado para uso através da requisição de criação de conta. Este usuário poderia simplesmente transferir este valor antes que a falha fosse identificada e causar sérios prejuízos ao banco.&lt;/p&gt;

&lt;p&gt;Um exemplo de API que permita esta operação é:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Conta&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GenratedValue&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;titular&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;saldo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CadastrarConta&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@AutoWired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ContaRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/contas"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cadastar&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;Conta&lt;/span&gt; &lt;span class="n"&gt;conta&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;UriComponentsBuilder&lt;/span&gt; &lt;span class="n"&gt;uriBuilder&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
        &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conta&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="no"&gt;URI&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uriBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"conta/credito/{id}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;buildAndExpand&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toUri&lt;/span&gt;&lt;span class="o"&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="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por mais que o desenvolvedor especifique que para criar uma conta é necessário enviar apenas o nome do titular no corpo da requisição. Como no exemplo abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /contas
...
body: {
    "titular": "Jordi Henrique Marques da Silva"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O usuário mal-intencionado poderia simplesmente informar o saldo na sua requisição e iniciar sua conta com valor disponível em operação. Por exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /contas
...
body: {
    "titular": "Jordi Henrique Marques da Silva",
    "saldo":1000000.00
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E este é o motivo do porquê não recebemos a entidade como porta de entrada de dados no &lt;strong&gt;&lt;em&gt;Controller&lt;/em&gt;&lt;/strong&gt;, pois ficamos vulneráveis a esta falha de segurança.&lt;/p&gt;

&lt;p&gt;Uma solução simples para este problema é inserir um objeto para representar as informações de cadastro. Este objeto é uma implementação do padrão de projeto &lt;a href="https://www.infoq.com/br/articles/dto-hipster-ou-depreciado/" rel="noopener noreferrer"&gt;Data Transfer Object (DTO)&lt;/a&gt;. Neste objeto inserimos apenas os atributos referente ao cadastro da Conta, não permitindo que o usuário acesse os demais campos da entidade. Abaixo o exemplo para cadastro de conta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CadastroContaDTO&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;titular&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Conta&lt;/span&gt; &lt;span class="nf"&gt;toModel&lt;/span&gt;&lt;span class="o"&gt;(){&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Conta&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;titular&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para deixar o nosso &lt;strong&gt;&lt;em&gt;Controller&lt;/em&gt;&lt;/strong&gt; seguro agora basta substituir o tipo do objeto conta de &lt;em&gt;Conta&lt;/em&gt; para &lt;em&gt;CadastroContaDTO&lt;/em&gt; no método cadastrar e adaptar a lógica de cadastro.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CadastrarConta&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@AutoWired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ContaRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/contas"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cadastar&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;CadastroContaDTO&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;UriComponentsBuilder&lt;/span&gt; &lt;span class="n"&gt;uriBuilder&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;

        &lt;span class="nc"&gt;Conta&lt;/span&gt; &lt;span class="n"&gt;conta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toModel&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conta&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="no"&gt;URI&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uriBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"conta/credito/{id}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;buildAndExpand&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toUri&lt;/span&gt;&lt;span class="o"&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="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O &lt;strong&gt;&lt;em&gt;Controller&lt;/em&gt;&lt;/strong&gt; adaptado fica igual ao código apresentado acima, e além de não ficar vulnerável ao ataque de atribuição de massa, ter um &lt;em&gt;DTO&lt;/em&gt; de entrada permite que os modelos evoluam de maneira independente, ou seja, minha entidade pode ganhar novos campos, sem que meu &lt;em&gt;DTO&lt;/em&gt; seja alterado e vice-versa.&lt;/p&gt;

&lt;p&gt;Ter um &lt;em&gt;DTO&lt;/em&gt; de entrada ou saída pode trazer consigo também uma otimização de dados trafegados na rede, dado que o &lt;em&gt;DTO&lt;/em&gt; contém apenas as informações necessárias para satisfazer uma funcionalidade, campos que antes eram serializados, trafegados e não utilizados, agora não fazem mais parte da mensagem. &lt;/p&gt;

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

&lt;p&gt;Receber uma entidade no &lt;strong&gt;&lt;em&gt;Controller&lt;/em&gt;&lt;/strong&gt; pode trazer grandes danos a segurança da sua aplicação e seu negócio, porém, ao inserir um  &lt;em&gt;DTO&lt;/em&gt; para receber estas informações, proporciona diversas vantagens como maior segurança contra vulnerabilidades, desacoplamento entre os  modelos da API e negócio, e ganhos em desempenho em relação aos dados trafegados na rede.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>java</category>
      <category>spring</category>
    </item>
  </channel>
</rss>
