<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Jean Jacques Barros on Medium]]></title>
        <description><![CDATA[Stories by Jean Jacques Barros on Medium]]></description>
        <link>https://medium.com/@jjeanjacques10?source=rss-9ed0e2046fe4------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*E_DiJh6m5X2wtGSUE3Gwjg.png</url>
            <title>Stories by Jean Jacques Barros on Medium</title>
            <link>https://medium.com/@jjeanjacques10?source=rss-9ed0e2046fe4------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 13:23:17 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@jjeanjacques10/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Spec Driven Development: A abordagem que melhorou meu desenvolvimento com IA]]></title>
            <link>https://jjeanjacques10.medium.com/spec-driven-development-a-abordagem-que-melhorou-meu-desenvolvimento-com-ia-1fc423707c89?source=rss-9ed0e2046fe4------2</link>
            <guid isPermaLink="false">https://medium.com/p/1fc423707c89</guid>
            <category><![CDATA[spec-driven-development]]></category>
            <category><![CDATA[sdd]]></category>
            <category><![CDATA[engenharia-de-software]]></category>
            <category><![CDATA[ai]]></category>
            <dc:creator><![CDATA[Jean Jacques Barros]]></dc:creator>
            <pubDate>Sat, 16 May 2026 23:42:01 GMT</pubDate>
            <atom:updated>2026-05-16T23:42:01.030Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*r7EvBPhzskkHGfg5g6Ye5Q.png" /></figure><p>O Spec Driven Development (SDD) está ganhando destaque como uma abordagem inovadora para o desenvolvimento de software, especialmente em times que já utilizam inteligência artificial (IA) no dia a dia. Nesse artigo vou trazer um pouco das melhores práticas que venho utilizando e mostrar um exemplo prático de SKILL que me ajudou a estruturar melhor as especificações antes de gerar código com IA.</p><blockquote>Acessar o artigo também no meu blog: <a href="https://jjeanjacques10.github.io/posts/2026-05-16-spec-driven-development/">Spec Driven Development: A abordagem que melhorou meu desenvolvimento com IA — Software Engineering</a></blockquote><h3>O que é Spec Driven Development?</h3><p>A IA pode gerar grandes volumes de código, mas frequentemente sofre com a falta de contexto e alucinações, resultando em retrabalho para o desenvolvedor e um gasto desnecessário de tokens.</p><p>O SDD minimiza esse problema ao fornecer especificações claras e estruturadas. Ao invés de começar escrevendo código, começamos definindo:</p><ul><li>O problema</li><li>As regras de negócio</li><li>Fluxos</li><li>Requisitos funcionais</li><li>Requisitos não funcionais</li><li>Critérios de aceite</li><li>Estrutura técnica</li><li>Limitações</li><li>Edge cases</li></ul><h3>Principais benefícios do SDD</h3><ul><li>Menos retrabalho: Com especificações claras, a IA tem menos chances de gerar código que não atende aos requisitos, reduzindo a necessidade de correções.</li><li>Melhor comunicação: O SDD promove uma comunicação mais eficaz entre os membros da equipe, garantindo que todos estejam alinhados quanto aos objetivos do projeto.</li><li>Aumento da produtividade: Com menos retrabalho e melhor comunicação, os times podem se concentrar em tarefas mais estratégicas, aumentando a eficiência geral.</li><li>Melhoria na qualidade do código: Especificações bem definidas ajudam a garantir que o código gerado esteja dentro das boas práticas do time/projeto.</li></ul><p>Estes são os principais benefícios, mas conforme a prática vai ficando mais madura dentro do time, outros benefícios vão surgindo, como a facilidade de onboarding de novos membros, a documentação automática do projeto e a redução de dívidas técnicas.</p><h3>Como funciona na prática?</h3><ol><li>Definição das especificações: O time define as especificações do projeto de forma clara e detalhada, utilizando uma linguagem que a IA possa entender facilmente.</li><li>Validação das especificações: As especificações são revisadas e validadas para garantir que estejam completas e corretas.</li><li>Implementação: A IA gera o código com base nas especificações fornecidas, e os desenvolvedores revisam o código para garantir que atenda aos requisitos.</li><li>Validação: Após o código ser gerado, ele é testado e validado para garantir que funcione conforme esperado. <em>(Aqui está o tempo que foi economizado anteriormente.)</em></li></ol><h3>Como estruturar uma SKILL para geração de SDD</h3><p>Uma skill é um prompt de sistema especializado, associado a um comando (como /spec-driven-build), que guia a IA por um fluxo estruturado de perguntas e geração de artefatos. Em vez de você descrever o processo a cada conversa, a skill carrega esse contexto automaticamente — e fica versionada no próprio repositório.</p><p>O ponto central de uma boa skill de SDD é o fluxo em fases. Cada fase tem uma responsabilidade clara e só avança com a aprovação explícita do desenvolvedor.</p><blockquote><em>Caso queira testar, gerei uma skill que utilizo no dia a dia para criar SDDs para meus projetos pessoais. Você pode ver o código completo dela aqui: </em><a href="https://github.com/jjeanjacques10/skills/blob/main/skills/development/spec-driven-build/SKILL.md"><em>jjeanjacques10/skills/spec-driven-build/SKILL.md</em></a></blockquote><p>Abaixo mostro a estrutura que proponho caso queira criar uma SKILL semelhante para seu time:</p><h3>Output esperado</h3><p>Dentro da pasta docs/&lt;feature&gt;/ do projeto, a skill deve gerar:</p><pre>docs/&lt;feature&gt;/<br>    ├── SDD.md<br>    └── tasks/<br>         ├── TASK-1-&lt;descricao-em-kebab-case&gt;.md<br>         ├── TASK-2-&lt;descricao-em-kebab-case&gt;.md<br>         └── TASK-N-verificacao-e2e.md</pre><h3>Etapas do processo</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ic8CmuqzaSj1Os9G.png" /></figure><h3>Fase 0 — Coleta de contexto</h3><p>Antes de escrever qualquer arquivo, a skill deve fazer perguntas direcionadas para eliminar ambiguidades:</p><ol><li>O que precisa ser corrigido ou implementado?</li><li>Qual é o impacto ou risco se não for feito?</li><li>Quais arquivos ou áreas do código já foram identificados como afetados?</li><li>Há restrições ou decisões já tomadas?</li></ol><h3>Fase 1 — Geração do SDD</h3><p>Com o contexto em mãos, a skill deve explorar o código do projeto com ferramentas reais antes de escrever qualquer linha do documento. O SDD gerado deve referenciar arquivos existentes, números de linha, interfaces e funções — nunca ser genérico.</p><p>A estrutura ideal do SDD.md tem sete seções fixas:</p><p>SeçãoConteúdo1. Contexto e ProblemaEstado atual + risco ou motivação (máx. 3 parágrafos)2. Escopo da CorreçãoO que muda × o que não muda (tabela + lista explícita)3. Design da SoluçãoDescrição técnica por módulo/componente com snippets prontos4. Fluxo após a correçãoDiagrama ASCII do novo fluxo de dados ou de usuário5. Arquivos a modificar/criarTabela de impacto com tipo de mudança6. Critérios de AceiteChecklist objetivo e verificável7. Considerações adicionaisSegurança, performance, débito técnico (omitir se vazio)</p><h3>Fase 2 — Validação do SDD</h3><p>Antes de apresentar o documento ao desenvolvedor, a skill deve fazer uma auto-revisão com um checklist interno:</p><ul><li>Todos os arquivos da seção 5 existem no projeto (ou são explicitamente novos)?</li><li>Os snippets são compatíveis com as versões de biblioteca usadas no projeto?</li><li>Os critérios de aceite são verificáveis objetivamente?</li><li>O escopo está claro e sem ambiguidade entre o que muda e o que não muda?</li></ul><p>Após a revisão, apresentar ao desenvolvedor um resumo de 3–5 linhas e aguardar aprovação antes de avançar.</p><h3>Fase 3 — Geração das Tasks</h3><p>Com o SDD aprovado, a skill cria arquivos individuais de task em docs/&lt;feature&gt;/tasks/. A nomenclatura segue o padrão:</p><pre>TASK-1-&lt;descricao-em-kebab-case&gt;.md<br>TASK-2-&lt;descricao-em-kebab-case&gt;.md<br>...<br>TASK-N-verificacao-e2e.md   ← sempre a última</pre><p>Cada arquivo de task deve conter:</p><ul><li>Arquivo-alvo com caminho completo (novo ou existente)</li><li>Referência à seção do SDD que originou aquela task</li><li>Dependências explícitas (Depende de: TASK-X ou nenhuma)</li><li>Contexto — por que a task existe e o que está errado hoje</li><li>O que fazer — com before/after quando for modificar código existente</li><li>Notas de implementação — armadilhas, decisões não óbvias, efeitos colaterais</li><li>Critério de aceite — checklist verificável</li></ul><h3>Fase 4 — Resumo final</h3><p>A skill encerra apresentando o mapa completo de execução com as dependências e o paralelismo possível:</p><pre>✓ SDD:   docs/&lt;feature&gt;/SDD.md<br>✓ Tasks: docs/&lt;feature&gt;/tasks/<br>   TASK-1 — Criar hook de contexto        [independente]<br>   TASK-2 — Adicionar guard de rota       [após TASK-1]<br>   TASK-3 — Atualizar componente de menu  [após TASK-1, paralelo com TASK-2]<br>   TASK-4 — Verificação e2e              [última]</pre><pre>Paralelas: TASK-2 e TASK-3 podem ser executadas simultaneamente.</pre><blockquote><em>Exemplo de como ficou em um projeto real:</em></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/283/0*2EDNkln7GdXqZdmN.png" /></figure><h3>Como estruturar um bom SDD</h3><p>O maior erro ao escrever um SDD é focar no “o quê” e esquecer o “por quê” e o “o que não muda”. Veja os princípios que fazem um SDD realmente funcionar:</p><p>Seja cirúrgico no escopo. Um SDD que tenta resolver mais de um problema ao mesmo tempo vira uma fonte de confusão. Se durante a escrita você perceber que algo adjacente também deveria ser corrigido, crie um segundo SDD separado.</p><p>Referencie o código real. “Adicionar validação no formulário” é vago. “Adicionar validação na função handleSubmit em src/routes/login.tsx, linha ~83, usando o schema Zod existente em src/lib/schemas.ts” é um SDD. A IA implementa o segundo sem perguntas.</p><p>Escreva snippets completos. Pseudocódigo cria margem para interpretação. Snippets compiláveis eliminam suposições e são a principal razão pela qual a IA gera código correto na primeira tentativa.</p><p>Use a seção “O que não muda” como cerca elétrica. Liste explicitamente o que está fora do escopo. Isso instrui a IA a não refatorar, não renomear, não mover arquivos que não foram solicitados. É a seção que mais reduz retrabalho na prática.</p><p>Critérios de aceite verificáveis. Cada critério deve ser uma afirmação que pode ser marcada como verdadeira ou falsa após a implementação. “O sistema deve funcionar bem” não é um critério — “O banner não aparece se o usuário já aceitou os cookies na sessão anterior” é.</p><h3>Exemplo</h3><p>Você pode ver a skill completa utilizada neste projeto aqui: <a href="https://github.com/jjeanjacques10/skills/blob/main/skills/development/spec-driven-build/SKILL.md">jjeanjacques10/skills/spec-driven-build/SKILL.md</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*LX955V2MMywPeaCL.png" /></figure><h3>Ferramentas para SDD</h3><p>Claude Code (Skills customizadas) A abordagem descrita neste artigo: skills são prompts de sistema associados a comandos (/spec, /review, etc.) que ficam versionados no próprio repositório em .claude/skills/. Funcionam como “especialistas contextuais” que entendem a estrutura e convenções do seu projeto específico.</p><p><a href="https://kiro.dev/">Kiro — AWS</a> O Kiro vai em uma abordagem de a própria IDE apoia no processo de SDD, guiando o desenvolvedor por perguntas estruturadas e gerando artefatos organizados. Ele é mais opinativo e menos customizável que uma skill personalizada, mas pode ser uma boa opção para times que buscam uma solução pronta.</p><p><a href="https://www.cursor.com/">Cursor</a> / <a href="https://githubnext.com/projects/copilot-workspace">GitHub Copilot Workspace</a> Ferramentas de IDE com geração de planos antes do código. O Copilot Workspace cria um “plan” editável antes de propor implementações — uma aproximação da ideia de SDD, mas sem a estrutura formal de documento e tasks separadas.</p><p><a href="https://specflow.org/">SpecFlow</a> <em>(BDD para .NET)</em> Framework que usa a sintaxe Gherkin (Given / When / Then) para escrever especificações legíveis por não-desenvolvedores. Mais voltado a testes de aceitação automatizados do que à fase de design, mas compartilha o princípio de “especificar antes de implementar”.</p><h3>Conclusão</h3><p>O Spec Driven Development representa uma evolução na forma como desenvolvemos software no dia a dia, saindo do tradicional prompt one-shot e adotando uma abordagem mais estruturada e eficiente. A qualidade de uma especificação determina diretamente a qualidade do código gerado — e uma boa skill automatiza exatamente isso: guiar a IA pelo processo certo antes de qualquer linha de código ser escrita.</p><p>Ao implementar o SDD, os times podem reduzir retrabalho, melhorar a comunicação e aumentar a produtividade, resultando em projetos de maior qualidade e sucesso.</p><p>Compartilhe com seu time e experimente essa nova abordagem. Depois me conta os resultados!</p><p>Obrigado por lerem!</p><h3>Referências</h3><ul><li><a href="https://support.claude.com/pt/articles/12512198-como-criar-skills-personalizadas">Documentação oficial do Claude Code sobre Skills personalizadas</a></li><li><a href="https://www.specflow.com/">Site oficial do SpecFlow, framework de BDD para .NET</a></li><li><a href="https://martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html">Understanding Spec-Driven-Development: Kiro, spec-kit, and Tessl</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1fc423707c89" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Resolvendo problemas de Rate Limiter com Token Bucket e Semáforos]]></title>
            <link>https://jjeanjacques10.medium.com/resolvendo-problemas-de-rate-limiter-com-token-bucket-e-sem%C3%A1foros-f521dd1166ef?source=rss-9ed0e2046fe4------2</link>
            <guid isPermaLink="false">https://medium.com/p/f521dd1166ef</guid>
            <category><![CDATA[token-buckets]]></category>
            <category><![CDATA[system-design-interview]]></category>
            <category><![CDATA[semaphore]]></category>
            <category><![CDATA[spring-boot]]></category>
            <category><![CDATA[aws-sqs]]></category>
            <dc:creator><![CDATA[Jean Jacques Barros]]></dc:creator>
            <pubDate>Sun, 18 Jan 2026 05:57:38 GMT</pubDate>
            <atom:updated>2026-01-25T04:18:45.887Z</atom:updated>
            <content:encoded><![CDATA[<p>Como utilizar estratégias de rate limit para controlar o consumo de mensagens em arquiteturas distribuídas.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4t-9Aa_tnztkVYVKnGQvtg.png" /></figure><p>Para quem trabalha com arquiteturas distribuídas e comunicações assíncronas, esse cenário é bem comum:</p><blockquote>Preciso controlar quantas mensagens devem ser processadas ao mesmo tempo pelos consumidores, para evitar o uso excessivo de recursos ou para seguir alguma regra de negócio específica.</blockquote><p>Para resolver esse tipo de problema, temos algumas estratégias interessantes que podemos utilizar. É exatamente isso que quero trazer neste artigo, além da aplicação de exemplo que criei para validar cada um dos cenários:</p><ul><li><a href="https://github.com/jjeanjacques10/rate-limit-hero-orders">https://github.com/jjeanjacques10/rate-limit-hero-orders</a></li></ul><h3>O problema</h3><p>Inspirado no universo de <strong><em>My Hero Academia</em></strong>, onde super-heróis recebem <strong>solicitações de serviço</strong> através de uma fila SQS, precisamos de controle, coordenação e limites para não causar problemas de excesso de requisições.</p><p>Cenário:</p><ul><li>Centenas de solicitações de heróis chegam simultaneamente</li><li>Vários “escritórios de heróis” (containers) processam mensagens em paralelo</li><li>Cada solicitação exige processamento confiável (nenhuma pode ser perdida)</li><li>O sistema não pode sobrecarregar recursos críticos</li></ul><p>Vamos agora explorar algumas estratégias para resolver esse problema…</p><h3>🏗️Estratégia 1: Controle na aplicação (local)</h3><p>Esta é a abordagem mais simples, onde o controle é feito dentro <strong>da própria aplicação/agência</strong>, limitando o que cada container individual pode fazer.</p><h4>Como funciona</h4><p>Configuramos o listener do SQS para processar um número fixo de mensagens por vez. No Spring Cloud AWS, usamos as propriedades maxConcurrentMessages e maxMessagesPerPoll. Cada escritório de super-heróis faz seu próprio controle. No exemplo abaixo, são <strong>3 solicitações por vez</strong>:</p><pre>@SqsListener(<br>    value = [&quot;event-hero-orders-queue&quot;],<br>    maxConcurrentMessages = &quot;3&quot;,<br>    maxMessagesPerPoll = &quot;3&quot;<br>)<br>fun consumeMessage(message: Message&lt;HeroOrderRequest&gt;, acknowledgement: Acknowledgement) {<br>    try {<br>        orderService.processOrder(message.payload)<br>        acknowledgement.acknowledge() // Confirmação manual<br>    } catch (e: Exception) {<br>        // Se falhar, volta para a fila automaticamente (validar maxReceiveCount)<br>    }<br>}</pre><blockquote><em>Em cenários de rate limit ou backpressure intencional (Token Bucket ou Semáforo), a mensagem pode retornar várias vezes para a fila sem representar erro. Caso a fila tenha DLQ configurada, é necessário ajustar o </em><em>maxReceiveCount para evitar que mensagens válidas sejam enviadas indevidamente para a DLQ apenas por falta momentânea de capacidade.</em></blockquote><h4>Vantagem</h4><ul><li>✔ Simples de implementar</li><li>✔ Não depende de componentes externos</li></ul><h4>Desvantagem</h4><ul><li>❌ Não escala bem</li><li>❌ Não existe uma visão <strong>global</strong> do limite</li></ul><p>Se você tiver 10 containers com limite de 3 mensagens, terá <strong>30 mensagens sendo processadas simultaneamente</strong>, independentemente da capacidade real do sistema.</p><h4>Cenário não recomendado</h4><ul><li>❌ Quando o número de containers escala dinamicamente</li></ul><h4>Execução</h4><p>Exemplo de itens sendo processados, de 3 em 3, com cada solicitação durante 5 segundos:</p><p>&gt;<em> ./mvnw spring-boot:run -Dspring.profiles.active=local-control</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WXFoHT8kuapyUfRKA0lErw.png" /><figcaption>Itens sendo processados com os parâmetros configurados (3 por vez no container)</figcaption></figure><p>Agora podemos entrar um pouco em estratégias que funcionam melhor em cenários distribuídos:</p><h3>🚥 Estratégia 2: Semáforos Distribuídos (Redis)</h3><p>Quando precisamos garantir que, <strong>independente do número de escritórios de heróis (containers)</strong>, apenas um certo número de solicitações sejam processadas simultaneamente no cluster todo, podemos usar um <strong>Semáforo Distribuído</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T-6dhAFAAwdvJhJCORqOBw.png" /></figure><h4>O Conceito</h4><p>Definimos o <strong>Redis</strong> como uma fonte da verdade. Antes de processar a mensagem, o consumidor tenta adquirir um “permissão” (permit).</p><ol><li>O consumidor busca a mensagem no SQS.</li><li>Tenta adquirir o permit no Redis.</li><li><strong>Conseguiu?</strong> Processa e, ao final, libera o permit.</li><li><strong>Não conseguiu?</strong> A mensagem não é confirmada (no-ACK) e volta para a fila para ser tentada novamente por outro (ou pelo mesmo) container.</li></ol><pre>@SqsListener(<br>    value = [&quot;event-hero-orders-queue&quot;],<br>    acknowledgementMode = &quot;MANUAL&quot;,<br>    id = &quot;distributed-semaphores-order-consumer&quot;<br>)<br>fun consumeMessage(message: Message&lt;HeroOrderRequest&gt;, acknowledgement: Acknowledgement) {<br>    var permitId: String? = null<br><br>    try {<br>        // Tenta adquirir uma permissão do semáforo distribuído<br>        permitId = distributedSemaphore.tryAcquire(message.payload.heroId)<br><br>        if (permitId == null) {<br>            // Não foi possível adquirir permissão, rejeita a mensagem para ser retentada depois<br>            log.warn(&quot;Could not acquire semaphore permit for order: ${message.payload.heroName}. Message will NOT be acknowledged (returning to queue).&quot;)<br>            // NÃO faz o acknowledge - a mensagem retornará à fila<br>            return<br>        }<br><br>        // Processa o pedido com a permissão<br>        orderService.processOrder(message.payload)<br><br>        // Faz o acknowledge da mensagem apenas após o processamento bem-sucedido<br>        acknowledgement.acknowledge()<br>        log.info(&quot;Message acknowledged successfully for hero: ${message.payload.heroName}&quot;)<br>    } catch (e: Exception) {<br>        // Se o processamento falhar, NÃO faz acknowledge - a mensagem retornará à fila<br>        log.error(&quot;Error processing message for hero: ${message.payload.heroName}. Message will NOT be acknowledged.&quot;, e)<br>        // A mensagem retornará automaticamente à fila<br>    } finally {<br>        // Sempre libera a permissão se ela foi adquirida<br>        permitId?.let {<br>            distributedSemaphore.release(it)<br>        }<br>    }<br>}</pre><blockquote><strong><em>Ponto Chave:</em></strong><em> O uso de TTL (Time-To-Live) nos permits do Redis garante que, se um container morrer, a permissão expire e não trave o sistema indefinidamente.</em></blockquote><h4>Cenário não recomendado</h4><ul><li>❌ Não usar para controle de taxa (ex: requisições por minuto)</li><li>❌ Não usar sem TTL (risco real de deadlock distribuído)</li></ul><h4>Execução</h4><p>Exemplo de itens sendo processados conforme os recursos são liberados:</p><p>&gt; <em>./mvnw spring-boot:run -Dspring.profiles.active=distributed-semaphores</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pYvDC6bq1bmYzdqQp2XsqA.png" /><figcaption>Processamento de solicitações com semáforo</figcaption></figure><h3>🪣 Estratégia 3: Token Bucket Distribuído</h3><p>Se o seu problema não é apenas a concorrência (quantos agora), mas sim a <strong>taxa de transferência</strong> (quantos por segundo), o algoritmo <strong>Token Bucket</strong> é a escolha ideal. Esse é o que mais vejo ser implementado no dia a dia!</p><h4>Como funciona</h4><p>Imagine um balde que se enche de “tokens” a uma taxa constante (ex: 2 tokens por segundo). Para processar uma mensagem, o consumidor precisa retirar um token do balde, para um novo serviço ser processado é necessário que os heróis estejam disponíveis para isso.</p><ul><li><strong>Burst:</strong> Se o balde estiver cheio (ex: 10 tokens), o sistema pode processar 10 mensagens.</li><li><strong>Refill:</strong> Após o pico, ele volta a processar apenas na velocidade em que os tokens são repostos (2/s).</li></ul><pre>fun tryConsume(tokensNeeded: Int = 1): Boolean {<br>    refillTokens() // Reabastece tokens baseado no tempo<br>    <br>    val currentTokens = getCurrentTokens()<br>    if (currentTokens &gt;= tokensNeeded) {<br>        // Consome o token atomicamente<br>        redisTemplate.opsForValue().decrement(TOKEN_BUCKET_KEY, tokensNeeded.toLong())<br>        return true<br>    }<br>    return false<br>}</pre><p>Com isso o consumidor segue o seguinte formato:</p><pre>@SqsListener(<br>    value = [&quot;event-hero-orders-queue&quot;],<br>    acknowledgementMode = &quot;MANUAL&quot;,<br>    id = &quot;distributed-token-bucket-order-consumer&quot;<br>)<br>fun consumeMessage(message: Message&lt;HeroOrderRequest&gt;, acknowledgement: Acknowledgement) {<br>    try {<br>        // Tenta consumir um token do bucket<br>        if (!tokenBucket.tryConsume()) {<br>            // Nenhum token disponível, rejeita a mensagem para ser retentada depois<br>            log.warn(&quot;No token available for order: ${message.payload.heroName}. Message will NOT be acknowledged (returning to queue).&quot;)<br>            // NÃO faz o acknowledge - a mensagem retornará à fila<br>            return<br>        }<br><br>        // Processa o pedido se o token foi consumido<br>        orderService.processOrder(message.payload)<br><br>        // Faz o acknowledge a mensagem apenas após o processamento bem-sucedido<br>        acknowledgement.acknowledge()<br>        log.info(&quot;Message acknowledged successfully for hero: ${message.payload.heroName}&quot;)<br>    } catch (e: Exception) {<br>        // Se o processamento falhar, NÃO faz acknowledge - a mensagem retornará à fila<br>        log.error(&quot;Error processing message for hero: ${message.payload.heroName}. Message will NOT be acknowledged.&quot;, e)<br>        // A mensagem retornará automaticamente à fila<br>    }<br>}</pre><h4>Cenário não recomendado</h4><ul><li>❌ Não usar quando o problema é concorrência simultânea</li><li>❌ Evitar usar sem que a operação seja atômica (como no exemplo acima).</li></ul><h4>Execução</h4><p>Processamentos sendo feitos com o uso de token bucket, muitas transações retornam para fila aguardando a liberação de mais fichas no balde.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OrxwBP3ExClY0qk2t2aLWg.png" /></figure><h3>🪣 Estratégia 4: Token Bucket Distribuído com <strong>Bucket4J</strong></h3><p>As implementações acima foram feitas de forma manual, vamos explorar um pouco uma solução de mercado para isso. Uma das mais famosas é o <a href="https://github.com/bucket4j/bucket4j">Bucket4J</a>, uma biblioteca Java para rate limit baseada no algoritmo de token-bucket. Mas antes quero falar um pouco dos motivos que resolvi trazer essa solução para esse artigo a “Atomicidade”:</p><h4>Atomicidade</h4><p>Atomicidade garante que a operação de <strong>verificar a quantidade de tokens disponível e consumi-los</strong> aconteça como <strong>uma única operação indivisível</strong>.</p><blockquote>Ou tudo acontece, ou nada acontece.</blockquote><p>Sem atomicidade, o sistema fica vulnerável a <strong>race conditions</strong>, como no exemplo abaixo:</p><ul><li>Dois consumidores leem simultaneamente que há 1 token disponível</li><li>Ambos acreditam que podem processar a mensagem</li><li>Ambos decrementam o contador</li><li>O limite é violado e mais mensagens são processadas do que o permitido</li></ul><p>Na primeira implementação de token bucket não tínhamos essa garantia, por serem múltiplos comandos do Redis, não uma única transação atômica. Isso significa que dois ou mais consumidores podem intercalar operações e violar o limite do balde. A lib do Bucket4j usa uma estratégia CAS (comparar e trocar) sobre um único estado de bucket serializado no Redis.</p><h4>Configurando o bucket4j</h4><p>Configuração do Bucket4J, preferi separar em uma classe de configuração nesse caso:</p><pre>@Configuration<br>class BucketConfig(<br>    @Value(&quot;\${spring.data.redis.host:localhost}&quot;) private val redisHost: String,<br>    @Value(&quot;\${spring.data.redis.port:6379}&quot;) private val redisPort: Int,<br>    @Value(&quot;\${spring.data.redis.password}&quot;) private val redisPassword: String<br>) {<br><br>    @Bean<br>    fun bucket4jRedisClient(): RedisClient {<br>        val redisUri = if (redisPassword.isNotBlank()) {<br>            &quot;redis://$redisPassword@$redisHost:$redisPort&quot;<br>        } else {<br>            &quot;redis://$redisHost:$redisPort&quot;<br>        }<br>        return RedisClient.create(redisUri)<br>    }<br><br>    @Bean<br>    fun bucket4jRedisConnection(redisClient: RedisClient): StatefulRedisConnection&lt;String, ByteArray&gt; {<br>        return redisClient.connect(RedisCodec.of(StringCodec.UTF8, ByteArrayCodec.INSTANCE))<br>    }<br><br>    @Bean<br>    fun bucket4jProxyManager(connection: StatefulRedisConnection&lt;String, ByteArray&gt;): ProxyManager&lt;String&gt; {<br>        return LettuceBasedProxyManager.builderFor(connection).build()<br>    }<br><br>    @Bean<br>    fun bucket4jConfigurationConfig(): BucketConfiguration {<br>        return BucketConfiguration.builder()<br>            .addLimit { limit -&gt;<br>                limit.capacity(MAX_TOKENS)<br>                    .refillGreedy(REFILL_TOKENS, Duration.ofSeconds(REFILL_PERIOD_SECONDS))<br>            }.build()<br>    }<br><br>    @Bean<br>    fun bucket(proxyManager: ProxyManager&lt;String&gt;, bucketConfiguration: BucketConfiguration): Bucket {<br>        val bucket = proxyManager.builder().build(BUCKET_KEY) { bucketConfiguration }<br>        log.info(&quot;Bucket4j initialized with capacity: $MAX_TOKENS tokens, refill rate: $REFILL_TOKENS tokens per $REFILL_PERIOD_SECONDS second(s)&quot;)<br>        return bucket<br>    }<br><br>    companion object {<br>        val log: Logger = LoggerFactory.getLogger(this::class.java)<br>        const val BUCKET_KEY = &quot;hero:orders:bucket4j:token_bucket&quot;<br>        const val MAX_TOKENS = 10L // Capacidade máxima do bucket<br>        const val REFILL_TOKENS = 2L // Tokens adicionados<br>        const val REFILL_PERIOD_SECONDS = 1L // Período de reabastecimento (1 segundo)<br>    }<br>}</pre><p>Com isso o controle fica no service da aplicação, validando se existem tokens válidos para uso naquele momento.</p><pre>@Service<br>class RedisDistributedTokenBucket4j(<br>    private val bucket: Bucket<br>) {<br><br>    /**<br>     * Tenta consumir um token do bucket<br>     * @param tokensNeeded número de tokens necessários (padrão 1)<br>     * @return true se o(s) token(s) foi(ram) consumido(s), false caso contrário<br>     */<br>    fun tryConsume(tokensNeeded: Long = 1): Boolean {<br>        val consumed = bucket.tryConsume(tokensNeeded)<br><br>        if (consumed) {<br>            val availableTokens = bucket.availableTokens<br>            log.info(&quot;Token(s) consumed: $tokensNeeded. Available tokens: $availableTokens/$MAX_TOKENS&quot;)<br>        } else {<br>            val availableTokens = bucket.availableTokens<br>            log.warn(&quot;Not enough tokens available. Current: $availableTokens/$MAX_TOKENS, Needed: $tokensNeeded&quot;)<br>        }<br><br>        return consumed<br>    }<br><br>    companion object {<br>        val log: Logger = LoggerFactory.getLogger(this::class.java)<br>    }<br>}</pre><h4>Vantagem</h4><ul><li>✔ Não há race conditions, o CAS (Compare-And-Set) garante atomicidade</li><li>✔ Fácil de configurar, com uma API simples e intuitiva</li><li>✔ Testado em produção por milhares de empresas</li></ul><p>Sempre é bom considerar “opções de mercado” com soluções já prontas, onde a “roda” não precisa ser reimplementada.</p><h4>Execução</h4><p>Exemplo de itens sendo processados utilizandos os tokens no bucket:</p><p><em>&gt; ./mvnw spring-boot:run -Dspring.profiles.active=</em>distributed-token-bucket4j</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*j9bHxCpCdUEFyLO2rdynTg.png" /><figcaption>Logs da execução com token bucket utilizando a lib bucket4j</figcaption></figure><h3>📊 Semáforo vs. Token Bucket</h3><p>Colocando as duas opções lado a lado:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/836/1*lkgLUi2SaibzeuLHQlJdvA.png" /><figcaption>Diferença entre Semáforo e Token Bucket</figcaption></figure><h3>💡 Conclusão</h3><p>Não existe “bala de prata” para rate limit.</p><ul><li>Se o controle é simples e o número de instâncias é fixo, um controle a nível de aplicação resolve!</li><li>Se temos uma limitação relacionada a quantos itens simultâneos precisam ser processados, por exemplo, caso seu banco de dados não aguenta mais de 10 conexões simultâneas, use <strong>Semáforos</strong>.</li><li>Se o controle precisa ser quantos processos são permitidos, como 100 itens por minuto, vá de <strong>Token Bucket</strong>.</li><li>Mesmo que seja possível implementar soluções próprias, sempre busque utilizar soluções de mercado se possível.</li></ul><p>O segredo está em entender o gargalo do seu sistema e escolher a estratégia que melhor protege seus recursos sem sacrificar a resiliência.</p><p>Caso tenha alguma crítica, sugestão ou dúvida fique à vontade para me enviar uma mensagem:</p><p>LinkedIn: <a href="https://www.linkedin.com/in/jjean-jacques10/">https://www.linkedin.com/in/jjean-jacques10/</a></p><p>Até a próxima!</p><h3>Referências</h3><ul><li><a href="https://fidelissauro.dev/api-gateway/#:~:text=de%20forma%20reativa.-,Token%20Bucket,-O%20Token%20Bucket">System Design — API Gateway | Matheus Fidelis (Token Bucket)</a></li><li><a href="https://www.youtube.com/watch?v=1HJwVcttuNU">Você Sabe o que é Token Bucket? Entenda de Forma Simples</a></li><li><a href="https://github.com/jjeanjacques10/rate-limit-hero-orders">jjeanjacques10/rate-limit-hero-orders</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f521dd1166ef" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Descomplicando a Configuração de Producers e Consumers com Kafka]]></title>
            <link>https://jjeanjacques10.medium.com/descomplicando-a-configura%C3%A7%C3%A3o-de-producers-e-consumers-com-kafka-0ad94058000e?source=rss-9ed0e2046fe4------2</link>
            <guid isPermaLink="false">https://medium.com/p/0ad94058000e</guid>
            <category><![CDATA[kafka]]></category>
            <category><![CDATA[message-queue]]></category>
            <category><![CDATA[spring-boot]]></category>
            <category><![CDATA[broker]]></category>
            <dc:creator><![CDATA[Jean Jacques Barros]]></dc:creator>
            <pubDate>Wed, 08 May 2024 10:57:19 GMT</pubDate>
            <atom:updated>2024-05-08T10:57:19.952Z</atom:updated>
            <content:encoded><![CDATA[<h4>Entendendo como configurar Producers e Consumers com Kafka de forma simples e descomplicada visando o que faz sentido para os seus projetos.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*W87cgope7_inl_rM.png" /></figure><p>Em algum momento pode ser que apareça uma tarefa em seu trabalho onde precise criar uma integração com o Kafka, quando você começa a desenvolver sua aplicação, criar sua lógica de negócio e então se de para com o arquivo cheio de parâmetros surgindo a pergunta: “Quais configurações devo fazer aqui que fazem sentido para o meu cenário?”.</p><p>É comum esse tipo de questionamento, kafka como uma ferramenta robusta nos oferece diversos recursos e configurações, e é importante entender como cada uma delas pode ser utilizada para atender as necessidades do seu projeto. Vamos entender um pouco mais sobre o Kafka e trazer exemplos práticos de como você pode preencher esses parâmetros da melhor forma sem precisar “chutar” o que pode ou não ser o melhor para o seu cenário.</p><h3>O que é o Kafka</h3><p>Kafka é uma plataforma de streaming distribuída desenvolvida pelo LinkedIn em 2011 e posteriormente, foi doada para a <a href="https://www.apache.org/">Apache Software Foundation</a>, que hoje cuida da ferramenta. Nasceu da necessidade de lidar com fluxos de dados em tempo real de forma escalável, durável e tolerante a falhas, tornando-se uma peça fundamental em arquiteturas modernas de processamento de dados com a capacidade de processar trilhões de mensagens por dia. Em exemplos reais, Kafka pode ser utilizado para:</p><ul><li>Streaming de Dados em tempo real</li><li>Monitoramento e Alertas</li><li>Processamento de Big Data</li><li>Integração entre Microsserviços</li></ul><h4>Tópicos</h4><p>Tópicos são utilizados para organizar o fluxo de dados no Kafka, onde os registros são armazenados na sequência em que foram gerados, vamos entrar em detalhes mais a frente de como isso funciona. Por exemplo, uma aplicação de processamento de pagamentos pode ter tópicos como “transacoes-cartao”, “transacoes-boletos” e “transacoes-pix”, cada um representando um tipo diferente de transação financeira.</p><p><strong>Tópicos vs Filas</strong>: Ao discutir tópicos no Kafka, muitas vezes surge a dúvida sobre a diferença entre eles e as filas. Em uma fila, as mensagens são consumidas sequencialmente por um único consumidor, garantindo o processamento FIFO (First In, First Out). Por outro lado, em um tópico, segue-se o modelo publish/subscribe, onde cada mensagem publicada é consumida por todos os consumidores registrados no tópico. Além disso, são utilizadas técnicas de particionamento para garantir a escalabilidade na leitura das mensagens pelos grupos de consumidores.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Nh3HGYjCTMZql3Cu.png" /></figure><h4>Brokers</h4><p>Os brokers no Kafka são os servidores encarregados de armazenar e gerenciar os dados dos tópicos. Cada broker é parte de um cluster Kafka e mantém uma cópia dos dados dos tópicos aos quais está atribuído. Eles são escaláveis e podem ser adicionados ou removidos do cluster conforme necessário para aumentar a capacidade ou a disponibilidade do sistema.</p><h4>Partições (Partitions)</h4><p>Partições são segmentações que permitem dividir o tópico em frações menores; novas mensagens são adicionadas a uma <em>partition</em>.</p><p>Cada partição possui <em>offsets</em>, que são a identificação da mensagem dentro de uma partição específica, o que ajuda a saber a ordem em que foram recebidas e que devem ser processadas. Serem separadas por <em>offsets</em> facilita na hora de processar as mensagens, pois é possível configurar para que sejam lidas a partir de um offset específico, como em casos de reprocessamento, regras de negócio que precisem “voltar no tempo” para processar mensagens antigas ou onde queremos apenas ler mensagens de um determinado ponto.</p><p>Utilizar várias partições garante desempenho com grandes cargas de trabalho, aproveitando a replicação e a distribuição de carga entre os brokers. Por exemplo, se um tópico tiver 3 partições e 3 consumidores, cada consumidor lerá de uma partição diferente, garantindo que as mensagens sejam processadas de forma paralela.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*qtp5IxXd3YOL3ZsT.png" /></figure><h4>Avro</h4><p>O Kafka transfere bytes de um local para outro, Avro é uma solução para garantir o contrato nessa comunicação. Ele é uma ferramenta de serialização/deserialização de dados que define o formato das mensagens que serão enviadas e recebidas. Além disso, ele permite definir esquemas para os dados transferidos, o que auxilia na garantia de que os dados sejam interpretados corretamente pelos consumidores, mesmo quando os esquemas evoluem ao longo do tempo.</p><p>Aqui está um exemplo de um esquema Avro que define um objeto chamado <strong>TransactionItem</strong>:</p><pre>{<br>  &quot;type&quot;: &quot;record&quot;,<br>  &quot;name&quot;: &quot;TransactionItem&quot;,<br>  &quot;namespace&quot;: &quot;com.payment&quot;,<br>  &quot;fields&quot;: [<br>    {<br>      &quot;name&quot;: &quot;value&quot;,<br>      &quot;type&quot;: &quot;string&quot;<br>    },<br>    {<br>      &quot;name&quot;: &quot;origin&quot;,<br>      &quot;type&quot;: {<br>        &quot;type&quot;: &quot;record&quot;,<br>        &quot;name&quot;: &quot;Origin&quot;,<br>        &quot;fields&quot;: [<br>          {<br>            &quot;name&quot;: &quot;account&quot;,<br>            &quot;type&quot;: &quot;string&quot;<br>          }<br>        ]<br>      }<br>    },<br>    {<br>      &quot;name&quot;: &quot;createdAt&quot;,<br>      &quot;type&quot;: {<br>        &quot;type&quot;: &quot;string&quot;<br>      }<br>    }<br>  ]<br>}</pre><p><em>Importante lembrar que o Avro não é obrigatório, mas é uma boa prática utilizá-lo para garantir a compatibilidade entre as mensagens enviadas e recebidas.</em></p><p>Além dos exemplos mencionados anteriormente, existem muitas outras aplicações para essa ferramenta. Neste artigo, vamos nos concentrar na comunicação entre <strong>microsserviços</strong>. Nosso exemplo será um <strong>sistema de pagamentos</strong> que processa transações PIX e notifica os clientes quando tudo ocorrer com sucesso!</p><blockquote><em>Todos os exemplos apresentados neste artigo podem ser encontrados no seguinte repositório no GitHub: </em><a href="https://github.com/jjeanjacques10/payment-async-kafka"><em>jjeanjacques10/payment-async-kafka: This is a payment system that utilizes Kafka technology for asynchronous integration</em></a></blockquote><h3>Producer</h3><p>O producer Kafka é responsável por gerar as mensagens e publicá-las nos tópicos. É possível ter diversos producers publicando mensagens em um mesmo tópico, e também é possível ter diversos producers publicando mensagens em tópicos diferentes. Para evitar de perder mensagens, lembre-se de configurar o “acks”, que define o número de replicas que devem confirmar o recebimento da mensagem, e também o “retries”, que define o número de tentativas que o producer deve realizar para enviar a mensagem.</p><p>Aqui está um exemplo de configuração para um produtor em Spring, onde as principais configurações estão relacionadas à forma como queremos enviar as mensagens.</p><pre>spring:<br>  application.name: pix-processor<br>  kafka:<br>    producer:<br>      bootstrap-servers: localhost:9092<br>      key-serializer: org.apache.kafka.common.serialization.StringSerializer<br>      value-serializer: io.confluent.kafka.serializers.KafkaAvroSerializer<br>      compression-type: snappy # Configura o tipo de compressão que deve ser aplicado às mensagens<br>      acks: all # Configura o acks para &quot;all&quot; para garantir que todas as réplicas confirmem o recebimento da mensagem<br>      retries: 3 # Configura o número de tentativas que o producer deve realizar para enviar a mensagem<br>    template:<br>      default-topic: payment-topic</pre><ul><li>bootstrap-servers: Define os endpoints utilizados para se conectar com o cluster Kafka. Ex: bootstrap-servers=kafka1:9092,kafka2:9092</li><li>key-serializer: Classe responsável pela serialização das chaves das mensagens produzidas. Ex: key-deserializer=org.apache.kafka.common.serialization.StringDeserializer</li><li>value-serializer: Classe responsável pela serialização dos valores das mensagens produzidas, ele define o formato que os consumidores devem seguir. Ex: key-deserializer=io.confluent.kafka.serializers.KafkaAvroSerializer</li><li>properties.schema.registry.url: URL do registro de esquemas utilizado para registrar e recuperar esquemas. Ex: schema.registry.url=<a href="http://localhost:8082/">http://localhost:8082</a> (exemplo para configuração local)</li><li>auto.register.schemas: Indica se os esquemas que ainda não existem devem ser registrados automaticamente no <em>schema registry</em>. Ex: auto.register.schemas=false</li><li>retries: Número de tentativas que o produtor deve realizar para enviar a mensagem. Ex: retries=3</li></ul><h4>acks</h4><p>O “acks” é uma propriedade que define o número de réplicas que devem confirmar o recebimento da mensagem. Existem três valores possíveis para essa propriedade:</p><ul><li>0: O produtor não aguarda nenhuma confirmação.</li><li>1: O produtor aguarda a confirmação do líder da partição.</li><li>all: O produtor aguarda a confirmação de todas as réplicas da partição.</li></ul><h4>value-serializer</h4><p>Um dos pontos principais a serem configurados no produtor é o “value-serializer”, que define como os valores das mensagens serão serializados. No exemplo acima, o “value-serializer” está configurado como “io.confluent.kafka.serializers.KafkaAvroSerializer”, que é um serializador Avro.</p><ul><li>io.confluent.kafka.serializers.KafkaAvroDeserializer</li><li>org.apache.kafka.common.serialization.StringSerializer</li><li>org.apache.kafka.common.serialization.ByteArraySerializer</li></ul><h4>compression-type</h4><p>O “compression-type” é uma propriedade que define o tipo de compressão que deve ser aplicado às mensagens. Existem alguns tipos de compressão disponíveis, como:</p><ul><li>none: Sem compressão.</li><li>gzip: Compressão GZIP.</li><li>snappy: Compressão Snappy.</li><li>lz4: Compressão LZ4.</li></ul><h3>Consumer</h3><p>Os consumidores são aqueles que se conectam aos nossos tópicos para ler as mensagens publicadas pelos produtores. É possível ter diversos consumers conectados a um tópico. Para evitar a leitura de mensagens repetidas, lembre-se de configurar o “group-id”, definindo que o novo consumidor faz parte de um grupo único e que realiza o mesmo processo e também realiza o “acknowledging” que remove a mensagem da fila para o grupo definido. É utilizado o <em>offset</em> para controlar a leitura das mensagens, garantindo que as mensagens sejam lidas apenas uma vez por aquele grupo consumidor (group-id).</p><p>Aqui está um exemplo de configuração para um consumidor em Spring, onde as principais configurações estão relacionadas à forma como queremos receber as mensagens e como elas devem ser tratadas.</p><pre>spring:<br>  application.name: notification<br>  kafka:<br>    consumer:<br>      bootstrap-servers: localhost:9092<br>      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer<br>      value-deserializer: io.confluent.kafka.serializers.KafkaAvroDeserializer<br>      group-id: notification-payment-group<br>      properties:<br>        auto.offset.reset: earliest # ou &#39;latest&#39;<br>    listener:<br>      ack-mode: MANUAL_IMMEDIATE<br>    template:<br>      default-topic: payment-topic<br>    properties:<br>      spring.deserializer.key.delegate.class: org.apache.kafka.common.serialization.StringDeserializer<br>      spring.deserializer.value.delegate.class: io.confluent.kafka.serializers.KafkaAvroDeserializer<br>      spring.deserializer.value.fail-on-unknown-type: false<br>      spring.deserializer.value.type: io.confluent.kafka.serializers.subject.RecordNameStrategy<br>      schema.registry.url: http://localhost:8082<br>      specific.avro.reader: true # Deserialize to the generated Avro class rather than a GenericRecord type<br>      auto.register.schemas: false # Whether schemas that do not yet exist should be registered</pre><p>Existem tantas configurações que seria difícil descrevê-las todas neste artigo, então vou focar em algumas das propriedades que costumo adicionar em minhas configurações, busque entender o que faz mais sentido para o seu cenário.</p><ul><li>key-deserializer: Classe responsável pela desserialização das chaves das mensagens consumidas. Ex: key-deserializer=org.apache.kafka.common.serialization.StringDeserializer</li><li>value-deserializer: Classe responsável pela desserialização dos valores das mensagens consumidas. Ex: value-deserializer=io.confluent.kafka.serializers.KafkaAvroDeserializer</li><li>group-id: Identificador do grupo de consumidores ao qual este consumidor pertence. Ex: group-id=pix-consumer-group</li><li>properties.spring.deserializer.key.delegate.class: Classe delegada responsável pela desserialização das chaves das mensagens, utilizada para configurações específicas do Spring. Ex: spring.deserializer.key.delegate.class=org.apache.kafka.common.serialization.StringDeserializer</li><li>properties.spring.deserializer.value.delegate.class: Classe delegada responsável pela desserialização dos valores das mensagens, utilizada para configurações específicas do Spring. Ex: spring.deserializer.value.delegate.class=io.confluent.kafka.serializers.KafkaAvroDeserializer</li><li>properties.spring.deserializer.value.fail-on-unknown-type: Indica se deve falhar ao desserializar um tipo desconhecido. Pode auxiliar em cenários onde o esquema é desconhecido. Ex: spring.deserializer.value.fail-on-unknown-type=false</li><li>properties.spring.deserializer.value.type: Estratégia para obter o nome do registro do esquema para um determinado valor, usado em conjunto com o Schema Registry. Ex: spring.deserializer.value.type=io.confluent.kafka.serializers.subject.RecordNameStrategy</li><li>specific.avro.reader: Indica se a desserialização deve ser feita para a classe Avro gerada específica ou para o tipo GenericRecord. Ex: specific.avro.reader=true</li></ul><h4>ack-mode</h4><p>O “ack-mode” é uma propriedade que define como o consumidor deve confirmar o recebimento da mensagem. Existem três modos de confirmação:</p><ul><li>RECORD: Confirmação de registro por registro.</li><li>BATCH: Confirmação de registros em lote.</li><li>MANUAL: Confirmação manual.</li><li>MANUAL_IMMEDIATE: Confirmação manual imediata.</li></ul><p>No exemplo abaixo, o “ack-mode” está configurado para MANUAL_IMMEDIATE, ou seja, a confirmação é feita manualmente e imediatamente após o processamento da mensagem.</p><pre>try {<br>    log.info(&quot;Consume Kafka message - topic: {}, offset: {}, partition: {}&quot;, topic, offset, partition)<br>    notificationService.process(transactionItem.toTransaction())<br>} catch (<br>ex: `Exception) {`<br>    log.error(&quot;Error processing message: {}&quot;, ex.message, ex)<br>} finally {<br>    ack.acknowledge() // Confirmação manual sendo realizada<br>}</pre><h4>consumer.properties.auto.offset.reset</h4><p>No Kafka, as configurações “earliest” e “latest” são usadas para determinar onde o consumidor deve começar a consumir mensagens.</p><ul><li>earliest: Quando o deslocamento da partição não está presente (por exemplo, quando o grupo de consumidores está consumindo a partição pela primeira vez), o consumidor começará a consumir a partir do início da partição.</li><li>latest: Quando o deslocamento da partição não está presente, o consumidor começará a consumir a partir do final da partição, ou seja, ele não consumirá nenhuma mensagem que já esteja na partição no momento em que começou a consumir.</li></ul><h3>Boas práticas</h3><p>É uma boa prática adiciona o log do <em>offset</em> e também a partition que está lendo, isso pode auxiliar na análise futura do problema de ambos os lados, tanto no consumer quanto no producer.</p><pre>@KafkaListener(topics = [&quot;payment-topic&quot;], containerFactory = &quot;kafkaListenerContainerFactory&quot;)<br>fun consumePayment(<br>    @Payload transaction: TransactionItem,<br>    @Header(KafkaHeaders.OFFSET) offset: Long,<br>    @Header(KafkaHeaders.RECEIVED_PARTITION) partition: Int?,<br>    @Header(KafkaHeaders.RECEIVED_TOPIC) topic: String?,<br>    ack: Acknowledgment<br>) {<br>    try {<br>        log.info(&quot;Consume Kafka message - topic: {}, offset: {}, partition: {}&quot;, topic, offset, partition)<br>    } catch (<br>      ex: `Exception) {`<br>        log.error(&quot;Error processing message: {}&quot;, ex.message, ex)<br>    } finally {<br>        ack.acknowledge()<br>    }<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*O7AsGrnB-fMDyHwf.png" /></figure><p>Outro ponto importante é a segurança, em ambientes produtivos é essencial proteger o cluster Kafka. Isso pode incluir autenticação, autorização, SSL/TLS, entre outros. Aqui está um exemplo de configuração para habilitar SSL/TLS:</p><pre>spring.kafka.properties.security.protocol: SSL<br>spring.kafka.properties.ssl.truststore.location: /path/to/truststore<br>spring.kafka.properties.ssl.truststore.password: truststorePassword<br>spring.kafka.properties.ssl.keystore.location: /path/to/keystore<br>spring.kafka.properties.ssl.keystore.password: keystorePassword</pre><p>Essas configurações devem ser ajustadas de acordo com as necessidades específicas do ambiente e da política de segurança da organização.</p><h3>Conclusão</h3><p>Desde que comecei a aprender sobre mensageria e todas as possíveis aplicações percebi como é um tema vasto. Até focando apenas no Kafka já vemos como não é algo simples, sendo ele uma ferramenta robusta que pode ser utilizada para diversos cenários, desde streaming de dados até integração entre microsserviços. Neste artigo, vimos algumas das principais configurações que pode ser feitas em nossas aplicações, para ir mais além vá mais a fundo na documentação e realize testes dentro de casa.</p><p>A forma que mais aprendi sobre Kafka foi passando por problemas durante o desenvolvimento, e o que mais me auxiliou a resolver esses problemas foi entender melhor como a ferramenta funciona, por isso a introdução de conceitos básicos é essencial para quem está começando.</p><p>Espero que este artigo tenha sido último em sua jornada e que seja um ponto de partida para você começar a explorar cada vez mais o Kafka em seus projetos.</p><p>Fique a vontade para compartilhar tópicos que ache interessantes nos comentários!</p><p>Gostaria de agradecer ao <a href="https://www.linkedin.com/in/gustavo-santos-madeira-611144151/">Gustavo Santos Madeira</a> por ter me apresentado estes conceitos sobre o Kafka me incentivado a ir mais a fundo nos estudos.</p><p>Caso tenha alguma crítica, sugestão ou dúvida fique a vontade para me enviar uma mensagem:</p><p>LinkedIn: <a href="https://www.linkedin.com/in/jjean-jacques10/">https://www.linkedin.com/in/jjean-jacques10/</a></p><p>Até a próxima!</p><h3>Referências</h3><ul><li><a href="https://kafka.apache.org/">https://kafka.apache.org/</a></li><li><a href="https://docs.spring.io/spring-kafka/api/org/springframework/kafka/listener/package-summary.html">org.springframework.kafka.listener (Spring for Apache Kafka 3.1.4 API)</a></li><li><a href="https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/serdes-avro.html">Avro Schema Serializer and Deserializer for Schema Registry | Confluent Documentation</a></li><li><a href="https://www.lydtechconsulting.com/blog-kafka-schema-registry-demo-part2.html">Kafka Schema Registry &amp; Avro: Spring Boot Demo (2 of 2)</a></li><li><a href="https://atitudereflexiva.wordpress.com/2019/03/12/implementacao-de-uma-fila-e-de-um-topico-jms/">Implementação de uma Fila e de um Tópico JMS</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0ad94058000e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Envie Artigos do Medium para o Kindle com Facilidade]]></title>
            <link>https://jjeanjacques10.medium.com/envie-artigos-do-medium-para-o-kindle-com-facilidade-a212ddd83a08?source=rss-9ed0e2046fe4------2</link>
            <guid isPermaLink="false">https://medium.com/p/a212ddd83a08</guid>
            <category><![CDATA[kindle]]></category>
            <category><![CDATA[medium]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[leitura]]></category>
            <category><![CDATA[github]]></category>
            <dc:creator><![CDATA[Jean Jacques Barros]]></dc:creator>
            <pubDate>Sun, 05 May 2024 12:15:59 GMT</pubDate>
            <atom:updated>2026-04-17T01:11:32.978Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yOuqadOCgMu6Q3ktjq3l3g.png" /></figure><p>Você é um leitor ávido que adora consumir conteúdo do Medium? Já imaginou poder ler seus artigos favoritos no Kindle, com a tela maior e mais confortável?</p><p>Com o <strong>Article Kindle Converter</strong>, você pode fazer isso com apenas alguns cliques! Essa ferramenta gratuita converte artigos do Medium para o formato epub, permitindo que você os envie diretamente para o seu Kindle.</p><h4><strong>Como Funciona</strong></h4><ol><li><strong>Acesse o site:</strong> <a href="https://medium-kindle-sender.onrender.com/">https://medium-kindle-sender.onrender.com</a></li><li><strong>Cole o link do artigo:</strong> Insira o link do artigo do Medium que você deseja ler no Kindle.</li><li><strong>Opcional: Adicione seu e-mail:</strong> Se desejar receber o artigo convertido por e-mail, digite seu endereço de e-mail.</li><li><strong>Converta e baixe:</strong> Clique em “Baixar epub” para converter o artigo para o formato epub e baixá-lo para o seu computador.</li><li><strong>Envie para o Kindle:</strong> Se você configurou o envio por e-mail, o artigo será enviado automaticamente para o seu Kindle. Caso contrário, você pode enviar o arquivo epub manualmente para o seu e-mail do Kindle.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*4xYpUG_jgXnpsjml" /></figure><h4><strong>Configuração do E-mail do Kindle</strong></h4><p>Para enviar artigos diretamente para o seu Kindle por e-mail, é necessário configurar o seu endereço de e-mail na Amazon (@kindle.com). Siga estas etapas que também estão descritas no site oficial <a href="https://www.amazon.com.br/sendtokindle/email">Send to Kindle — E-mail (amazon.com.br)</a>:</p><ol><li>Acesse <a href="https://www.amazon.com.br/hz/mycd/digital-console/contentlist/booksAll/dateDsc/">Gerencie seu conteúdo e dispositivos</a></li><li>Vá para a guia “Preferências”.</li><li>Em “Configurações de Documentos Pessoais”, role para baixo até “Lista de E-mails de Documentos Pessoais Aprovados”.</li><li>Clique em “Adicionar um novo endereço de e-mail aprovado”.</li><li>Digite o endereço de e-mail que você deseja usar e clique em “Adicionar Endereço”.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*5OUSQCF2cqq2_Dko" /></figure><p>Por enquanto esse é o único e-mail válido para usar com o Article Kindle Converter: <strong><em>automation@trial-3zxk54vne31ljy6v.mlsender.net</em></strong></p><h4><strong>Recursos Adicionais</strong></h4><ul><li><strong>Código Aberto:</strong> O código do Article Kindle Converter está disponível no GitHub: <a href="https://github.com/jjeanjacques10/medium-kindle-sender">https://github.com/jjeanjacques10/medium-kindle-sender</a>. Você pode contribuir com o projeto ou sugerir melhorias.</li><li><strong>Suporte:</strong> Se precisar de ajuda ou tiver alguma dúvida, consulte a página “Como Usar” no site da ferramenta: <a href="https://medium-kindle-sender.onrender.com/how-to-send-to-kindle">https://medium-kindle-sender.onrender.com/how-to-send-to-kindle</a></li></ul><h4><strong>Conclusão</strong></h4><p>O Article Kindle Converter é uma ferramenta valiosa para quem gosta de ler artigos do Medium no Kindle. Com sua interface simples e intuitiva, você pode converter e enviar seus artigos favoritos para o seu dispositivo com facilidade.</p><p>Espero que goste e recomende para amigos!</p><p>Caso tenha alguma crítica, sugestão ou dúvida fique a vontade para me enviar uma mensagem:</p><p>LinkedIn: <a href="https://www.linkedin.com/in/jjean-jacques10/">https://www.linkedin.com/in/jjean-jacques10/</a></p><p>Até a próxima!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a212ddd83a08" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A habilidade mais importante para um desenvolvedor é a comunicação]]></title>
            <link>https://jjeanjacques10.medium.com/a-habilidade-mais-importante-para-um-desenvolvedor-%C3%A9-a-comunica%C3%A7%C3%A3o-14b4eb79070f?source=rss-9ed0e2046fe4------2</link>
            <guid isPermaLink="false">https://medium.com/p/14b4eb79070f</guid>
            <category><![CDATA[programação]]></category>
            <category><![CDATA[documentation]]></category>
            <category><![CDATA[tech-writer]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[software]]></category>
            <dc:creator><![CDATA[Jean Jacques Barros]]></dc:creator>
            <pubDate>Fri, 16 Jun 2023 17:14:38 GMT</pubDate>
            <atom:updated>2023-08-08T01:12:00.300Z</atom:updated>
            <content:encoded><![CDATA[<h4>Nem sempre escrever código vai resolver os maiores desafios na carreira de um desenvolvedor</h4><figure><img alt="Imagem de uma pessoal digitando no computador — capa do artigo" src="https://cdn-images-1.medium.com/max/1024/0*Co3JdAPnKh5uo8f9" /></figure><p>Podemos dizer que 90% dos problemas de uma empresa são causados pela falta de comunicação. Isso em nível de colegas de equipe e até entre áreas inteiras. Então, como podemos melhorar a forma como nos comunicamos e evitar dores de cabeça com problemas simples que acabam crescendo com o tempo? Neste artigo, proponho-me a trazer algumas dicas de como se expressar melhor, transmitir ideias de forma escrita e verbal.</p><p>Muito além das habilidades técnicas, um bom desenvolvedor precisa entender como compartilhar suas soluções e problemas com outras pessoas.</p><h3>O poder de um bom texto….</h3><p>Você já deve ter participado de uma reunião que poderia ter sido substituída por um e-mail… Frequentemente, ela ocorreu apenas porque acreditavam que seria “impossível” transmitir a ideia por escrito. Posso afirmar que a maioria delas realmente deveria ter sido um texto objetivo. No entanto, para alcançar esse patamar, é necessário compreender previamente como redigir textos de qualidade.</p><ol><li>Cuidado com erros de digitação</li><li>Conheça seu público algo</li><li>Faça as perguntas certas</li></ol><h4>Cuidado com erros de digitação</h4><p>O primeiro é o mais simples de todos: Evite equívocos de digitação ou de português, você não precisa redigir a redação do ENEM, nem ser formal constantemente. O dilema ocorre quando tais equívocos podem prejudicar a compreensão do leitor, portanto revise sempre que possível antes de clicar em “ENVIAR”. Alguns segundos adicionais não comprometerão sua oportunidade de compartilhar algo, mas substituir “significado” por “sinificado” pode gerar confusão para o leitor.</p><h4>Conheça seu público</h4><p>O segundo aspecto está intimamente relacionado à elaboração de palestras; um palestrante competente sempre pesquisa o público-alvo de seu conteúdo, a fim de aprimorar a forma como é apresentado. No texto, devemos ter a mesma preocupação; não é adequado chegar a uma reunião com profissionais de negócios discorrendo sobre conceitos altamente técnicos. Vejamos um exemplo:</p><figure><img alt="Olá, pessoal! Atualizei a lib do DynamoDB, e na tabela info_meios_pagamento não estamos mais utilizando GlobalSecondaryIndexs para consulta." src="https://cdn-images-1.medium.com/max/906/1*o4SSJCHmw7FLIrt-4p2aFQ.png" /><figcaption>Abordagem muito técnica trazendo informações desnecessárias para o contexto</figcaption></figure><p>A melhor forma de expressar isso seria abstraindo essas informações e focando no que é importante para esse público:</p><figure><img alt="Olá, pessoal! Realizei a atualização no aplicativo e com isso melhoramos a pesquisa de meios de pagamento, acredito que nossos clientes já consigam sentir a diferença." src="https://cdn-images-1.medium.com/max/911/1*cmlLBaNWUHadvqXbW5FR2A.png" /><figcaption>Exemplo de uma interação certa com pessoas sem conhecimento técnico mais aprofundado</figcaption></figure><h4>Faça as perguntas certas</h4><p>O último ponto é importante independente da senioridade, ele se baseia no “saber perguntar”, um dos sites mais famosos para desenvolvedores é o <a href="https://stackoverflow.com/">stackoverflow</a>, nele é crucial descrever de maneira precisa o problema a fim de buscar auxílio de programadores de todo o mundo. Essa prática também ocorre dentro das empresas, quando surge uma dúvida, é necessário enviar uma mensagem no Teams ou no Slack para alguém com maior experiência ou algum profissional de negócios.</p><p>Muitas vezes partimos do pressuposta que as outras pessoas tem o mesmo contexto que nós, que sabem todo o caminho que passamos para chegar até aquele ponto, mas nem sempre (grande parte das vezes) não é este o caso. Quando entrei na área, esse foi um dos melhores conselhos que recebi: “Mostre como você chegou a essa conclusão”. Segue mais um exemplo:</p><figure><img alt="Bruce Wayne: Bom dia, tudo bem? Ontem realizei alguns testes na aplicação, mas não consegui conectar ela ao banco de dados (RDS), estou fazendo a tarefa JIRA-X1999 (Link tarefa).
 
 Consegui configurar as variáveis de ambiente
 
 &lt;print das variáveis&gt;
 
 Atualizei as dependências também
 
 &lt;print do endpoint atualizado&gt;
 
 Tony: Você já tentou trocar as credenciais da AWS?
 
 Bruce Wayne: Deu certo, obrigado!" src="https://cdn-images-1.medium.com/max/970/1*xWnlw5p0RMvcpLqVSVwrfw.png" /><figcaption>Exemplo de uma conversa com mais contexto e fácil de ser compreendida por colegas de equipe</figcaption></figure><p>Costumo incluir também imagens, pois isso evita que a pessoa precise navegar por vários links para me auxiliar em algo simples. Caso seu colega possua alguma deficiência visual, adicione o texto, se possível, ou descreva o que anteriormente seria uma imagem. Quanto mais detalhes forem acrescentados, mais valor você está dando ao tempo de seus colegas de equipe.</p><h4>Don’t ask to ask, just ask</h4><p>Um site que encontrei por acaso e enfatiza bastante esse aspecto é o <a href="https://dontasktoask.com/">Don’t ask to ask, just ask</a>. Ele concentra-se nas interações que ocorrem principalmente em fóruns da internet, onde alguém chega com a seguinte pergunta:</p><figure><img alt="Tony: Algum expert em java aqui?" src="https://cdn-images-1.medium.com/max/917/1*jkE1TASL8po1egxXrG5cxA.png" /><figcaption>Exemplo de um péssimo pedido de ajuda em fórum, sem detalhamento</figcaption></figure><p>Como mencionado no tópico sobre formular as perguntas adequadas, é necessário sempre trazer o contexto do problema, preferencialmente descrevendo as medidas já tomadas para tentar solucioná-lo.</p><figure><img alt="tony: Como eu implemento uma função em Java para filtrar todos os meios de pagamento com saldo maior que 1.000?" src="https://cdn-images-1.medium.com/max/911/1*wkCnfebaYso-s10ZTNe-SQ.png" /><figcaption>Exemplo de uma boa abordagem em um fórum, pergunta direta e com detalhes</figcaption></figure><p>Ao adotar esse método, você obterá uma resposta mais rápida, pois a pessoa não precisará enviar uma nova mensagem perguntando sobre qual tipo de ajuda você precisa. Além de ajudar quem for pesquisar sobre este assunto futuramente, muitas vezes será você mesmo.</p><p>Aqui tem alguns <a href="https://stackoverflow.com/help/how-to-ask">exemplos do próprio stackoverflow</a> de como escrever bons questionamentos (tradução livre feita por mim):</p><blockquote><strong>Bad:</strong> C# Problema de matemática<br><strong>Good:</strong> Por que usar float em vez de int me dá resultados diferentes quando todas as minhas entradas são números inteiros?</blockquote><blockquote><strong>Bad:</strong> [php] dúvida sobre sessões<br><strong>Good:</strong> Como posso redirecionar usuários para páginas diferentes com base em dados de sessão em PHP?</blockquote><blockquote><strong>Bad:</strong> problemas com if-else no Android<br><strong>Good:</strong> Por que str == “valor” retorna falso quando str é definido como “valor”?</blockquote><h3>Iniciando uma conversa</h3><p>Outra questão bastante comum é como iniciar uma conversa. Provavelmente, você já recebeu uma mensagem no chat com um simples “Oi, tudo bem?” que permaneceu ali até que você respondesse com um “Oi” de volta. No entanto, essa troca pode levar tempo, enquanto novas prioridades surgem e você não tem ideia da urgência daquela saudação sem contexto. Não devemos deixar as pessoas em “espera” como em uma ligação telefônica. Seja direto, fornecendo todas as informações necessárias de forma objetiva junto à saudação.</p><p>Não faça isso:</p><figure><img alt="Bruce: Oi Alfred: Olá, tudo bem? Bruce: Como rodo o script na minha máquina? Alfred: sh open_batcaverna.sh" src="https://cdn-images-1.medium.com/max/1004/1*mevlzV8n-8m9wgQietPSyg.png" /><figcaption>Exemplo de uma abordagem lenta</figcaption></figure><p>Prefira uma abordagem direta:</p><figure><img alt="Bruce: Oi, como faço para rodar o script na minha máquina?
 Alfred: Olá! sh open_batcaverna.sh" src="https://cdn-images-1.medium.com/max/991/1*LH5jl7VU5wTsVI_2G71Hsg.png" /><figcaption>Exemplo de uma abordagem direta</figcaption></figure><h3>TechWriter</h3><p>Em nossa área temos diversas vertentes, uma delas é a de TechWriters, que são profissionais especializados em escrever e comunicar informações técnicas de maneira clara e compreensível. Eles criam documentação, manuais de usuário e guias de instruções, ajudando a traduzir conceitos complexos em linguagem acessível. Embora os desenvolvedores não precisem ser TechWriters, é importante que aprendam com esses profissionais a compartilhar informações, garantindo o sucesso e a usabilidade de produtos e de software.</p><p>Se você tiver a oportunidade de trabalhar com estes profissionais peça algumas dicas, com certeza eles lhe ensinarão algo novo, assim como você ensinará a eles.</p><h3>Inteligência artificial</h3><p>Uma ótima ferramenta para lhe ajudar a escrever textos melhores é a Inteligência artificial (IA). Essa tecnologia tem se mostrado cada vez mais promissora na hora de revisar textos. Através de algoritmos avançados, as ferramentas de IA podem identificar erros gramaticais, sugerir correções e até mesmo oferecer alternativas de palavras e frases para melhorar a clareza e a fluidez do texto.</p><p>Algumas das opções mais utilizadas atualmente incluem o <a href="https://chat.openai.com/">ChatGPT</a>, <a href="https://clarice.ai/">Clarice.ai</a>, <a href="https://www.copy.ai/">copy.ai</a> e várias outras.</p><blockquote>Se você pretende utilizar essas ferramentas em ambientes corporativos, certifique-se de não violar nenhuma política interna relacionada ao compartilhamento de informações.</blockquote><figure><img alt="Texto sendo melhorado no ChatGPT. Link para o texto: https://chat.openai.com/share/b04829d0-a1df-4579-a062-374ec135e99d" src="https://cdn-images-1.medium.com/max/554/1*Nwi5M-s5K-_iWfJtC7sU8w.png" /><figcaption>Exemplo de como melhorar um texto no ChatGPT</figcaption></figure><h3>Timidez, uma barreira para se expressar</h3><p>Aqui está outra vantagem de se expressar por texto. Sempre fui uma pessoa muito tímida e busquei por muito tempo não dar minha opinião em conversas no trabalho. Por isso, comecei a escrever. Na escrita, você se expõe menos e tem a chance de revisar mais de uma vez o que será enviado. Apenas tome cuidado para não ficar lapidando um texto até chegar na versão “perfeita”, porque ela não existe. É mais importante você ter uma versão direta com as informações mais importantes do que cobrir todos os cenários.</p><p>Além disso, a comunicação escrita também proporciona uma forma de registro e referência para futuras consultas. Ao expressar ideias por meio de textos, você cria um histórico que pode ser utilizado como base para tomadas de decisão, análises retrospectivas e aprendizados.</p><h3>Algumas dicas extras</h3><ul><li>Leia bastante, quem escreve bem é aquele que lê muito</li><li>Pegue feedback com seus colegas de trabalho</li><li>Não tenha medo de expor suas ideias</li></ul><h3>Conclusão</h3><p>Escrevi esse artigo para compartilhar com vocês ações que me auxiliaram muito em minha carreira, ainda estou aprendendo a me expressar melhor a transmitir minha ideias para as pessoas, isso tudo é um processo que só vai melhorar com muita prática, assim como programar!</p><p>Pratique realizando anotações sobre a tarefa que está fazendo, se possível documente seu projeto, vai ajudar você e será uma forma fácil de compartilhar com novas pessoas que entrarem em sua equipe (sem passar horas em reuniões de overview).</p><p>Espero que tenha gostado e sempre que ver alguém fazendo uma pergunta sem muito contexto compartilhe esse artigo com essa pessoa! Vamos melhorar a forma que nos comunicamos e criar um mundo com menos desentendimentos!</p><p>Caso tenha alguma crítica, dúvida ou sugestão, fique à vontade para comentar abaixo ou nos envie uma mensagem:</p><p><strong>Jean Jacques</strong>, Backend Software Engineer — <a href="https://www.linkedin.com/in/jjean-jacques10">https://www.linkedin.com/in/jjean-jacques10</a>/</p><p>Até a próxima!</p><h3>Referências</h3><ul><li><a href="https://dontasktoask.com/">https://dontasktoask.com/</a></li><li><a href="https://nohello.net/en/">https://nohello.net/en/</a></li><li><a href="http://catb.org/~esr/faqs/smart-questions.html">http://catb.org/~esr/faqs/smart-questions.html</a></li><li><a href="https://stackoverflow.com/help/how-to-ask">https://stackoverflow.com/help/how-to-ask</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=14b4eb79070f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Perspectivas futuras: A visão de um desenvolvedor sobre o avanço da Inteligência Artificial]]></title>
            <link>https://jjeanjacques10.medium.com/perspectivas-futuras-a-vis%C3%A3o-de-um-desenvolvedor-sobre-o-avan%C3%A7o-da-intelig%C3%AAncia-artificial-cc1973a85c65?source=rss-9ed0e2046fe4------2</link>
            <guid isPermaLink="false">https://medium.com/p/cc1973a85c65</guid>
            <category><![CDATA[inteligencia-artificial]]></category>
            <category><![CDATA[chatgpt]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[futurismo]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Jean Jacques Barros]]></dc:creator>
            <pubDate>Sat, 20 May 2023 21:10:59 GMT</pubDate>
            <atom:updated>2023-05-20T21:11:52.630Z</atom:updated>
            <content:encoded><![CDATA[<h4>Trago uma perspectiva do que imagino para o futuro com base nos meus estudos como entusiasta de IA</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mypLV8VS1zSStjxqrK3Yaw.png" /><figcaption>Imagem de um desenvolvedor gerada pela<a href="https://labs.openai.com/s/tNzkGgdVES57cxXGZ12x3naD"> DALL-E</a></figcaption></figure><p>Desde pequeno, ouço falar sobre Inteligência Artificial. Meu primeiro contato foi por meio dos filmes de ficção científica: “Eu, Robô” (2004) e “A.I. — Inteligência Artificial” (2001). Depois de vários anos, quando comecei a programar, o tema voltou à tona junto com Data Science e a “febre dos dados”, todos precisavam gerar dados para se manter no mercado e aproveitar os benefícios das IAs. Por volta de 2016, comecei a estudar mais a fundo e até considerei me tornar pesquisador nessa área. Hoje, sou apenas um entusiasta de IA e trabalho como desenvolvedor back-end Java.</p><p>Depois daquela primeira explosão em que todos falavam sobre Inteligência Artificial, o mercado meio que se acalmou. Agora, em 2023, estamos presenciando mais uma onda de pessoas se surpreendendo com a capacidade dos algoritmos que podem automatizar nossos trabalhos.</p><h4>Não é de hoje esse alarde para a perda de empregos</h4><p>Meu primeiro trabalho como programador foi criar uma página em WordPress para consumir uma API que retornava “As carreiras que vão desaparecer em 10 anos”, com base em uma pesquisa que dizia o seguinte:</p><blockquote>“Até 2026, 30 milhões de vagas com carteira assinada poderão ser fechadas à medida que robôs avançam e ganham espaço. É o que aponta estudo realizado pelo Laboratório de Aprendizado de Máquina em Finanças e Organizações (LAMFO) da UnB (Universidade de Brasília), que avaliou 2,6 mil ocupações brasileiras utilizando denominações de cargos e funções do antigo Ministério do Trabalho e Emprego. A opinião especializada de 69 acadêmicos e profissionais atuantes em aprendizado de máquinas foi levantada para embasar o cálculo das probabilidades de automação.” — <a href="https://itforum.com.br/noticias/robos-podem-substituir-54-dos-empregos-formais-do-brasil-preve-unb/">Robôs podem substituir 54% dos empregos formais do Brasil, prevê UNB — IT Forum</a></blockquote><p>Essa não é a única pesquisa que traz alertas como esses, mas um ponto interessante sobre a maioria dessas pesquisas é que foram realizadas antes da pandemia. Esse evento mudou de várias formas a maneira como as pessoas interagem entre si, e também a forma que consomem produtos, gerando necessidades de mercado que se alteraram significativamente desde então.</p><p>Esse foi um ponto que levantei durante a leitura do livro “<a href="https://www.amazon.com.br/Intelig%C3%AAncia-artificial-Kai-Fu-Lee/dp/6580634324">Inteligência artificial — Como os robôs estão mudando o mundo</a>”, escrito por Kai-Fu Lee. Trata-se de uma obra excelente e que recomendo. O livro foi lançado em 2019, antes dos impactos da pandemia, e aborda algumas das mudanças que já estavam surgindo naquela época, incluindo pesquisas que alertavam sobre a perda em massa de empregos para sistemas automatizados. Minha perspectiva sobre o assunto é baseada nessa obra e em outras que li ao longo dos anos. Portanto, irei discorrer sobre os impactos atuais e avançarei para possíveis cenários futuros…</p><h4>Ferramentas</h4><p>Atualmente venho utilizando bastante os modelos generativos, como o GitHub Copilot e o ChatGPT, para me dar um apoio no dia a dia de trabalho. Venho percebendo um aumento na minha produtividade por conta destas ferramentas, assim como as linguagens de programação, que mostram para os programadores outra forma de fazer suas tarefas.</p><p>Tivemos diversas revoluções com o tempo… Cada geração de desenvolvedores teve o seu apoio:</p><blockquote>Livros -&gt; Fóruns na internet -&gt; StackoverFlow -&gt; Copilot</blockquote><p>Apenas trocamos a página (ou aba, por assim dizer) de onde encontramos as repostas para nossas dúvidas. Não adianta correr disso!</p><p>Deixo aqui algumas sugestões de ferramentas que venho utilizando:</p><ul><li>GitHub Copilot: <a href="https://copilot.github.com/">https://copilot.github.com/</a></li><li>ChatGPT: <a href="https://chatbot.gpt.dobro.ai/">https://chatbot.gpt.dobro.ai/</a></li><li>Phind.ai: <a href="https://phind.ai/">https://phind.ai/</a></li></ul><h4>Vamos perder nossos empregos?</h4><p>Quanto à possibilidade do cargo de desenvolvedor de software desaparecer, eu adotaria uma abordagem mais realista: as IAs não eliminam “empregos”, mas sim funções específicas. E quais são as principais funções de um desenvolvedor?</p><ul><li>Análise de requisitos</li><li>Alinhar expectativas com os clientes</li><li>Design e arquitetura de software</li><li>Codificação</li><li>Documentação</li><li>Trabalho em equipe</li><li>Manutenção e suporte</li></ul><p>Embora existam muitas outras funções, com base nas mencionadas acima, quais você acredita que um robô seria capaz de realizar? A resposta é: todas as tarefas repetitivas e tediosas, mas aquelas que envolvem empatia ainda estão longe de serem realizadas por IAs especialistas. Talvez nossos cargos mudem novamente (como já aconteceu várias vezes desde o surgimento do <em>WebMaster</em> haha) e tenhamos que aprender novas habilidades para nos mantermos no mercado de trabalho. No entanto, isso não deve ser assustador, especialmente para aqueles que trabalham com tecnologia, pois já deveriam estar acostumados com mudanças e disrupções.</p><h4>Para quem está começando na área…</h4><p>Se você está buscando entrar agora no mercado de trabalho como desenvolvedor, não há muita diferença em relação ao que estava acontecendo quando eu me candidatei para minha primeira vaga. Foque em resolver os problemas atuais com as ferramentas disponíveis, mas mantenha-se atualizado sobre as novidades que surgem e tente aplicá-las no seu cotidiano, isso sempre será visto como um diferencial e irá ajudar você a criar uma mentalidade flexível.</p><blockquote>“Ser um(a) programador(a) vai além de escrever código; é ser capaz de trazer soluções inovadoras para os clientes, utilizando ferramentas e aplicando um pensamento sistêmico”</blockquote><h3>Perspectivas de futuro</h3><h4>Curto prazo</h4><p>Existem dois tipos de IA: especialistas e generalistas. As especialistas são treinadas para realizar tarefas específicas, como reconhecimento de imagens e de voz. As generalistas têm a capacidade de desempenhar diversas tarefas, embora ainda não tenhamos uma IA de propósito geral que possa resolver todos os nossos problemas. Nesse sentido, é importante estudar os impactos que teremos na indústria, no mercado e no meio acadêmico, visando desenvolver um plano de ação bem estruturado. Esse processo requer o envolvimento de diversas áreas, como Filosofia, Sociologia, Psicologia e, claro, a própria Tecnologia.</p><p>Na minha visão, nos próximos anos veremos uma combinação do trabalho humano com o trabalho realizado pelas máquinas, o que já ocorre no meu dia a dia como desenvolvedor. Ambos irão se complementar. A Inteligência Artificial assumirá as tarefas pesadas, repetitivas e não criativas, enquanto os seres humanos se dedicarão ao trabalho criativo. Aqueles que não estão na área de tecnologia precisarão adotar a mesma mentalidade de adaptação, abandonando velhos hábitos para se manterem produtivos em um mundo onde essa habilidade será cada vez mais valorizada.</p><p>Daqui para frente, existem 2 cenários possíveis:</p><ol><li>Vamos perder empregos para a automatização e o mundo vai entrar em colapso</li><li>Vamos perder empregos para a automatização e o mundo vai entrar em uma nova era de prosperidade</li></ol><h4>Longo Prazo</h4><p>Para o longo prazo, prevejo muitas funções desaparecendo e a necessidade de profissionalização. Por exemplo, no caso do pacote Office: antes esse software ajudava nas tarefas que já eram realizadas, mas agora vamos precisar que as pessoas aprendam novas profissões e muitas delas ainda nem existem. Com o tempo, departamentos inteiros de empresas vão desaparecer, e precisaremos de uma coordenação entre os governos e as empresas privadas para entender como lidar com pessoas que não serão mais necessárias no mercado de trabalho. Não haverão tantos empregos como hoje, sendo que até no cenário atual faltam oportunidades de trabalho.</p><p>Atualmente, as empresas privadas, principalmente aquelas sediadas na China e nos Estados Unidos, são líderes na criação e posse dos modelos de IA, o que direciona os lucros para essas nações. Essa realidade gera uma preocupante desigualdade social, que já é evidente hoje, mas que tende a se agravar caso os governos e empresas não atuem de forma global para mitigar esse cenário. Esse é um dos pontos levantados por Kai-Fu Lee em seu livro, sobre como a substituição dos empregos irá atingir os profissionais com formação superior e teremos desemprego generalizado, mesmo com pessoas migrando de uma área para a outra, não haverá mercado o suficiente para tantos profissionais. A partir daí, precisaremos pensar em formas de distribuir essa renda que se contra em poucos países e empresas. Caso contrário, teremos um caos social.</p><p><em>Deixando claro que essas são perspectivas para um futuro distante, não acredito estar vivo para ver essa mudança drástica no modelo econômico mundial.</em></p><h3>Conclusão</h3><p>Como mencionei, esses modelos inteligentes têm me ajudado há algum tempo e vejo que estão se popularizando cada vez mais. Acredito que daqui para frente a área que mais precisaremos investir seja a educação, pois nenhuma geração vivenciou, em um curto espaço de tempo, a quantidade massiva de mudanças que está por vir. Precisamos aprender tanto quanto os modelos artificiais que estamos ensinando.</p><p><em>Estou animado para acompanhar os próximos passos que daremos em direção à uma sociedade que cresce junto com o desenvolvimento de novas tecnologias potencializadas pela Inteligência Artificial. Tenho, sim, minhas preocupações, principalmente em relação aos pontos que mencionei sobre desigualdade, mas acredito que podemos encontrar uma solução como sociedade para esses problemas!</em></p><p>Caso tenha alguma crítica, dúvida ou sugestão, fique à vontade para comentar abaixo ou nos envie uma mensagem:</p><p><strong>Jean Jacques</strong>, Backend Software Engineer — <a href="https://www.linkedin.com/in/jjean-jacques10">https://www.linkedin.com/in/jjean-jacques10</a>/</p><p><a href="https://medium.com/u/1496dd11bfee"><strong>Gabriel Petillo</strong></a>, Product Owner — <a href="https://www.linkedin.com/in/gabrielpetillo/">https://www.linkedin.com/in/gabrielpetillo/</a></p><p>Até a próxima!</p><h3>Referências</h3><ul><li><a href="https://globolivros.globo.com/livros/inteligencia-artificial">Inteligência artificial — Kai-Fu Lee</a></li><li><a href="https://itforum.com.br/noticias/robos-podem-substituir-54-dos-empregos-formais-do-brasil-preve-unb/">Robôs podem substituir 54% dos empregos formais do Brasil, prevê UNB — IT Forum</a></li><li><a href="https://medium.com/nerdzao/introdu%C3%A7%C3%A3o-a-intelig%C3%AAncia-artificial-cf11f274d483">Introdução a inteligência artificial | by Jean Jacques Barros | Medium</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cc1973a85c65" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como usar e criar Annotations em Kotlin — Spring Framework]]></title>
            <link>https://jjeanjacques10.medium.com/como-usar-e-criar-annotations-em-kotlin-spring-framework-7bec90ea2ff2?source=rss-9ed0e2046fe4------2</link>
            <guid isPermaLink="false">https://medium.com/p/7bec90ea2ff2</guid>
            <category><![CDATA[spring-boot]]></category>
            <category><![CDATA[spring]]></category>
            <category><![CDATA[kotlin]]></category>
            <category><![CDATA[annotations]]></category>
            <category><![CDATA[api]]></category>
            <dc:creator><![CDATA[Jean Jacques Barros]]></dc:creator>
            <pubDate>Fri, 03 Mar 2023 13:23:08 GMT</pubDate>
            <atom:updated>2023-03-03T13:23:08.734Z</atom:updated>
            <content:encoded><![CDATA[<h3>Como usar e criar Annotations em Kotlin — Spring Framework</h3><h4><strong>Este é um guia que crie para auxiliar na criação de annotations customizadas no Spring Framework utilizando Kotlin</strong></h4><figure><img alt="Capa do artigo" src="https://cdn-images-1.medium.com/max/1024/1*GoPDZTrXSQHGkD1rXmXzZw.png" /></figure><p>Trabalhando com Spring, uma das primeiras coisas que se nota é a quantidade de annotations que precisamos utilizar. Sendo que elas têm a finalidade de fornecer informações adicionais tanto para o compilador quanto para o Spring Framework, indicando como lidar com determinadas classes, métodos ou propriedades.</p><p>Como exemplo temos o @Service, @Controller, @Component e dentre várias outras comumente utilizadas. Mesmo com um grande número de anotações disponíveis por padrão pode ser interessante criar a sua própria, nesse artigo vou explicar um pouco sobre como criar e utilizar anotações em Kotlin.</p><p><strong>Repositório projeto de exemplo:</strong></p><blockquote><em>Todos esses exemplos podem ser encontrados no seguinte repositório no GitHub: </em><a href="https://github.com/jjeanjacques10/annotations-kotlin-spring-boot">https://github.com/jjeanjacques10/annotations-kotlin-spring-boot</a></blockquote><h3><strong>Pondo a mão na massa 👨🏻‍💻</strong></h3><p>Para isso vamos criar uma nova aplicação Spring Boot para realizar o cadastro de livros. Vamos começar adicionando o seguinte controller, observe que estou utilizando o @Valid, essa annotation é utilizada para validar os dados de entrada de uma requisição, sem ela as próximas configurações na classe <em>BookRequest </em>não funcionarão.</p><pre>import javax.validation.Valid<br><br>@RestController<br>class BookController {<br>  @PostMapping(&quot;/book&quot;)<br>    fun create(@Valid @RequestBody book: BookRequest): Map&lt;String, Any?&gt; {<br>        return mapOf(<br>            &quot;status&quot; to 200,<br>            &quot;details&quot; to &quot;Book created - &quot;.plus(book.title)<br>        )<br>    }<br>}</pre><h4><strong>Utilizando anotações de validação do Spring</strong></h4><p>Um dos usos mais comuns quando trabalhamos com API’s Rest é validar os dados de entrada enviados pelo usuário. Para isso podemos importar a lib <em>javax.validation.constraints</em>, nela temos várias anotações que podem ser utilizadas para validar os dados de entrada. A classe BookRequest representa os dados de entrada que serão enviados pelo usuário.</p><pre>import javax.validation.constraints.NotBlank<br><br>data class BookRequest(<br>    @field:NotBlank<br>    val title: String? = &quot;&quot;<br>)</pre><p>Nela temos o atributo title que possui acima dele o <strong>@NotBlank</strong>, essa annotation indica que o atributo não pode ser nulo e nem vazio. Temos diversas possibilidades quando utilizamos essa lib de validação, seguem as principais:</p><ul><li><strong>@NotNull</strong> — Indica que o atributo não pode ser nulo</li><li><strong>@NotEmpty</strong> — Indica que o atributo não pode ser nulo ou vazio</li><li><strong>@NotBlank </strong>— Indica que o atributo não pode ser nulo ou vazio e não pode conter apenas espaços em branco</li><li><strong>@Size(min = 1, max = 10)</strong> — Indica que o atributo deve ter no mínimo 1 e no máximo 10 caracteres</li><li><strong>@Email</strong> — Indica que o atributo deve ser um email válido</li><li><strong>@Pattern(regexp = “^[a-zA-Z0–9]*$”) </strong>— Indica que o atributo deve seguir o padrão regex informado</li></ul><blockquote><strong><em>❗ Importante:</em></strong><em> Passei um tempo quebrando a cabeça por não saber que no caso de validações em Kotlin precisamos utilizar o </em><strong><em>@field:&lt;annotation&gt;</em></strong><em> para que a anotação seja aplicada ao atributo</em></blockquote><h3><strong>Por baixo dos panos 🕵🏼‍♂️</strong></h3><p>Vamos olhar um pouco mais fundo para entender como que essa flag com @ na frente funciona. Quando utilizamos uma annotation em Kotlin estamos utilizando uma feature chamada <a href="https://kotlinlang.org/docs/reference/reflection.html"><strong>reflection</strong></a>, que basicamente permite que o código acesse informações sobre si mesmo em tempo de execução. Com isso podemos acessar informações sobre classes, métodos, atributos e etc.</p><p>Se entrarmos em uma das anotações do Spring, como por exemplo a <strong>@NoEmpty</strong>, podemos ver que ela é uma interface que herda de <strong>@Constraint</strong>, <strong>@Target </strong>e <strong>@Retention</strong>.</p><pre>@Constraint(validatedBy = {})<br>@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})<br>@Retention(RetentionPolicy.RUNTIME)<br>public @interface NotEmpty {<br>    ...<br>}</pre><blockquote><em>Existem mais anotações que compõe NotEmpty, mas para fins didáticos não vou entrar em detalhes.</em></blockquote><p><strong>@Target</strong> especifica os tipos possíveis de elementos que podem ser anotados. Os valores possíveis são:</p><ul><li>CLASS: Pode ser aplicada em classes, interfaces e enums.</li><li>FUNCTION: Pode ser aplicada em funções e construtores.</li><li>PROPERTY: Pode ser aplicada em propriedades de classes.</li><li>FIELD: Pode ser aplicada em campos de classes.</li></ul><p><strong>@Retention</strong> especifica se a annotation é armazenada nos arquivos de classe compilados e se ela é visível por meio de reflection em tempo de execução (por padrão, ambas são verdadeiras). Existem três possíveis valores para essa propriedade:</p><ul><li>SOURCE: É descartada pelo compilador e não está presente no bytecode gerado.</li><li>CLASS: É mantida no bytecode, mas não está disponível em tempo de execução.</li><li>RUNTIME: É mantida no bytecode e também está disponível em tempo de execução.</li></ul><p>Esse é o básico de como funcionam as configurações das principais annotations no Spring. Vamos agora ver como podemos criar nossas próprias aplicando esses conceitos.</p><h3><strong>Criando uma annotation customizada</strong></h3><p>Mas não apenas do padrão vive o dev, às vezes nossos projetos pedem por comportamentos específicos que o framework não está apto a fornecer. Nesse caso podemos criar nossas próprias annotations, vamos então adicionar a validação se o gênero do livro é válidos.</p><p>Como comentado anteriormente, precisamos ter o @Target, que nesse caso será FIELD, pois queremos que apenas seja utilizada em atributos de classes. Também precisamos do @Retention, sendo ele definido como RUNTIME, já que esse tipo de validação é feita em tempo de execução. E por fim, a mais importante, a annotation @Constraint, nela que definimos a regra de validação que será utilizada.</p><pre>import javax.validation.Constraint<br>import javax.validation.Payload<br>import kotlin.reflect.KClass<br><br>@MustBeDocumented<br>@Target(AnnotationTarget.FIELD)<br>@Retention(AnnotationRetention.RUNTIME)<br>@Constraint(validatedBy = [GenreValidator::class])<br>annotation class Genre(<br>    val message: String = &quot;O gênero do livro é inválido&quot;,<br>    val groups: Array&lt;KClass&lt;*&gt;&gt; = [],<br>    val payload: Array&lt;KClass&lt;out Payload&gt;&gt; = []<br>)</pre><p>Definimos na annotation @Genre três parâmetros: message, groups e payload. O primeiro é a mensagem de erro que será exibida caso a validação falhe, o segundo é um array de classes que podem ser utilizadas para agrupar validações e o terceiro também um array de classes que podem ser utilizadas para definir payloads customizados. Vamos preencher apenas a mensagem de erro, pois os outros parâmetros não serão utilizados nesse exemplo.</p><pre>enum class GenreType {<br>    FANTASY,<br>    SCI_FI,<br>    ROMANCE,<br>    MYSTERY,<br>    HORROR,<br>    THRILLER,<br>    NON_FICTION<br>}<br><br>class GenreValidator : ConstraintValidator&lt;Genre, String&gt; {<br>    override fun isValid(value: String?, context: ConstraintValidatorContext?): Boolean {<br>        if (value == null) false<br>        return try {<br>            GenreType.valueOf(value!!)<br>            true<br>        } catch (e: IllegalArgumentException) {<br>            false<br>        }<br>    }<br>}</pre><p>A lógica da constraint se encontra na classe GenreValidator, nela apenas verificamos se o valor do atributo está presente na lista de GenreType, caso não esteja ele retornará que esse valor é inválido. Agora vamos aplicar essa annotation na classe BookRequest.</p><pre>data class BookRequest(<br>    @field:Genre<br>    val genre: String<br>)</pre><h4><strong>Testando a annotation customizada</strong></h4><p>Agora com nossas annotations criadas vamos testar se elas estão funcionando corretamente. Quando realizamos uma chamada na API com um gênero inválido o retorno será 422 Unprocessable Entity.</p><figure><img alt="{
 “code”: 422,
 “details”: [
 “O gênero do livro é inválido”
 ]
 }" src="https://cdn-images-1.medium.com/max/1024/1*TN9yDQbEli7ro_kX1Ipszw.png" /><figcaption>Erro retornado pela API por conta do campo gênero ter sido enviado errado</figcaption></figure><p>Também podemos retornar uma lista com diversos erros, como por exemplo, quando o título do livro é nulo e o gênero é inválido:</p><figure><img alt="Mensagem de erro: {  “code”: 422,  “details”: [  “Título não deve estar vazio”,  “O gênero do livro é inválido”,  “Descrição não deve estar vazia”  ]  }" src="https://cdn-images-1.medium.com/max/1024/1*Tt_PfgNsEf5OGOxiPehBbg.png" /><figcaption>Error retornado pela API por campos enviados serem inválidos</figcaption></figure><p>Ao realizar a chamada na API com valores válidos o retorno será 200 OK, mostrando que o livro foi criado com sucesso.</p><figure><img alt="{
 “status”: 200,
 “details”: “Book created — Sapiens: Uma breve história da humanidade”
 }" src="https://cdn-images-1.medium.com/max/1024/1*Z8Y17decrwwsNwbBRMeFeQ.png" /><figcaption>Mensagem de sucesso quando o livro foi cadastrado</figcaption></figure><h3>Conclusão</h3><p>Passei um bom tempo estudando sobre annotations no Spring para poder utilizar em um projeto, esse conteúdo é um resumo de tudo que pesquisei e consegui aplicar, espero que tenha lhe ajudado em sua jornada.</p><p>Caso tenha alguma crítica, sugestão ou dúvida fique a vontade para me enviar uma mensagem:</p><p>Linkedin: <a href="https://www.linkedin.com/in/jjean-jacques10/">https://www.linkedin.com/in/jjean-jacques10/</a></p><p>Até a próxima!</p><h3>Referências</h3><p>Doc Kotlin Annotations &lt;<a href="https://kotlinlang.org/docs/annotations.html">https://kotlinlang.org/docs/annotations.html</a>&gt;</p><p>Kotlin Spring validation not working &lt;<a href="https://youtrack.jetbrains.com/issue/KT-50511/Kotlin-Spring-validation-not-working">https://youtrack.jetbrains.com/issue/KT-50511/Kotlin-Spring-validation-not-working</a>&gt;</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7bec90ea2ff2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Estratégias utilizando Application Auto Scaling com ECS]]></title>
            <link>https://jjeanjacques10.medium.com/estrat%C3%A9gias-utilizando-application-auto-scaling-com-ecs-129b6ed25062?source=rss-9ed0e2046fe4------2</link>
            <guid isPermaLink="false">https://medium.com/p/129b6ed25062</guid>
            <category><![CDATA[elastic-container-service]]></category>
            <category><![CDATA[aws]]></category>
            <category><![CDATA[autoscaling]]></category>
            <category><![CDATA[cloud-computing]]></category>
            <category><![CDATA[microservices]]></category>
            <dc:creator><![CDATA[Jean Jacques Barros]]></dc:creator>
            <pubDate>Mon, 05 Dec 2022 13:40:18 GMT</pubDate>
            <atom:updated>2026-04-17T01:12:02.728Z</atom:updated>
            <content:encoded><![CDATA[<h4>Como criar regras personalizadas com o Application Auto Scaling em aplicações AWS ECS utilizando CloudFormation</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1Ux0xC4LcxvjoWn8H3_xjg.png" /></figure><p>Uma das grandes vantagens de utilizarmos a AWS ou qualquer outro provedor de Cloud é a possibilidade de pagar apenas pelos recursos que utilizamos. Uma ferramenta extremamente útil para gerenciar estes recursos de forma eficiente, evitando gastos desnecessários, é o Auto Scaling ou dimensionamento automático. Esta funcionalidade permite aumentar ou diminuir de forma dinâmica a quantidade desejada de tasks (máquinas rodando) em serviços como o Amazon Elastic Computer Service (ECS). Dessa forma, garantimos que a quantidade de <em>tasks </em>esteja adequada às necessidades da aplicação, optimizando custos e também mantendo nosso serviço disponível mesmo com o aumento de demanda.</p><p>O Amazon ECS oferece diversas vantagens na hora de configurar estratégias de escalonamento. Podemos personalizar essas configurações de acordo com o tipo de serviço, como por exemplo aplicações de e-commerce no período de Black Friday ou aquelas com poucos acessos durante a noite, cenários que envolvem uma carga de trabalho variável.</p><p>Hoje vou apresentar algumas estratégias que podem ser úteis em seus projetos visando o escalonamento horizontal, replicando as máquinas ao invés de aumentar o tamanho delas.</p><blockquote>Lembrando que todos os códigos apresentados nesse artigo podem ser encontrados no seguinte repositório do GitHub: <a href="https://github.com/jjeanjacques10/springboot-ecs-fargate">https://github.com/jjeanjacques10/springboot-ecs-fargate</a></blockquote><h4>Como funciona o Application Auto Scaling</h4><p>O AWS <a href="https://docs.aws.amazon.com/autoscaling/application/userguide/what-is-application-auto-scaling.html"><strong>Application Auto Scaling</strong></a> é um serviço que permite escalar automaticamente seus recursos Amazon EC2, Amazon ECS, Amazon EKS, Amazon DynamoDB e Amazon Aurora em resposta à demanda de aplicativos em constante mudança.</p><figure><img alt="Fluxo de provisionamento de tasks em ECS" src="https://cdn-images-1.medium.com/max/1024/1*2zfqy_udbcKcGrA6JvwHsQ.jpeg" /><figcaption>Fluxo de provisionamento de tasks em ECS</figcaption></figure><p>No exemplo acima temos o fluxo de Auto Scaling, para definir o recurso a ser dimensionado criamos um <em>Scalable Target</em>, que especifica a capacidade mínima e máxima desse recurso e também as políticas de dimensionamento dele, que determinam como o recurso aumenta ou diminui em resposta a mudanças na demanda. O serviço ECS (Digimon Service) está se comunicando o tempo inteiro com o CloudWatch e enviando métricas para os alarmes presentes nele que serão gerenciados pelo Application Auto Scaling e tem as regras definidas no Scaling Policy.</p><pre>ScalableTarget:<br>  Type: AWS::ApplicationAutoScaling::ScalableTarget<br>  DependsOn: Service<br>  Properties:<br>    MinCapacity: 2<br>    MaxCapacity: 6<br>    ResourceId: !Join<br>      - &quot;/&quot;<br>      - - service<br>        - !Ref EcsClusterName<br>        - !GetAtt Service.Name<br>    RoleARN: !GetAtt AutoScalingRole.Arn<br>    ScalableDimension: ecs:service:DesiredCount<br>    ServiceNamespace: ecs</pre><p>Agora para especificar como e quando escalar precisamos do <em>Scaling Policy</em>, vamos seguir então para alguns exemplos, começando pelo mais comum de encontrar em aplicações…</p><h4>Uso de CPU ou Memória</h4><p>Esse tipo de métrica utilizada no <em>Scaling Policy</em> é comum porque utiliza características de nossos serviços que dizem muito sobre o status atual da aplicação a nível de hardware. Nestes casos devemos definir um valor alvo a ser atingido (<em>TargetValue</em>) em qualquer uma das duas métricas, CPU ou memória RAM, e quando o valor é alcançado, como por exemplo 30% de uso da CPU, temos o aumento no número de tarefas rodando até o limite pré-estabelecido no <em>Scalable Target.</em> No caso do exemplo abaixo são no máximo 6 tasks e no mínimo 2 tasks.</p><figure><img alt="Visão do console AWS para o AutoScaling de 30% de uso da CPU" src="https://cdn-images-1.medium.com/max/813/1*cG-OfNiYR25d5ZSK-LlLMw.png" /><figcaption>Visão do console AWS para o AutoScaling de 30% de uso da CPU</figcaption></figure><pre>ScalingPolicy:<br>  Type: AWS::ApplicationAutoScaling::ScalingPolicy<br>  Properties:<br>    PolicyName: ScaleWithCPUUtilization<br>    PolicyType: TargetTrackingScaling<br>    ScalingTargetId: !Ref ScalableTarget<br>    TargetTrackingScalingPolicyConfiguration:<br>      PredefinedMetricSpecification:<br>        PredefinedMetricType: ECSServiceAverageCPUUtilization<br>      TargetValue: 30.0</pre><p>Para trocar para o verificar 30% do uso de memória alteramos o <strong><em>PredefinedMetricType </em></strong>para “ECSServiceAverageMemoryUtilization”.</p><p>Esse tipo de métrica pode ser utilizado para qualquer serviço, mas existem alguns cenários que outros tipos de alarmes podem ser mais interessantes. Por exemplo, podemos aumentar o número de tarefas observando serviços externos como filas SQS e utilizar a métrica de…</p><h4>Quantidade de mensagens na fila</h4><p>Quando trabalhamos com consumers a principal métrica utilizada é a quantidade de itens a serem processados, para isso podemos olhar para um SQS na AWS e de acordo com o aumento no número de mensagens antigas em uma fila disponibilizar mais <em>tasks </em>ou remover essa capacidade extra caso não tenha nada para ser processado.</p><p>Pondo a mão na massa vamos então vamos criar nossas <em>Policies. </em>Um ponto importante de saber é que podemos ter várias <em>Policies</em>, mas cada <em>policy</em> só pode ser dimensionada em uma única direção, por essa razão precisamos de duas neste caso.</p><pre>ScalingPolicySQSUp:<br>  Type: AWS::ApplicationAutoScaling::ScalingPolicy<br>  Properties:<br>    PolicyName: ScaleWithSQSUp<br>    PolicyType: StepScaling<br>    ScalingTargetId: !Ref ScalableTarget<br>    StepScalingPolicyConfiguration:<br>      AdjustmentType: ChangeInCapacity<br>      Cooldown: 60<br>      MetricAggregationType: Average<br>      StepAdjustments:<br>        - MetricIntervalLowerBound: 0<br>           MetricIntervalUpperBound: 100<br>           ScalingAdjustment: 1<br>        - MetricIntervalLowerBound: 100<br>           ScalingAdjustment: 2</pre><p>A primeira é referente ao <em>UpScaling</em>, nela será adicionado 1 contêiner quando a fila estiver entre 1000 e 1100, mas adicionará 2 quando houverem mais itens. Agora para diminuir o número de tasks quando cair a demanda criamos outra Policy com valores negativos, nesse caso quando estiver entre 900 e 999 itens será removida 1 task.</p><pre>ScalingPolicySQSDown:<br>  Type: AWS::ApplicationAutoScaling::ScalingPolicy<br>  Properties:<br>    PolicyName: ScaleWithSQSDown<br>    PolicyType: StepScaling<br>    ScalingTargetId: !Ref ScalableTarget<br>    StepScalingPolicyConfiguration:<br>      AdjustmentType: ChangeInCapacity<br>      Cooldown: 300<br>      MetricAggregationType: Average<br>      StepAdjustments:<br>        - MetricIntervalUpperBound: 0<br>          ScalingAdjustment: -1</pre><p>Criadas as ações vamos agora configurar os alarmes no CloudWatch do tipo ApproximateNumberOfMessagesVisible. Vamos precisa de dois alarmes o primeiro é para que quando a fila “digimon-queue” atingir 1000 mensagens, nesse momento a Action de “ScalingPolicySQSUp” será ativada para aumentar a capacidade. O segundo é para nos alertar de quando esse valor diminuir e assim sermos capazes de diminuir o número de tasks ativando a Action “ScalingPolicySQSDown”.</p><pre># Use SQS Queue to scale up<br>CloudWatchSQSAlarmScalingUp:<br>  Type: &quot;AWS::CloudWatch::Alarm&quot;<br>  Properties:<br>    AlarmName: &quot;Alarm SQS Up&quot;<br>    AlarmDescription: &quot;Alarm if our SQS queue is higher than usual.&quot;<br>    Namespace: AWS/SQS<br>    EvaluationPeriods: 2<br>    Statistic: Average<br>    MetricName: ApproximateNumberOfMessagesVisible<br>    ComparisonOperator: GreaterThanThreshold<br>    Period: 60<br>    Dimensions:<br>      - Name: QueueName<br>        Value: digimon-queue<br>    Threshold: 1000<br>    AlarmActions:<br>      - !Ref ScalingPolicySQSUp<br><br># Use SQS Queue to scale down<br>CloudWatchSQSAlarmScalingDown:<br>    Type: AWS::CloudWatch::Alarm<br>    Properties:<br>      AlarmName: &quot;Alarm SQS Down&quot;<br>      AlarmDescription: &quot;Alarm if our SQS queue is smaller than usual.&quot;<br>      Namespace: AWS/SQS<br>      EvaluationPeriods: 1<br>      Statistic: Average<br>      MetricName: ApproximateNumberOfMessagesVisible<br>      Threshold: !Ref SqsMessagesVisibleThresholdScaleDown<br>      ComparisonOperator: LessThanOrEqualToThreshold<br>      Period: 60<br>      Dimensions:<br>        - Name: QueueName<br>          Value: digimon-queue<br>      Threshold: 1000<br>      AlarmActions:<br>      - !Ref ScalingPolicySQSDown</pre><h4>Quantidade de chamadas</h4><p>Quando se trabalha com API’s uma ótima métrica para utilizar é a de quantidade de chamadas recebidas, principalmente nos cenários onde já temos a quantidade de transações por segundo (TPS) mapeada. A criação das Scaling Policies é muito parecido com o de itens na fila, a maior diferença é o Alarme no CloudWatch que será utilizado aqui.</p><pre>ScalingPolicyTCPUp:<br>  Type: AWS::ApplicationAutoScaling::ScalingPolicy<br>  Properties:<br>    PolicyName: ScaleWithFlowCountTCPStepPolicyUp<br>    PolicyType: StepScaling<br>    ScalingTargetId: !Ref ScalableTarget<br>    StepScalingPolicyConfiguration:<br>      AdjustmentType: ChangeInCapacity<br>      Cooldown: 60<br>      MetricAggregationType: Average<br>      StepAdjustments:<br>        - MetricIntervalLowerBound: 0<br>          ScalingAdjustment: +1<br><br>ScalingPolicyTCPDown:<br>  Type: AWS::ApplicationAutoScaling::ScalingPolicy<br>  Properties:<br>    PolicyName: ScaleWithFlowCountTCPStepPolicyDown<br>    PolicyType: StepScaling<br>    ScalingTargetId: !Ref ScalableTarget<br>    StepScalingPolicyConfiguration:<br>      AdjustmentType: ChangeInCapacity<br>      Cooldown: 60<br>      MetricAggregationType: Average<br>      StepAdjustments:<br>        - MetricIntervalUpperBound: 0<br>          ScalingAdjustment: -1</pre><p>Agora os alarmes que temos que criar são usados para monitorar as requisições recebidas e acionar um Grupo de Auto Scaling para aumentar ou diminuir com base no número de solicitações. O alarme compara o número de solicitações TCP nos últimos <strong>60 segundos</strong> com o <strong>limite de 100 solicitações</strong>. Se o número de solicitações for maior que o limite, o alarme acionará <em>ScalingPolicyTCPUp </em>e caso seja menor acionará o <em>ScalingPolicyTCPDown </em>para dimensionar o Grupo de Auto Scaling de acordo com a regra pré-definida.</p><pre># Use Active flow count to scale up <br>HighTpsAlarm:<br>  Type: AWS::CloudWatch::Alarm<br>  Properties:<br>    AlarmDescription: !Sub &#39;${MicroServiceName} - High volume of requests&#39;<br>    EvaluationPeriods: 1<br>    Threshold: 100<br>    ComparisonOperator: GreaterThanOrEqualToThreshold<br>    Period: 60<br>    Statistic: Sum<br>    MetricName: ActiveFlowCount<br>    Dimensions:<br>      - Name: LoadBalancer<br>        Value: !GetAtt LoadBalancer.LoadBalancerFullName<br>    Namespace: AWS/NetworkELB<br>    AlarmActions:<br>      - !Ref ScalingPolicyTCPUp<br><br># Use Active flow count to scale down<br>LowTpsAlarm:<br>  Type: AWS::CloudWatch::Alarm<br>  Properties:<br>    AlarmDescription: !Sub &#39;${MicroServiceName} - Low volume of requests&#39;<br>    EvaluationPeriods: 1<br>    Threshold: 90<br>    ComparisonOperator: LessThanOrEqualToThreshold<br>    Period: 60<br>    Statistic: Sum<br>    MetricName: ActiveFlowCount<br>    Dimensions:<br>      - Name: LoadBalancer<br>        Value: !GetAtt LoadBalancer.LoadBalancerFullName<br>    Namespace: AWS/NetworkELB<br>    AlarmActions:<br>      - !Ref ScalingPolicyTCPDown</pre><p>No momento em que o alarme é acionado, temos a ação sendo ativada, como demonstrado no exemplo abaixo, onde podemos observar o número de tarefas aumentando após diversas requisições.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*g4U1qzsMQWRoO3AUsPlmtw.png" /><figcaption>Visão do CloudWatch com alarm de HithTps sendo ativado.</figcaption></figure><p>Para verificar se essa configuração está certa podemos acessar nosso serviço ECS e entrar na aba “Auto Scaling”. Nela são apresenados detalhes da nossa configuração, como as regras de escalonamento e a política de subida e descida. Se a configuração estiver correta, esses parâmetros serão exibidos de acordo com as configurações feitas.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*srba99N897RsmTakN0rpWg.png" /><figcaption>Visão do console AWS com as configurações na aba de Auto Scaling</figcaption></figure><h4>Horário (CRON)</h4><p>Também é possível gerencia a quantidade de tarefas rodando por meio de Schedules que identificam o horário definido para aumentar ou diminuir o número de tasks.</p><p>Para definir qual horário nossa ação deve rodar podemos utilizar o CRON, que é um utilitário normalmente utilizado em linha de comando para agendar ações a serem realizadas no sistema.</p><blockquote>Quando for configurar lembre-se que o tempo é em UTC, precisando então adicionar 3 horas (para quem está no Brasil)</blockquote><p>Seguem um exemplo onde definimos que todo dia às 8h00 (11 UTC) iremos seguir uma regra de capacidade e a partir das 20h00 iremos parar todas as tarefas rodando.</p><pre>ScalableTarget:<br>  &lt;...&gt;<br>  ScheduledActions:<br>    - ScalableTargetAction:<br>        MinCapacity: 0<br>        MaxCapacity: 0<br>      Schedule: &#39;cron(0 0 22 ? * MON-FRI)&#39;<br>      ScheduledActionName: scale-down<br>    - ScalableTargetAction:<br>        MinCapacity: 2<br>        MaxCapacity: 6<br>      Schedule: &#39;cron(0 0 10 ? * MON-FRI)&#39;<br>      ScheduledActionName: scale-up</pre><blockquote>Esta é uma excelente configuração para usar em ambientes de desenvolvimento ou teste, pois economiza recursos que normalmente não são usados em certos horários!</blockquote><p>Com o <em>Scaling Policy</em> são diversas as possibilidades, aqui tem a documentação com todos os valores possíveis do campo <a href="https://docs.aws.amazon.com/autoscaling/application/APIReference/API_PredefinedMetricSpecification.html"><em>PredefinedMetricSpecification</em></a>. Lá temos alguns outros exemplos utilizando DynamoDB, conexões RDS, Storage Kafka e muito mais!</p><h3>Conclusão</h3><p>Apresentei as principais estratégias de como utilizar Auto Scaling e suas aplicações, o ponto mais importante é entender o melhor cenários para utilizar cada uma delas. Muitas vezes, acabamos não monitorando a métrica certa, o que acarreta problemas para os nossos aplicativos.</p><p>Espero que esse conteúdo o tenha ajudado à entender melhor como implementar Auto Scaling em aplicações ECS na AWS!</p><p>Caso tenha alguma crítica, sugestão ou dúvida fique à vontade para me enviar uma mensagem:</p><p>LinkedIn: <a href="https://www.linkedin.com/in/jjean-jacques10/">https://www.linkedin.com/in/jjean-jacques10/</a></p><p>Até a próxima!</p><h3>Referências</h3><ul><li><a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-applicationautoscaling-scalabletarget-scalabletargetaction.html">AWS::ApplicationAutoScaling::ScalableTarget ScalableTargetAction — AWS CloudFormation (amazon.com)</a></li><li><a href="https://docs.aws.amazon.com/pt_br/lambda/latest/dg/services-cloudwatchevents-expressions.html">Programar expressões usando rate ou cron — AWS Lambda (amazon.com)</a></li><li><a href="https://github.com/awsdocs/aws-cloudformation-user-guide/blob/main/doc_source/aws-properties-applicationautoscaling-scalabletarget-scheduledaction.md">aws-cloudformation-user-guide/aws-properties-applicationautoscaling-scalabletarget-scheduledaction.md at main · awsdocs/aws-cloudformation-user-guide (github.com)</a></li><li><a href="https://medium.com/swlh/auto-scaling-microservices-on-aws-ecs-using-cloudformation-f2dbcc246877">Auto Scaling Microservices on ECS | by Idan Lupinsky | The Startup | Medium</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=129b6ed25062" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Atualizando dados no DynamoDB utilizando AWS Glue]]></title>
            <link>https://jjeanjacques10.medium.com/atualizando-dados-no-dynamodb-utilizando-aws-glue-4b1585f6ccd6?source=rss-9ed0e2046fe4------2</link>
            <guid isPermaLink="false">https://medium.com/p/4b1585f6ccd6</guid>
            <category><![CDATA[etl]]></category>
            <category><![CDATA[aws]]></category>
            <category><![CDATA[glue]]></category>
            <category><![CDATA[dynamodb]]></category>
            <category><![CDATA[pyspark]]></category>
            <dc:creator><![CDATA[Jean Jacques Barros]]></dc:creator>
            <pubDate>Tue, 15 Nov 2022 16:37:57 GMT</pubDate>
            <atom:updated>2022-11-15T16:37:57.631Z</atom:updated>
            <content:encoded><![CDATA[<h4>Uma forma simples de atualizar um grande número de registros no banco de dados não relacional utilizam a ferramenta AWS Glue</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0RwwTUcFACF-0tLLXCItQw.png" /><figcaption>Diagrama do AWS Glue ETL Job para atualizar a tabela tb_pokemons</figcaption></figure><p>Quando trabalhamos com grandes massas de dados diversas abordagens surgem na mente para realizar atualizações em lotes. Contudo, nem sempre essas soluções são práticas e dependem de configuração de uma infraestrutura e da criação de aplicações completas para um job que deveria ser simples de se fazer, como popular um campo ou removê-lo.</p><p>Uma das ferramentas que podemos utilizar nesses casos é o <strong>AWS Glue</strong>, que é uma plataforma serverless de ETL (Extract, Transform, Load), orientada a eventos fornecida pela Amazon. É um serviço de computação que executa código em resposta à eventos e gerência automaticamente os recursos de computação exigidos por esse código.</p><blockquote>Todos os códigos apresentados nesse artigo podem ser encontrados no seguinte repositório do GitHub: <a href="https://github.com/jjeanjacques10/update-dynamodb-glue">https://github.com/jjeanjacques10/update-dynamodb-glue</a></blockquote><h4>Cenário</h4><p>Como exemplo temos essa tabela no DynamoDB onde apenas as informações de número e nome de nossos Pokémon estão cadastradas. Surgiu uma nova regra de negócio e nosso cliente precisa que sejam retornadas as categorias de cada Pokémon, para que assim não seja necessário buscar em fontes externas. Vamos então realizar esse processo de atualização para ele!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bEpErdUW4FKppNDmIAmGhQ.png" /><figcaption>Tabela de Pokémon no DynamoDB sem o campo de categoria</figcaption></figure><p>Nesse exemplo temos 9 itens cadastrados (no total são 905 criaturinhas no mundo do anime), mas poderíamos ter terabytes de informação nesse tipo de base e realizar manualmente à atualização desses dados levaria muito tempo, sem falar que é necessário filtrar apenas os dados que precisam ser atualizados, assim evitando consumir recursos de forma desnecessária no DynamoDB, que é cobrado por capacidade de leitura e escrita.</p><h3>Criando AWS Glue ETL Job</h3><h4>Adicionando as permissões</h4><p>Para poder executar o script Glue é necessário ter uma <em>Role </em>com os acessos necessários, o primeiro é o Trusted Entity do tipo Service, onde iremos selecionar o serviço do Glue.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FHXnIKzh1G1qmZEbHTB_6g.png" /><figcaption>Tela de cadastro de uma nova Role, selecionando o tipo AWS Service e Glue</figcaption></figure><p>Com o arquivo de Role gerado sendo o seguinte:</p><pre>{<br>    &quot;Version&quot;: &quot;2012-10-17&quot;,<br>    &quot;Statement&quot;: [<br>        {<br>            &quot;Effect&quot;: &quot;Allow&quot;,<br>            &quot;Principal&quot;: {<br>                &quot;Service&quot;: &quot;glue.amazonaws.com&quot;<br>            },<br>            &quot;Action&quot;: &quot;sts:AssumeRole&quot;<br>        }<br>    ]<br>}</pre><p>Como iremos trabalhar com o DynamoDB teremos que adicionar a Policy que libera o acesso à esse banco de dados e também ao S3 que é onde o nosso arquivo <em>script.py</em> será armazenado.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/858/1*YN5N9jorSVimF10W8FIVmA.png" /><figcaption>Policies selecionadas <a href="https://us-east-1.console.aws.amazon.com/iam/home#/policies/arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess">AmazonDynamoDBFullAccess</a> e <a href="https://us-east-1.console.aws.amazon.com/iam/home#/policies/arn:aws:iam::aws:policy/AmazonS3FullAccess">AmazonS3FullAccess</a></figcaption></figure><blockquote><em>Você não precisa dar o </em>FullAccess<em>, a melhor prática é adicionar apenas as permissões necessárias, como por exemplo apenas leitura ou escrita.</em></blockquote><p>Com a Role <strong><em>glue-pokemon-role</em></strong> criada e todas as permissões configuradas podemos ir para o desenvolvimento do script que vai fazer a mágica acontecer.</p><h4>Script</h4><p>Então vamos para a criação do nosso PySpark Script. O primeiro ponto é realizar o setup onde é criado o contexto e a sessão que iremos utilizar quando esse script for executado. Também temos aqui as importações que serão necessárias mais para frente.</p><pre>import sys<br>import os<br>import requests<br>from awsglue.dynamicframe import DynamicFrame<br>from awsglue.transforms import *<br>from awsglue.utils import getResolvedOptions<br>from pyspark.context import SparkContext<br>from awsglue.context import GlueContext<br>from awsglue.job import Job<br><br># @params: [JOB_NAME]<br>args = getResolvedOptions(sys.argv, [&#39;JOB_NAME&#39;])<br><br>sc = SparkContext()<br>glueContext = GlueContext(sc)<br>spark = glueContext.spark_session<br>job = Job(glueContext)<br>job.init(args[&#39;JOB_NAME&#39;], args)</pre><p>Após iniciar o job é feita a conexão com o banco de dados. Nele informamos qual o nome da tabela e os parâmetros de configuração para essa conexão.</p><pre>TABLE_NAME = &quot;tb_pokemons&quot;<br><br>dyf = glueContext.create_dynamic_frame.from_options(<br>    connection_type=&quot;dynamodb&quot;,<br>    connection_options={<br>        &quot;dynamodb.input.tableName&quot;: TABLE_NAME,<br>        &quot;dynamodb.throughput.read.percent&quot;: &quot;1.0&quot;,<br>        &quot;dynamodb.splits&quot;: &quot;100&quot;<br>    }<br>)<br><br>print(f&quot;Count Items: {dyf.count()}&quot;)<br>print(&quot;Items:&quot;)<br>dyf.show()</pre><p>Nas últimas linhas apresentamos a quantidade de itens listados e podemos dar um <em>dyf.show() </em>para mostrar todos os itens encontrados. Caso tenham muitas linhas em sua tabela é desejável que comente esse comando para evitar custos indesejáveis no CloudWatch 😅.</p><p>Vamos filtrar apenas os registros que nos interessam, fazemos isso por meio da função <em>filter</em>. Na Lambda a seguir digo que quero apenas os registros que não possuem “category”.</p><pre>filtered_dyf = dyf.filter(f=lambda x: &quot;category&quot; not in x)<br><br>print(f&quot;Count Items without category: {filtered_dyf.count()}&quot;)<br>print(&quot;Items without category:&quot;)<br>filtered_dyf.show()</pre><p>Dados filtrados, já sabemos onde atacar, para popular esse campo iremos utilizar a <a href="https://pokeapi.co/"><em>PokeAPI</em></a>, que é um serviço gratuito onde podemos buscar informações sobre todos os Pokémon deste vasto universo. Na função buscamos pelo número do registro e então atualizamos a linha com o valor encontrado.</p><pre>def add_category(row):<br>    url = f&quot;https://pokeapi.co/api/v2/pokemon/{row[&#39;number&#39;]}&quot;<br><br>    row[&quot;category&quot;] = requests.get(url).json()[&quot;types&quot;][0][&quot;type&quot;][&quot;name&quot;]<br>    return row<br><br># Filter out the rows that no have category<br>updated_dyf = filtered_dyf.map(add_category)<br><br># Show date filtered<br>updated_dyf.show()</pre><p><em>DataFrame </em>atualizado agora é a hora de enviar esses dados para a tabela <strong><em>tb_pokemons </em></strong>utilizando o mesmo glueContext passamos como parâmetro o frame igual ao nosso DF.</p><pre>if filtered_dyf.count() &lt; 1:<br>    print(&#39;There are no items to process&#39;)<br>    os._exit(0)<br>else:<br>    print(&#39;There are items to process&#39;)<br><br>    # Update the table<br>    new_data = DynamicFrame.fromDF(updated_dyf.toDF(), glueContext, &quot;new_data&quot;)<br><br>    write_df = glueContext.write_dynamic_frame_from_options(<br>        frame=new_data,<br>        connection_type=&quot;dynamodb&quot;,<br>        connection_options={<br>            &quot;dynamodb.output.tableName&quot;: TABLE_NAME,<br>            &quot;dynamodb.throughput.write.percent&quot;: &quot;1.0&quot;<br>        },<br>        transformation_ctx=&quot;write_df&quot;<br>    )<br><br>    job.commit()</pre><h4>Resultado</h4><p>Após rodar o script podemos verificar que os dados que faltavam de categoria agora estão inseridos na tabela!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0BdPpgJ1wl41o0fR28vI9g.png" /><figcaption>Tabela <strong>tb_pokemons </strong>com os registros atualizados possuindo categoria</figcaption></figure><h4>Conclusão</h4><p>A Amazon Web Services possuí diversos serviços que podem facilitar a nossa vida, normalmente o Glue é utilizado para trabalhos envolvendo Data Science ou engenharia de dados, trouxe para vocês um exemplo de como desenvolvedores podem utilizar essa ferramenta para resolver problemas do dia a dia.</p><p>Conseguimos realizar a atualizar em lote de forma fácil e rápida, sem precisar gerenciar uma arquitetura complexa como a criação de uma Lambda ou de uma aplicação apenas para isso. Essa prova de conceito serviu para mostrar o potencial de um script Python para evitar que utilizemos um canhão para matar uma formiga.</p><p>Caso tenha alguma crítica, sugestão ou dúvida fique à vontade para me enviar uma mensagem:</p><p>LinkedIn: <a href="https://www.linkedin.com/in/jjean-jacques10/">https://www.linkedin.com/in/jjean-jacques10/</a></p><p>Até a próxima!</p><h3>Referências</h3><ul><li><strong>AWS Glue<br></strong><a href="https://aws.amazon.com/glue/">https://aws.amazon.com/glue/</a></li><li><strong>Connection types and options for ETL in AWS Glue<br></strong><a href="https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-etl-connect.html#aws-glue-programming-etl-connect-dynamodb">https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-etl-connect.html#aws-glue-programming-etl-connect-dynamodb</a></li><li><strong>Update DynamoDB Glue GitHub</strong><br><a href="https://github.com/jjeanjacques10/update-dynamodb-glue">https://github.com/jjeanjacques10/update-dynamodb-glue</a></li><li><strong>PySpark Glue Tutorial</strong><br><a href="https://github.com/johnny-chivers/pyspark-glue-tutorial">https://github.com/johnny-chivers/pyspark-glue-tutorial</a></li><li><strong>How to export an Amazon DynamoDB table to Amazon S3 using AWS Step Functions and AWS Glue<br></strong><a href="https://aws.amazon.com/blogs/big-data/how-to-export-an-amazon-dynamodb-table-to-amazon-s3-using-aws-step-functions-and-aws-glue/">https://aws.amazon.com/blogs/big-data/how-to-export-an-amazon-dynamodb-table-to-amazon-s3-using-aws-step-functions-and-aws-glue/</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4b1585f6ccd6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[DynamoDB Single-Table Design com Spring Boot]]></title>
            <link>https://jjeanjacques10.medium.com/dynamodb-single-table-design-com-spring-boot-4d4af58221b6?source=rss-9ed0e2046fe4------2</link>
            <guid isPermaLink="false">https://medium.com/p/4d4af58221b6</guid>
            <category><![CDATA[spring-boot]]></category>
            <category><![CDATA[aws]]></category>
            <category><![CDATA[dynamodb]]></category>
            <category><![CDATA[cloud]]></category>
            <category><![CDATA[spring]]></category>
            <dc:creator><![CDATA[Jean Jacques Barros]]></dc:creator>
            <pubDate>Tue, 13 Sep 2022 12:14:14 GMT</pubDate>
            <atom:updated>2022-09-13T12:14:14.774Z</atom:updated>
            <content:encoded><![CDATA[<h4>Um exemplo prático de como utilizar Single-table Design em uma aplicação Java Spring Boot</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ITOdDbaqmjC03QWTVUCl0w.png" /></figure><p>Desde que conheci o conceito de Sigle-Table Design quis entender melhor como isso poderia ser aplicado em um cenário real, criei essa prova de conceito e gostaria de apresentar para vocês!</p><p>DynamoDB é um banco de dados No-SQL de chave-valor (key-value) criado pela AWS, ele tem como foco trabalhar com grandes volumes de dados oferecendo performance. Diversas empresas atualmente utilizam esse banco de dados, como por exemplo Itaú, Amazon, Mercado Livre e a própria AWS. Neste artigo irei abordar algumas diferenças que você teria ao utilizar a estratégia de Single-table.</p><h4>O que é Single-table Design</h4><p>Vamos começar lembrando um fato sobre o <strong>DynamoDB,</strong> sendo um banco de dados No-SQL, não tem o conceito de “joins”, portanto, se os dados estão na forma normalizada em várias tabelas, recuperar itens relacionados em mais de uma tabela exigirá várias solicitações em série para obter esses registros. No seguinte exemplo temos duas tabelas, a de personagens e a de revistas, que em nossa consulta retornam para o usuário informações sobre a os quadrinhos de um personagem específico e também seu perfil, aqui precisaríamos realizar duas consultas separadas para cada tabela.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UmSvyAmLRyoeGHldeIOkcQ.png" /><figcaption>Consulta sendo realizada em múltiplas tabelas</figcaption></figure><p>Um método mais simples de obter esse mesmo resultado é centralizando tudo em apenas 1 tabela, aplicando o <strong>Single-Table Design</strong>, assim sempre que for necessário retornar informações do personagem e das revistas em que ele participa podemos realizar apenas 1 consulta que obteremos todos os dados.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8XV3A766p8iQjrzle1uP7Q.png" /><figcaption>Consulta sendo feita em uma Single Table</figcaption></figure><p>Sendo essa a principal razão para migrar para uma <strong>single table</strong> no DynamoDB, a possibilidade de recuperar vários tipos de dados heterogêneos usando uma única solicitação.</p><p>Outra vantagem é que não será necessária a criação e acompanhamento de de alarmes ou métricas de monitoração para múltiplas tabelas, sendo que a cada uma que você provisiona novas configurações são necessárias, como permissões e monitoração como já citado. Quando centralizamos tudo em apenas uma tabela temos a redução de custos consumo para capacidades de leitura sendo que antes tínhamos o consumo dessa capacidade sendo feita em múltiplas <em>queries </em>e agora apenas 1 é necessária.</p><h4>Modelando a tabela</h4><p>Vamos iniciar nossa modelagem determinando quais entidades queremos trabalhar, esse processo é muito parecido com o que fazemos ou modelar tabelas em um banco relacional, como MySQL ou Oracle. No nosso caso de uso as duas entidades são <strong><em>Character </em></strong>(Personagem) e <strong><em>Comic </em></strong>(Quadrinho), como é possível observar na imagem a baixo poderíamos ter uma tabela para cada um deles.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/919/1*6bf5smS5e8Ep_V4_GWOJzQ.png" /><figcaption>Modelagem relacional das tabelas</figcaption></figure><p>Em banco relacionais temos a opção de realizar “JOIN’s” para obter a informação destas duas tabelas em conjunto, mas no DynamoDB isso não é possível.</p><p>Então vamos evoluir esse modelo aplicando o <strong>Single-Table Design</strong> a ele. Primeiro passo é definir os padrões de acesso que iremos utilizar:</p><ul><li>Retornar o perfil de um personagem</li><li>Listar todos os perfis de personagens</li><li>Listar todos os quadrinhos de um personagem</li><li>Listar todos os quadrinhos</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-J06RmA1rK2U9O-xtbwabA.png" /><figcaption>Tabela de comics normalizada para Single-Table Design</figcaption></figure><p>Nesta nova modelagem, já no estilo de nosso banco No-SQL, temos como PK (<em>PartitionKey</em>) a informação de Character juntamente com o seu nickname, e nossa SK (<em>SortKey</em>) é composta pelo tipo (PROFILE/COMIC) e seu identificador. Antes o que eram 2 tabelas se transformaram em apenas uma onde cada linha representa um tipo de dado, como o DynamoDB é um banco com esquema “fraco” podemos ter diferentes colunas em cada linha da tabela, por conta disso não ficamos presos em colunas desnecessárias, exemplo disso são as colunas “comicId” e “name”.</p><p>Agora, quando queremos buscar o Personagem e os Quadrinhos, podemos fazê-lo em uma única solicitação sem precisar de uma operação de concatenação.</p><p>Com esse conceito e com nossa tabela já modelada, vou mostrar agora como colocar a mão na massa e implementar chamadas em uma aplicação Spring Boot!</p><h3>Criando nossa Aplicação Spring Boot</h3><p>Criei a base do projeto utilizando o site: <a href="https://start.spring.io/">https://start.spring.io/</a> . Selecione as bibliotecas padrões do Spring Web para criação de uma API Rest.</p><h4>Dependências</h4><p>Após descompactar a pasta dele vamos iniciar adicionando as dependências necessárias, as duas libs que iremos utilizar são:</p><ul><li>AWS SDK</li><li>DynamoDB Enhanced</li></ul><pre>&lt;dependency&gt;<br>    &lt;groupId&gt;software.amazon.awssdk&lt;/groupId&gt;<br>    <strong>&lt;artifactId&gt;dynamodb-enhanced&lt;/artifactId&gt;</strong><br>    &lt;version&gt;2.17.263&lt;/version&gt;<br>&lt;/dependency&gt;<br>&lt;dependency&gt;<br>    &lt;groupId&gt;com.amazonaws&lt;/groupId&gt;<br><strong>    &lt;artifactId&gt;aws-java-sdk-dynamodb&lt;/artifactId&gt;<br></strong>    &lt;version&gt;1.11.857&lt;/version&gt;<br>&lt;/dependency&gt;</pre><blockquote><em>Obs: Não irei entrar em muitos detalhes sobre a configuração dessas duas libs, então caso tenha alguma dúvida pode entrar no repositório de exemplo na pasta config (</em><a href="https://github.com/jjeanjacques10/spring-dynamodb-single-table-design/blob/main/src/main/java/com/jjeanjacques10/comics/config/DynamoDbConfig.java">comics/config/DynamoDbConfig.java</a>)</blockquote><h4>Entidades</h4><p>Nessa próxima etapa vamos criar nossa entidade que será a referência do item salvo em na tabela do Dynamo. Aqui temos a Entity de Characters apenas com os campos que esse tipo de dado necessita, na visão da tabela seria a linha com o SK <strong><em>PROFILE#</em></strong>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4ca4dde6d42d5063be55678afa5ef4b9/href">https://medium.com/media/4ca4dde6d42d5063be55678afa5ef4b9/href</a></iframe><p>Agora essa é referente ao item de Comics (Quadrinho), são muito parecidas, afinal apenas os campos “comicId” e “type” foram adicionados para esse tipo de objeto e que não existiam no outro. Na visão da tabela seria a linha com o SK <strong><em>COMIC#</em></strong>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f4e6988d850d5872d0013ea096613dcc/href">https://medium.com/media/f4e6988d850d5872d0013ea096613dcc/href</a></iframe><p>Agora para o cenário que comentamos anteriormente, onde são retornados ambos: <em>profile </em>e <em>comics</em>, vamos utilizar essa nova entidade que possuí todos os campos necessários para os dois itens. Nos casos onde um não é possível encontrar o campo no registro apenas será ignorado, como por exemplo profile que não te “comicId” e comic que não possuí “name”.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4db9555ed4e02587837dd158b80ffc3e/href">https://medium.com/media/4db9555ed4e02587837dd158b80ffc3e/href</a></iframe><p>Pronto! Entidades criadas, elas serão a base para os próximos passos onde iremos realizar as consultas.</p><h4>Realizando buscas</h4><p>O DynamoDb trabalha com dois tipos de buscas o Scan e a Query, sendo o primeiro o mais lento e custoso, que faz uma pesquisa na tabela e retorna todas as informações, podemos até realizar filtros, porém, eles apenas serão aplicados depois de mapear todos os dados. O método Query é o mais recomendado a ser utilizado, onde apenas solicitamos a informação que precisamos. Contudo, o query apenas funciona em buscas utilizando os campos <em>PartitionKey </em>e <em>SortKey </em>que juntos forma o PrimaryKey. Este campo<em> </em>no DynamoDB pode ser <em>simples</em> ou <em>composto</em>.</p><ul><li><strong>Simple Primary Keys:</strong> Consistem em uma <em>PartitionKey </em>e nenhuma <em>SortKey</em>.</li><li><strong>Composite Primary Keys:</strong> Consistem em uma <em>PartitionKey </em>e uma <em>SortKey</em>.</li></ul><p>Para não ficarmos limitados apenas aqueles primeiros PK e SK que criamos podemos criar <strong>Global Secondary Indexes</strong>, que são PK’s e SK’s diferentes do que geramos na criação da tabela e correspondem a cópias de nossa tabela, facilitando assim na hora de realizar buscas. Por exemplo, seria a utilização do campo <em>type </em>(PK) e <em>date </em>(SK) para realizar filtros de tempo. Isso se mostra como uma vantagem, mas apenas podemos criar 20 desses indexes personalizados.</p><p>Para realizar buscas mais avançadas sem precisar de Global Secondary Indexes para tudo podemos utilizar o begins_with que faz um filtro em nossa <em>SortKey</em>, e essa é uma das principais vantagens de utilizar um <strong>Single-table Design</strong>. Aqui é um exemplo de código onde busco apenas o perfil de um personagem:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/29ba3504ffa832911a3fb8df455145d5/href">https://medium.com/media/29ba3504ffa832911a3fb8df455145d5/href</a></iframe><p>Também conseguimos filtrar todos os quadrinhos de um personagem específico utilizando o begins_with <strong>COMIC#</strong>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0e6b6a94a9518361146a7338808759ec/href">https://medium.com/media/0e6b6a94a9518361146a7338808759ec/href</a></iframe><blockquote>Só é possível utilizar esse método begins_with em SortKeys.</blockquote><p>Agora para o tipo de consulta que comentamos acima onde devolvemos essas duas consultas anteriores juntas, em apenas uma busca na tabela fazemos a query sem passar a informação de SortKey, apenas a nossa partition.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/20273f97d8fbec2a3252208afdce3d30/href">https://medium.com/media/20273f97d8fbec2a3252208afdce3d30/href</a></iframe><p>De forma simples evitamos consumir muitos recursos na hora de fazer o que antes seria um “JOIN” procurando a informação em duas tabelas. Exemplo de retorno:</p><pre>[<br>    {<br>        &quot;pk&quot;: &quot;CHARACTER#greenlantern&quot;,<br>        &quot;sk&quot;: &quot;COMIC#58f59aa1&quot;,<br>        &quot;type&quot;: &quot;COMIC&quot;,<br>        &quot;nickName&quot;: &quot;greenlantern&quot;,<br>        &quot;realName&quot;: null,<br>        &quot;description&quot;: &quot;Green Lantern: New Guardians is an American comic book series originally written by Tony Bedard with art by Tyler Kirkham and Batt and published by DC Comics. The team consists of representatives of each of the Corps that tap into a particular portion of the emotional spectrum.&quot;,<br>        &quot;comicId&quot;: &quot;58f59aa1&quot;,<br>        &quot;title&quot;: &quot;Green Lantern: New Guardians&quot;<br>    },<br>    &lt;.. Comics ..&gt;<br>    {<br>        &quot;pk&quot;: &quot;CHARACTER#greenlantern&quot;,<br>        &quot;sk&quot;: &quot;PROFILE#greenlantern&quot;,<br>        &quot;type&quot;: null,<br>        &quot;nickName&quot;: &quot;greenlantern&quot;,<br>        &quot;realName&quot;: &quot;Hal Jordan&quot;,<br>        &quot;description&quot;: &quot;Harold &#39;Hal&#39; Jordan, one of the characters known as Green Lantern, is a superhero appearing in American comic books published by DC Comics.&quot;,<br>        &quot;comicId&quot;: null,<br>        &quot;title&quot;: null<br>    }<br>]</pre><p><strong>Repositório projeto de exemplo:</strong></p><blockquote><em>Todos esses exemplos podem ser encontrados no seguinte repositório no GitHub: </em><a href="https://github.com/jjeanjacques10/spring-dynamodb-single-table-design">jjeanjacques10/spring-dynamodb-single-table-design: Demo project for Spring Boot + DynamoDB Single Table Design (github.com)</a></blockquote><h4>Desvantagens</h4><p>Em bases como essa temos algumas desvantagens, por exemplo a dificuldade para realizar análises de dados, como temos essas informações desnormalizadas, o processo de exportar esse tipo de tabela para ferramentas de analytic acaba se tornando complexo. Além disso, com o tempo surge a falta de flexibilidade para adicionar novos padrões de acesso, como por exemplo uma nova entidade que se relacionará com personagens e quadrinhos.</p><p>Outro ponto é a curva de aprendizado que acaba sendo maior para quem inicia em um projeto em andamento, sendo que no inicio como vimos foram necessárias diversas conversas para chegar em um modelo ideal, esse conhecimento teria que ser passado a frente juntamente com as regras de negócio que ajudaram a modelar esse resultado atual do projeto.</p><h3>Conclusão</h3><p>Com isso podemos concluir que o que irá determinar o uso desse tipo de design é o caso de uso que você tem em mãos. Como tudo em desenvolvimento de software esse tipo de abordagem é muito interessante, porém tem seus <em>tradeoffs</em>.</p><p>O uso de tabelas DynamoDB facilita muito no dia a dia, principalmente quando nossos dados são utilizamos muito para consultas, convido vocês a clonar o <a href="https://github.com/jjeanjacques10/spring-dynamodb-single-table-design">repositório</a> com os exemplos, nele tem um ambiente criado para testes locais utilizando o <a href="https://github.com/localstack/localstack">Localstack</a>, simulando o DynamoDB. Só colocando a mão na massas que será possível entender se essa abordagem cumpre com os desafios propostos!</p><p>Caso tenha alguma crítica, sugestão ou dúvida fique a vontade para me enviar uma mensagem:</p><p>Linkedin: <a href="https://www.linkedin.com/in/jjean-jacques10/">https://www.linkedin.com/in/jjean-jacques10/</a></p><p>Até a próxima!</p><h4>Referências</h4><p><a href="https://www.alexdebrie.com/posts/dynamodb-single-table/">The What, Why, and When of Single-Table Design with DynamoDB (alexdebrie.com)</a></p><p><a href="https://aws.amazon.com/blogs/database/amazon-dynamodb-single-table-design-using-dynamodbmapper-and-spring-boot/">Amazon DynamoDB single-table design using DynamoDBMapper and Spring Boot | AWS Database Blog</a></p><p><a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GettingStarted.html">Getting started with DynamoDB and AWS SDKs — Amazon DynamoDB</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4d4af58221b6" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>