<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Eduardo Rabelo</title>
    <description>The latest articles on Forem by Eduardo Rabelo (@oieduardorabelo).</description>
    <link>https://forem.com/oieduardorabelo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F95913%2F093483ee-40d3-4ad2-9dbd-7a2eb57b020d.jpg</url>
      <title>Forem: Eduardo Rabelo</title>
      <link>https://forem.com/oieduardorabelo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/oieduardorabelo"/>
    <language>en</language>
    <item>
      <title>Por que eu não uso bibliotecas de gerenciamento de estado no React</title>
      <dc:creator>Eduardo Rabelo</dc:creator>
      <pubDate>Sat, 14 Dec 2024 01:18:44 +0000</pubDate>
      <link>https://forem.com/oieduardorabelo/por-que-eu-nao-uso-bibliotecas-de-gerenciamento-de-estado-no-react-1i</link>
      <guid>https://forem.com/oieduardorabelo/por-que-eu-nao-uso-bibliotecas-de-gerenciamento-de-estado-no-react-1i</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Original source in English by&lt;/strong&gt; &lt;a href="https://x.com/ipla03" rel="noopener noreferrer"&gt;&lt;strong&gt;Fabrizio Beccaceci&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;
Why i no longer use a React state management library&lt;br&gt;
&lt;a href="https://medium.com/@ipla/why-i-no-longer-use-a-react-state-management-library-7bdffae54600" rel="noopener noreferrer"&gt;https://medium.com/@ipla/why-i-no-longer-use-a-react-state-management-library-7bdffae54600&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Índice
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;O que é uma Biblioteca de Gerenciamento de Estado Global?&lt;/li&gt;
&lt;li&gt;Como o Redux Toolkit Resolve o Estado Global&lt;/li&gt;
&lt;li&gt;As Duas Faces do Estado Global&lt;/li&gt;
&lt;li&gt;A Abordagem Enxuta para Estado Global

&lt;ul&gt;
&lt;li&gt;Para Estado do Servidor use TanStack Query&lt;/li&gt;
&lt;li&gt;Para Estado Compartilhado use Observables&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Por que não usar React Context?&lt;/li&gt;

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

&lt;/ul&gt;

&lt;p&gt;Quando comecei a aprender React anos atrs, havia &lt;strong&gt;duas coisas que voc precisava saber&lt;/strong&gt; para se considerar um desenvolvedor React: &lt;strong&gt;TypeScript e Redux&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Hoje em dia, as coisas mudaram. Se voc est aprendendo React agora e se depara com o conceito de gerenciamento de estado, vai se sentir sobrecarregado com uma infinidade de bibliotecas: Redux, Redux Toolkit, MobX, Jotai, Zustand e a lista continua.&lt;/p&gt;

&lt;p&gt;Mas antes de nos aprofundarmos, vamos responder pergunta bsica:&lt;/p&gt;

&lt;h2&gt;
  
  
  O que uma Biblioteca de Gerenciamento de Estado Global?
&lt;/h2&gt;

&lt;p&gt;Mesmo se voc for relativamente novo no React, provavelmente sabe o que o termo &lt;strong&gt;&lt;em&gt;estado&lt;/em&gt;&lt;/strong&gt;. Voc cria um com o hook &lt;code&gt;useState&lt;/code&gt;, e quando voc o atualiza, seu aplicativo renderiza novamente para refletir as mudanas.&lt;/p&gt;

&lt;p&gt;No entanto, &lt;strong&gt;as coisas ficam complicadas quando voc precisa compartilhar alguma informao entre vrias partes no conectadas do seu aplicativo&lt;/strong&gt; React.&lt;/p&gt;

&lt;p&gt;Isso o que frequentemente chamamos de &lt;strong&gt;estado global&lt;/strong&gt; , e o problema que as bibliotecas de gerenciamento de estado tentam resolver: &lt;strong&gt;como tornar o estado compartilhado acessvel a qualquer componente&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Vamos ver como o Redux Toolkit lida com isso.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Como o Redux Toolkit Resolve o Estado Global&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Primeiro, voc cria uma &lt;strong&gt;&lt;em&gt;store&lt;/em&gt;&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;configureStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;configureStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Inferindo os tipos de RootState e AppDispatch usando a store&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;RootState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AppDispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este o &lt;strong&gt;local central&lt;/strong&gt; onde seu estado global ir existir. Em seguida, voc envolve seu aplicativo com um &lt;strong&gt;&lt;em&gt;provider&lt;/em&gt;&lt;/strong&gt; para tornar a &lt;em&gt;store&lt;/em&gt; acessvel a todos os componentes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ReactDOM&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app/store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Provider&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora, voc precisa definir os &lt;strong&gt;&lt;em&gt;slices&lt;/em&gt;&lt;/strong&gt; , que so partes individuais do seu estado global. Por exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSlice&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CounterState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CounterState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counterSlice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSlice&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;counter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;decrement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;incrementByAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;decrement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;incrementByAmount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;counterSlice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;counterSlice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por fim, voc conecta este &lt;em&gt;slice&lt;/em&gt;&lt;em&gt;store&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;configureStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;counterReducer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./features/counter/counterSlice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;configureStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;counterReducer&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neste ponto, voc pode usar seu estado global nos componentes da seguinte forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useDispatch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;decrement&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./counterSlice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDispatch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;())}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Increment&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;decrement&lt;/span&gt;&lt;span class="p"&gt;())}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Decrement&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Embora o Redux Toolkit simplifique significativamente o Redux, ainda tem &lt;strong&gt;muito cdigo &lt;em&gt;boilerplate&lt;/em&gt; para o que geralmente uma necessidade simples&lt;/strong&gt;. E nem sequer falamos sobre aes assncronas como requisies ao servidor!&lt;/p&gt;

&lt;p&gt;Alm disso, muitos iniciantes caem na armadilha de colocar todo o seu estado no Redux, levando a stores globais infladas que so mais difceis de gerenciar.&lt;/p&gt;

&lt;h2&gt;
  
  
  As Duas Faces do Estado Global
&lt;/h2&gt;

&lt;p&gt;Quando voc para pra pensar, a maioria do "estado global" se enquadra em duas categorias:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Estado do Servidor&lt;/strong&gt; : Dados obtidos de um servidor, como uma lista de clientes em um aplicativo CRM.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Estado da UI Compartilhado&lt;/strong&gt; : Pequenos pedaos de dados necessrios em vrios lugares, como o usurio atualmente logado.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Vamos abordar cada um deles separadamente.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;A Abordagem Enxuta para Estado Global&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Com ferramentas modernas, voc pode lidar com estado global sem uma biblioteca dedicada de gerenciamento de estado:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Para Estado do Servidor use TanStack Query&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TanStack Query (anteriormente React Query) feito especificamente para gerenciar estado do servidor. Ele lida com cache, recarregamento, dados obsoletos e muito mais, tudo pronto para uso, e voc definitivamente deveria usar isso porque voc realmente no vai querer reimplementar tudo isso do zero. Por exemplo, buscar uma lista de clientes to simples quanto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tanstack/react-query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Customers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;fetchCustomers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;}&amp;lt;/u&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O TanStack Query elimina a necessidade de gerenciar manualmente o estado do servidor e, como o estado que voc busca armazenado em cache, voc pode us-lo exatamente da mesma maneira em todos os componentes e ele ser buscado apenas uma vez.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Para Estado Compartilhado use Observables&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Para estados menores e especficos do aplicativo, como o usurio logado, eu uso uma implementao leve do padro &lt;strong&gt;Observable&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;subscribers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;initialValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;initialValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com isso, voc pode conectar Observables ao React usando &lt;code&gt;useSyncExternalStore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useSyncExternalStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useObservable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useSyncExternalStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;observable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;observable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora, voc pode criar e usar &lt;strong&gt;&lt;em&gt;observables&lt;/em&gt;&lt;/strong&gt; desta forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loggedUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;loggedUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="c1"&gt;// Para acessar o valor dentro do Observable fora do React&lt;/span&gt;

&lt;span class="nx"&gt;loggedUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;someOtherUser&lt;/span&gt; &lt;span class="c1"&gt;// Para definir o valor, tanto fora quanto dentro do React&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dentro de um componente React:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useObservable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loggedUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Por que no usar React Context?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Embora o React Context funcione em alguns casos, ele tem desvantagens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Escopo Limitado&lt;/strong&gt; : Voc no pode acessar o Context fora dos componentes React.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Problemas de Performance&lt;/strong&gt; : Context dispara uma nova renderizao para todos os componentes abaixo dele, at mesmo para os componentes que no usem o valor atualizado.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Usando &lt;strong&gt;&lt;em&gt;Observables&lt;/em&gt;&lt;/strong&gt; evitamos ambos problemas!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Concluso&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Gerenciamento de estado global no precisa ser complicado. Combinando TanStack Query e um padro Observable leve, voc pode simplificar seu aplicativo evitando as armadilhas das bibliotecas tradicionais de gerenciamento de estado.&lt;/p&gt;

&lt;p&gt;Entre em contato comigo se quiser discutir sobre React, React Native, Nextjs&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Me contate no &lt;a href="https://x.com/ipla03" rel="noopener noreferrer"&gt;X (antigo Twitter)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ou acesse &lt;a href="https://www.iplastudio.com/" rel="noopener noreferrer"&gt;Iplastudio&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bom desenvolvimento! 🎉&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>redux</category>
      <category>ptbr</category>
    </item>
    <item>
      <title>S3 virus scanning with TypeScript and Node.js 20.x AWS Lambda Container</title>
      <dc:creator>Eduardo Rabelo</dc:creator>
      <pubDate>Wed, 06 Dec 2023 05:06:45 +0000</pubDate>
      <link>https://forem.com/aws-builders/s3-virus-scanning-with-typescript-and-nodejs-20x-aws-lambda-container-220f</link>
      <guid>https://forem.com/aws-builders/s3-virus-scanning-with-typescript-and-nodejs-20x-aws-lambda-container-220f</guid>
      <description>&lt;p&gt;Searching the internet, you can find guides showing how to create a serverless virus scanning with ClamAV:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/sutt0n/using-a-lambda-container-to-scan-files-with-clamav-via-serverless-2a5g"&gt;Using Serverless to Scan Files with ClamAV in a Lambda Container&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/sutt0n/scanning-files-on-lambda-with-a-clamav-lambda-layer-475c"&gt;Using Serverless to Scan Files with a ClamAV Lambda Layer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tsh.io/blog/serverless-tutorial-virus-scanning-solution/" rel="noopener noreferrer"&gt;Serverless tutorial: Let’s build a virus scanning solution with automated database updates&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even in the AWS Blog you can find good ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/developer/virus-scan-s3-buckets-with-a-serverless-clamav-based-cdk-construct/" rel="noopener noreferrer"&gt;Virus scan S3 buckets with a serverless ClamAV based CDK construct&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples/aws-serverless-s3-antivirus" rel="noopener noreferrer"&gt;AWS SAM application to keep your S3 objects safe from viruses using ClamAV Open Source software&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, a few aspects fell short of my expectations:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) They are not using the &lt;a href="https://www.clamav.net/downloads" rel="noopener noreferrer"&gt;latest version of ClamAV&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
The examples above are installing ClamAV directly from the OS package registry (e.g., &lt;code&gt;yum install clamav&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;They aren't always in sync with the latest release, and if you don't update or upgrade the OS package registry, you will install older versions.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;yum install clamav&lt;/code&gt; for Amazon Linux 2, we receive the &lt;code&gt;0.100.x&lt;/code&gt; version, whereas the ClamAV release is already in the &lt;code&gt;1.x&lt;/code&gt; version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) Outdated Node.js runtime&lt;/strong&gt;&lt;br&gt;
Examples using Node.js use &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html" rel="noopener noreferrer"&gt;the outdated Node.js 14.x runtime&lt;/a&gt;. This runtime is already under &lt;strong&gt;Deprecation (Phase 1)&lt;/strong&gt; in the AWS timeline for Node.js supported runtimes and &lt;a href="https://nodejs.org/en/about/previous-releases" rel="noopener noreferrer"&gt;is not part of the maintenance window of Node.js releases&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Using outdated and unsupported versions is a risk I try to avoid!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3) Using plain JavaScript&lt;/strong&gt;&lt;br&gt;
While not a deal-breaker, I don't use plain JavaScript in my production projects.&lt;/p&gt;

&lt;p&gt;How would a TypeScript example look in the latest AWS Lambda runtime for Node.js?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4) How to use top-level await in Node.js handlers&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://dev.to/oieduardorabelo/top-level-await-in-aws-lamba-with-typescript-1bf0"&gt;Top-level await&lt;/a&gt; support in AWS Lambda is not new, but how can we configure it with everything else? (e.g., TypeScript, esbuild, etc). How can we output ESM code in the lambda handler?&lt;/p&gt;
&lt;h2&gt;
  
  
  Here it comes Amazon Linux 2023
&lt;/h2&gt;

&lt;p&gt;Starting on Node.js 20.x runtime, the &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html" rel="noopener noreferrer"&gt;default operational system for the AWS Lambda base image&lt;/a&gt; is Amazon Linux 2023.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.aws.amazon.com/linux/al2023/ug/package-management.html" rel="noopener noreferrer"&gt;AL2023&lt;/a&gt; brings a new package management tool called &lt;code&gt;dnf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dnf&lt;/code&gt; is the successor to &lt;code&gt;yum&lt;/code&gt;, the package management tool in Amazon Linux 2.&lt;/p&gt;

&lt;p&gt;While many of the commands are compatible, for example, for the following Amazon Linux 2 &lt;code&gt;yum&lt;/code&gt; commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;packagename
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;yum search packagename
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;yum remove packagename
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In AL2023, they become these commands:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo dnf install packagename
$ sudo dnf search packagename
$ sudo dnf remove packagename
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Not everything stays the same. Be aware of changes! 🚨&lt;/p&gt;

&lt;p&gt;You can check the page &lt;a href="https://dnf.readthedocs.io/en/latest/cli_vs_yum.html" rel="noopener noreferrer"&gt;changes in &lt;code&gt;dnf&lt;/code&gt; CLI compared to &lt;code&gt;yum&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's build a newer example
&lt;/h2&gt;

&lt;p&gt;We'll keep the example project similar to the guides listed at the beginning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A S3 bucket with object notification to an AWS Lambda&lt;/li&gt;
&lt;li&gt;When an object is created in S3, a notification triggers the AWS Lambda&lt;/li&gt;
&lt;li&gt;The Lambda will read the file from the bucket, write it to &lt;code&gt;/tmp&lt;/code&gt;, and run &lt;code&gt;clamscan&lt;/code&gt; on it&lt;/li&gt;
&lt;li&gt;The returned code from &lt;code&gt;clamscan&lt;/code&gt; will be used to check the file status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4fkzptasm0fd6emwq9v9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4fkzptasm0fd6emwq9v9.png" alt="Diagram showing the example project: a s3 bucket with object created notification to lambda"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are a few constraints I want to define for our newer example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Because we need to download the ClamAV virus database in our lambda source code, the &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html" rel="noopener noreferrer"&gt;uncompressed file size of 250MB&lt;/a&gt; can be an issue.&lt;/li&gt;
&lt;li&gt;We will be using &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/images-create.html" rel="noopener noreferrer"&gt;AWS Lambda Container&lt;/a&gt; image code, which enables us to have up to 10GB of uncompressed image size.&lt;/li&gt;
&lt;li&gt;The Docker &lt;code&gt;build&lt;/code&gt; process should take care of transpiling TypeScript to JavaScript and installing production-only dependencies.&lt;/li&gt;
&lt;li&gt;We want to download and install the latest ClamAV during the Docker &lt;code&gt;build&lt;/code&gt; process and update its virus definitions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The Dockerfile
&lt;/h3&gt;

&lt;p&gt;We can use &lt;a href="https://docs.docker.com/build/building/multi-stage/" rel="noopener noreferrer"&gt;Docker multi-stage builds&lt;/a&gt; to create these steps:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ========================================&lt;/span&gt;
&lt;span class="c"&gt;# Builder Image&lt;/span&gt;
&lt;span class="c"&gt;# ========================================&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--platform=linux/x86_64 public.ecr.aws/lambda/nodejs:20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json index.ts  ./&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# 1) install dependencies with dev dependencies&lt;/span&gt;
&lt;span class="c"&gt;# 2) build the project&lt;/span&gt;
&lt;span class="c"&gt;# 3) remove dev dependencies&lt;/span&gt;
&lt;span class="c"&gt;# 4) install dependencies without dev dependencies&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    npm run build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; node_modules &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--omit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev

&lt;span class="c"&gt;# ========================================&lt;/span&gt;
&lt;span class="c"&gt;# Runtime Image&lt;/span&gt;
&lt;span class="c"&gt;# ========================================&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;--platform=linux/x86_64 public.ecr.aws/lambda/nodejs:20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runtime&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; CLAMAV_PKG=clamav-1.2.1.linux.x86_64.rpm&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;
    set -ex

    &lt;span class="c"&gt;#&lt;/span&gt;
    &lt;span class="c"&gt;# install glibc-langpack-en to support english language and utf-8&lt;/span&gt;
    &lt;span class="c"&gt;# this was required by clamscan to avoid error "WARNING: Failed to set locale"&lt;/span&gt;
    &lt;span class="c"&gt;#&lt;/span&gt;
    dnf install wget glibc-langpack-en -y

    &lt;span class="c"&gt;# &lt;/span&gt;
    &lt;span class="c"&gt;# 1) download latest ClamAV from https://www.clamav.net/downloads&lt;/span&gt;
    &lt;span class="c"&gt;# 2) install using `rpm` and it requires full path for local packages&lt;/span&gt;
    &lt;span class="c"&gt;# 3) remove the downloaded package and clean up for smaller runtime image&lt;/span&gt;
    &lt;span class="c"&gt;# &lt;/span&gt;
    wget https://www.clamav.net/downloads/production/${CLAMAV_PKG}
    rpm -ivh "${LAMBDA_TASK_ROOT}/${CLAMAV_PKG}"
    rm -rf ${CLAMAV_PKG}
    dnf remove wget -y
    dnf clean all

    &lt;span class="c"&gt;#&lt;/span&gt;
    &lt;span class="c"&gt;# the current working directory is "/var/task" as defined in the base image:&lt;/span&gt;
    &lt;span class="c"&gt;# https://github.com/aws/aws-lambda-base-images/blob/nodejs20.x/Dockerfile.nodejs20.x&lt;/span&gt;
    &lt;span class="c"&gt;#&lt;/span&gt;
    &lt;span class="c"&gt;# 1) "lib/database" is the path to download the virus database&lt;/span&gt;
    &lt;span class="c"&gt;# 2) "freshclam.download.log" and "freshclam.conf.log" are the log files for freshclam CLI&lt;/span&gt;
    &lt;span class="c"&gt;#&lt;/span&gt;
    mkdir -p ${LAMBDA_TASK_ROOT}/lib/database
    touch ${LAMBDA_TASK_ROOT}/lib/{freshclam.download.log,freshclam.conf.log}
    chmod -R 777 ${LAMBDA_TASK_ROOT}/lib

    &lt;span class="c"&gt;#&lt;/span&gt;
    &lt;span class="c"&gt;# default configuration path for freshclam is "/usr/local/etc/freshclam.conf"&lt;/span&gt;
    &lt;span class="c"&gt;# we create a symbolic link to the default configuration path and copy our custom config file&lt;/span&gt;
    &lt;span class="c"&gt;#&lt;/span&gt;
    ln -s /usr/local/etc/freshclam.conf ${LAMBDA_TASK_ROOT}/lib/freshclam.conf
EOF

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; freshclam.conf /var/task/lib/freshclam.conf&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# freshclam CLI is a virus database update tool for ClamAV, documentation:&lt;/span&gt;
&lt;span class="c"&gt;# https://linux.die.net/man/1/freshclam&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;
    set -ex
    export LOG_FILE_PATH="${LAMBDA_TASK_ROOT}/lib/freshclam.conf.log"

    freshclam --verbose --stdout --user root \
        --log=${LOG_FILE_PATH} \
        --datadir=${LAMBDA_TASK_ROOT}/lib/database

    if grep -q "Can't download daily.cvd\|Can't download main.cvd\|Can't download bytecode.cvd" ${LOG_FILE_PATH}; then
        echo "ERROR: Unable to download ClamAV database files - your request may be being rate limited"
        exit 1;
    fi
EOF

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# copy application files from the builder image&lt;/span&gt;
&lt;span class="c"&gt;# &lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /var/task/dist/* /var/task/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /var/task/node_modules /var/task/node_modules&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; [ "index.handler" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The above Dockerfile covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install and build the TypeScript Lambda to JavaScript with production dependencies using a multi-stage build. The first stage &lt;code&gt;as builder&lt;/code&gt; creates the &lt;code&gt;dist&lt;/code&gt; folder and &lt;code&gt;node_modules&lt;/code&gt; folder used by the &lt;code&gt;as runtime&lt;/code&gt; stage&lt;/li&gt;
&lt;li&gt;Download the latest ClamAV from their release page, install it using &lt;code&gt;rpm&lt;/code&gt; and remove cache for smaller final image&lt;/li&gt;
&lt;li&gt;Download the ClamAV virus database definitions with &lt;code&gt;freshclam&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You can change the &lt;code&gt;CLAMAV_PKG&lt;/code&gt; to be in sync with the latest version of ClamAV&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🚨 &lt;strong&gt;Important:&lt;/strong&gt; To update your database definition, you need to re-build this image every once in a while&lt;/p&gt;

&lt;p&gt;The required &lt;code&gt;freshclam.conf&lt;/code&gt; file contains the following:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CompressLocalDatabase yes
DatabaseDirectory /var/task/lib/database
DatabaseMirror database.clamav.net
DNSDatabaseInfo current.cvd.clamav.net
ScriptedUpdates no
UpdateLogFile  /var/task/lib/freshclam.conf.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;🚨 &lt;strong&gt;Important:&lt;/strong&gt; The full path files (e.g., &lt;code&gt;/var/task/*&lt;/code&gt; must match the Dockerfile definitions&lt;/p&gt;
&lt;h3&gt;
  
  
  The TypeScript AWS Lambda Handler
&lt;/h3&gt;

&lt;p&gt;For a S3 notification event, we can write our handler similar to:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;S3CreateEvent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aws-lambda&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GetObjectCommand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;S3Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/client-s3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;spawnSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:child_process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;writeFile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:fs/promises&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s3Client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// directories for clamscan&lt;/span&gt;
&lt;span class="c1"&gt;// "/tmp/files_to_scan" where we will store the files from s3 to scan&lt;/span&gt;
&lt;span class="c1"&gt;// "/tmp/clamscan_tmp" required by clamscan to store temporary files during the virus scan&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/tmp/files_to_scan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/tmp/clamscan_tmp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;S3CreateEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bucketName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;objectKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getObjectCommand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;objectKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s3Object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getObjectCommand&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s3ObjectContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;transformToString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tmpFilePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`/tmp/files_to_scan/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;objectKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tmpFilePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s3ObjectContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// clamscan CLI documentation:&lt;/span&gt;
    &lt;span class="c1"&gt;// https://linux.die.net/man/1/clamscan&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clamavScan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;spawnSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clamscan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--verbose&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--stdout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`--database=/var/task/lib/database`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`--tempdir=/tmp/clamscan_tmp`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tmpFilePath&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;stdio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pipe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clamavScan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// You can find the return codes here:&lt;/span&gt;
    &lt;span class="c1"&gt;// https://linux.die.net/man/1/clamscan&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clamavScan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no virus found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clamavScan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;virus found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clamavScan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;some error(s) occured in clamscan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;unlink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tmpFilePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We use the top-level await feature and create two folders when the lambda container starts.&lt;/p&gt;

&lt;p&gt;Later, we use &lt;code&gt;spawnSync&lt;/code&gt; to trigger the &lt;code&gt;clamscan&lt;/code&gt; binary installed via the Dockerfile. &lt;/p&gt;

&lt;p&gt;Ensure you use full path definitions in the &lt;code&gt;clamscan&lt;/code&gt; parameters, for example: &lt;code&gt;/var/task/lib/database&lt;/code&gt;, to load the correct virus definitions.&lt;/p&gt;

&lt;p&gt;We can test the ClamAV detection using any &lt;a href="https://www.eicar.org/download-anti-malware-testfile/" rel="noopener noreferrer"&gt;EICAR text files&lt;/a&gt;. The result should look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wkopxa24ofv9d0855l3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wkopxa24ofv9d0855l3.png" alt="Result of lambda handler execution of clamscan"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we have our Dockerfile, ClamAV configuration, and Lambda handler. &lt;/p&gt;

&lt;p&gt;Where do we deploy all of that?&lt;/p&gt;
&lt;h2&gt;
  
  
  The CDK TypeScript Project
&lt;/h2&gt;

&lt;p&gt;Because Docker is building our lambda handler, we create its own &lt;code&gt;package.json&lt;/code&gt; with dependencies:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"clamav-scan"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rimraf dist &amp;amp;&amp;amp; esbuild index.ts --format=esm --outfile=dist/index.mjs"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/aws-lambda"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^8.10.130"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esbuild"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.19.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rimraf"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^5.0.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"typescript"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^5.3.2"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@aws-sdk/client-s3"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.465.0"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Using &lt;code&gt;"type": "module"&lt;/code&gt; will tell TypeScript and Node.js that we are aiming to use ECMAScript Modules in our source code (ESM).&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;build&lt;/code&gt; command asks &lt;code&gt;esbuild&lt;/code&gt; to output our source code in the ESM format with the &lt;code&gt;--format=esm&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;The last piece of the puzzle, is the &lt;code&gt;tsconfig.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"forceConsistentCasingInFileNames"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isolatedModules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NodeNext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NodeNext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noEmit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"preserveConstEnums"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skipLibCheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sourceMap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ESNext"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"node_modules"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Using &lt;code&gt;NodeNext&lt;/code&gt; for &lt;code&gt;moduleResolution&lt;/code&gt; / &lt;code&gt;module&lt;/code&gt; and &lt;code&gt;ESNext&lt;/code&gt; for &lt;code&gt;target&lt;/code&gt;, will tell the TypeScript engine &lt;code&gt;tsc&lt;/code&gt; to output code in ESM format.&lt;/p&gt;

&lt;p&gt;The complete example can be found on GitHub:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/oieduardorabelo" rel="noopener noreferrer"&gt;
        oieduardorabelo
      &lt;/a&gt; / &lt;a href="https://github.com/oieduardorabelo/s3-virus-scanning-typescript-aws-lambda-container" rel="noopener noreferrer"&gt;
        s3-virus-scanning-typescript-aws-lambda-container
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      S3 virus scanning with TypeScript and Node.js 20.x AWS Lambda Container
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;ClamAV 1.2.1 with AWS Lambda Container Images for Node.js 20.x&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;CDK project for deploying a ClamAV 1.2.1 with AWS Lambda Container Images for Node.js 20.x&lt;/p&gt;

&lt;p&gt;This helps you to scan files for viruses using AWS Lambda functions&lt;/p&gt;

&lt;p&gt;🚨 Important:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Virus definitions are updated during build&lt;/li&gt;
&lt;li&gt;Ensure you are building the container regularly to keep your definitions up to date&lt;/li&gt;
&lt;li&gt;You can update the Dockerfile to use a different version of ClamAV&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/ce240c1159d9fdcff2fc81d709f1be9917867fedd11ef3e9c78cceee6c8570db/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f70726163746963616c6465762f696d6167652f66657463682f732d2d5845735368796d5a2d2d2f635f6c696d6974253243665f6175746f253243666c5f70726f6772657373697665253243715f6175746f253243775f3830302f68747470733a2f2f6465762d746f2d75706c6f6164732e73332e616d617a6f6e6177732e636f6d2f75706c6f6164732f61727469636c65732f34666b7a707461736d30666436656d77713976392e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/ce240c1159d9fdcff2fc81d709f1be9917867fedd11ef3e9c78cceee6c8570db/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f70726163746963616c6465762f696d6167652f66657463682f732d2d5845735368796d5a2d2d2f635f6c696d6974253243665f6175746f253243666c5f70726f6772657373697665253243715f6175746f253243775f3830302f68747470733a2f2f6465762d746f2d75706c6f6164732e73332e616d617a6f6e6177732e636f6d2f75706c6f6164732f61727469636c65732f34666b7a707461736d30666436656d77713976392e706e67" alt="Diagram showing the example project: a s3 bucket with object created notification to lambda"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/oieduardorabelo/s3-virus-scanning-typescript-aws-lambda-container" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  🚨 WARNING: You are being rate-limited
&lt;/h2&gt;

&lt;p&gt;This is super important and caught me off guard multiple times. &lt;/p&gt;

&lt;p&gt;Pay attention to the number of viruses your database is using:&lt;/p&gt;

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

&lt;p&gt;During the build process of your Docker image, the ClamAV database mirror can rate limit your IP address and block you from downloading the virus definitions.&lt;/p&gt;

&lt;p&gt;For example, visiting to &lt;a href="https://database.clamav.net/main.cvd" rel="noopener noreferrer"&gt;https://database.clamav.net/main.cvd&lt;/a&gt;, can return the following:&lt;/p&gt;

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

&lt;p&gt;Ensure your &lt;code&gt;freshclam&lt;/code&gt; is downloading and loading the definitions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;daily.cvd&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;main.cvd&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;and &lt;strong&gt;bytecode.cvd&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;By default &lt;code&gt;freshclam&lt;/code&gt; CLI will NOT throw an error when that happens.&lt;/p&gt;

&lt;p&gt;That's why in the Dockerfile we are grepping the log file generated by the CLI and looking for errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"Can't download daily.cvd&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;Can't download main.cvd&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;Can't download bytecode.cvd"&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LOG_FILE_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Unable to download ClamAV database files - your request may be being rate limited"&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we manually throw an error when any of the rate-limiting messages are detected! 🏁&lt;/p&gt;

</description>
      <category>aws</category>
      <category>node</category>
      <category>typescript</category>
      <category>docker</category>
    </item>
    <item>
      <title>Understanding AWS CloudFormation Execution Permissions</title>
      <dc:creator>Eduardo Rabelo</dc:creator>
      <pubDate>Mon, 17 Jul 2023 09:51:12 +0000</pubDate>
      <link>https://forem.com/oieduardorabelo/understanding-aws-cloudformation-execution-permissions-1b8p</link>
      <guid>https://forem.com/oieduardorabelo/understanding-aws-cloudformation-execution-permissions-1b8p</guid>
      <description>&lt;p&gt;Do you know how CloudFormation generates permissions to provision resources in your account?&lt;/p&gt;

&lt;p&gt;In my new article on the AWS Fundamentals blog, we walk through the CloudFormation lifecycle and how you can apply security concepts like the least privileged access in the deployment of your stack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.awsfundamentals.com/aws-cloudformation-execution-permissions"&gt;https://blog.awsfundamentals.com/aws-cloudformation-execution-permissions&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>devops</category>
      <category>cloudformation</category>
    </item>
    <item>
      <title>AWS IAM Roles with AWS CloudFormation</title>
      <dc:creator>Eduardo Rabelo</dc:creator>
      <pubDate>Sun, 09 Jul 2023 22:20:18 +0000</pubDate>
      <link>https://forem.com/oieduardorabelo/aws-iam-roles-with-aws-cloudformation-2kj5</link>
      <guid>https://forem.com/oieduardorabelo/aws-iam-roles-with-aws-cloudformation-2kj5</guid>
      <description>&lt;p&gt;If you are starting with CloudFormation and IAM Roles, I wrote an introductory article on the AWS Fundamentals blog:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.awsfundamentals.com/aws-iam-roles-with-aws-cloudformation"&gt;https://blog.awsfundamentals.com/aws-iam-roles-with-aws-cloudformation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you enjoyed this post &amp;amp; you want to get 𝗯𝗶𝘁𝗲-𝘀𝗶𝘇𝗲𝗱 𝗔𝗪𝗦 𝗰𝗼𝗻𝘁𝗲𝗻𝘁 straight to your inbox, feel free to check out our bi-weekly newsletter 📨&lt;/p&gt;

&lt;p&gt;You can also browse our 𝗽𝗿𝗲𝘃𝗶𝗼𝘂𝘀 𝗶𝘀𝘀𝘂𝗲𝘀 to see what you'll subscribe to 📚&lt;/p&gt;

&lt;p&gt;&lt;a href="https://newsletter.awsfundamentals.com/"&gt;https://newsletter.awsfundamentals.com/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>devops</category>
      <category>cloudformation</category>
    </item>
    <item>
      <title>Parte 03 - Maximize a economia de custos e escalabilidade do DynamoDB com índice secundário otimizado</title>
      <dc:creator>Eduardo Rabelo</dc:creator>
      <pubDate>Wed, 12 Apr 2023 11:34:13 +0000</pubDate>
      <link>https://forem.com/oieduardorabelo/maximize-a-economia-de-custos-e-escalabilidade-do-dynamodb-com-indice-secundario-otimizado-5a4n</link>
      <guid>https://forem.com/oieduardorabelo/maximize-a-economia-de-custos-e-escalabilidade-do-dynamodb-com-indice-secundario-otimizado-5a4n</guid>
      <description>&lt;h1&gt;
  
  
  Créditos
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Escrito originalmente por &lt;a href="https://twitter.com/pj_naylor"&gt;Pete Naylor&lt;/a&gt;, em &lt;a href="https://www.gomomento.com/blog/maximize-cost-savings-and-scalability-with-an-optimized-dynamodb-secondary-index"&gt;Maximize cost savings and scalability with an optimized DynamoDB secondary index&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Entenda projeções e medição de dados. Esqueça a sobrecarga de GSI.
&lt;/h2&gt;

&lt;p&gt;Esta é a terceira parte de uma série de artigos sobre modelagem de dados do DynamoDB, criada principalmente para ajudar os desenvolvedores usando DynamoDB a entender as práticas recomendadas e evitar seguir um caminho lamentável com técnicas equivocadas de "design de tabela única". No episódio anterior - isso é um show agora? - introduzimos índices secundários locais e globais (LSIs e GSIs). &lt;/p&gt;

&lt;p&gt;Hoje, vamos nos aprofundar para entender quais dados da tabela são projetados em um índice secundário e como isso contribui para o consumo de taxa de transferência. Também vamos separar uma das reviravoltas mais recentes no "design de tabela única" — sobrecarga de GSI — e explicar por que geralmente é uma péssima ideia sem nenhum lado positivo real. Preparado? Lá vamos nós!&lt;/p&gt;

&lt;h1&gt;
  
  
  Quais mudanças na tabela base são relevantes para projeção em um índice secundário?
&lt;/h1&gt;

&lt;p&gt;Ao definir um índice secundário, você pode escolher quais atributos serão copiados (projetados) das alterações de itens &lt;strong&gt;relevantes&lt;/strong&gt; na tabela base: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ALL&lt;/code&gt; — todos os atributos no item da tabela base serão copiados para o índice secundário&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;INCLUDE&lt;/code&gt; — somente a lista nomeada de atributos será copiada (e também os atributos de chave primária da tabela base e o índice secundário — estes são sempre incluídos)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;KEYS_ONLY&lt;/code&gt; — somente os atributos de chave primária da tabela base e o índice secundário serão copiados &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;É assim que a medição funciona para índices secundários do DynamoDB: cada gravação feita em sua tabela base será considerada para a projeção de dados em cada um de seus índices secundários. Se a alteração do item na tabela base for &lt;strong&gt;relevante&lt;/strong&gt; para o índice secundário, ela será projetada e haverá consumo da unidade de gravação - medido de acordo com o tamanho do item projetado. Haverá um custo de armazenamento para os dados projetados no índice secundário também. E lembre-se, conforme explicado no primeiro artigo desta série, &lt;code&gt;Query&lt;/code&gt; e &lt;code&gt;Scan&lt;/code&gt; (as únicas operações de leitura disponíveis para índices secundários) agregam o tamanho de todos os itens e, em seguida, arredondam para cima, para avaliar o total da medição da unidade de leitura. Se você mantiver a projeção de seus itens pequena, o índice custará menos e será dimensionado ainda mais antes de enfrentar qualquer tipo de preocupação importante. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Considere a projeção com muito cuidado para seu índice secundário. &lt;code&gt;ALL&lt;/code&gt; parece simples, mas pode ser muito caro e pode inibir a escalabilidade de seu design em geral. &lt;strong&gt;Projete apenas os atributos que você realmente precisa&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Há mais detalhes nesta equação de eficiência para índices secundários. Você provavelmente está se perguntando por que continuo enfatizando a palavra &lt;strong&gt;relevante&lt;/strong&gt; . Bem, é porque é realmente importante! Primeiro, como o DynamoDB oferece flexibilidade de esquema, os atributos definidos como a chave para seu índice secundário não precisam estar presentes em todos os itens de sua tabela (a menos que façam parte da chave primária dessa tabela). Se os atributos da chave do índice não estiverem presentes em um item da tabela, ele não será considerado &lt;strong&gt;relevante&lt;/strong&gt; para projeção no índice. Você pode usar isso para criar um "índice esparso" — um poderoso mecanismo de filtragem — para localizar os dados necessários com o menor custo de armazenamento e taxa de transferência.&lt;/p&gt;

&lt;p&gt;Um exemplo pode ser um banco de dados que armazena detalhes de status de milhões de tarefas - a grande maioria delas sendo concluída. Como podemos tornar as busca de tarefas que estão no status "enviado" mais fácil? Para tarefas que estão no status relevante da busca ("enviado"), crie um atributo que indique isso e adicione um índice secundário que seja definido por esse atributo. A presença do atributo efetivamente se torna um sinalizador - quando a tarefa for concluída, remova este atributo - a tarefa não estará mais visível no índice secundário quando a busca for realizada. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4SCBvaFE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4l7bvfbqec01ybtvbz9c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4SCBvaFE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4l7bvfbqec01ybtvbz9c.png" alt="GSI waiting-jobs-index" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[Nossa tabela base que rastreia o status da tarefa]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c--VAA15--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1c4ytz9x5l58dawta260.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c--VAA15--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1c4ytz9x5l58dawta260.png" alt="GSI waiting-jobs-index-ALL" width="800" height="185"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[Índice secundário esparso que contém apenas tarefas aguardando atribuição]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos imaginar que, como parte do fluxo de trabalho para tarefas com status enviado, o item de tarefa na tabela seja atualizado cinco vezes para adicionar vários detalhes em atributos não-chave. Se esses atributos forem projetados para nosso índice secundário esparso de tarefas enviadas, o índice também precisará ser atualizado cinco vezes, consumindo várias unidades de gravação. Mas nosso índice secundário esparso realmente não tem utilidade para esses atributos - eles não são relevantes para o padrão de acesso que precisamos que o índice atenda. Portanto, escolhemos uma projeção &lt;code&gt;KEYS_ONLY&lt;/code&gt; para o índice — ela mantém baixo o consumo de unidade de leitura, maximiza a escalabilidade de busca de tarefas, economiza dinheiro em armazenamento e evita várias gravações desnecessárias na projeção do índice. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8TpAg4TG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gesd4qupjbtlofkib9mk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8TpAg4TG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gesd4qupjbtlofkib9mk.png" alt="GSI waiting-jobs-index-KEYS_ONLY" width="800" height="185"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[O mesmo índice secundário, agora esparso com projeção KEYS_ONLY. Muito mais eficiente!]&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  O que é "sobrecarga de GSI" e por que é uma má ideia?
&lt;/h1&gt;

&lt;p&gt;Em 2017, as equipes da Amazon estavam trabalhando duro para &lt;a href="https://www.youtube.com/watch?v=qcuH2ikQkaM"&gt;migrar suas cargas de trabalho mais críticas&lt;/a&gt; de bancos de dados relacionais para o DynamoDB. Eles estavam aprendendo a pensar de maneira diferente sobre a modelagem de dados, olhando além da familiar terceira forma normal (3FN) e desnormalizando dados em itens e coleções de itens no DynamoDB. Naquela época, o DynamoDB suportava no máximo 5 GSIs por tabela, limite máximo. Em alguns casos raros, haveria uma equipe com uma tabela que exigia mais de 5 GSIs para cobrir todos os seus padrões de acesso. Para aqueles de nós que trabalham para dar suporte a essas equipes, percebemos que algumas das necessidades de índice podem ser esparsas e não se sobrepõem. Talvez com alguma adaptação instável pudéssemos usar o mesmo GSI para cobrir vários requisitos de padrão de acesso - isso poderia nos ajudar a trapacear no limite do GSI? Sim: era o último recurso, tinha implicações terríveis de eficiência e operabilidade - mas às vezes funcionava. Nós nos referimos a isso como "sobrecarga de GSI".&lt;/p&gt;

&lt;p&gt;Avançando para dezembro de 2018, o DynamoDB &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/12/amazon-dynamodb-increases-the-number-of-global-secondary-indexes-and-projected-index-attributes-you-can-create-per-table/"&gt;aumentou o limite de GSIs&lt;/a&gt; por tabela para 20. Alegria! Não há necessidade do hack feio de sobrecarga a partir de agora... certo? Bem, a equipe de engenharia pensou que sim, mas então surgiram algumas reviravoltas perturbadoras na fábula do "design de tabela única". &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Acontece que, se você fizer coisas não naturais, desnecessárias, complexas e ineficientes para enviar dados completamente não relacionados para apenas uma tabela do DynamoDB sem motivo, acabará tendo que criar mais índices secundários para essa tabela. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Se você estiver projetando dados com o objetivo de ter exatamente uma tabela (em vez de priorizar eficiência, flexibilidade e escalabilidade), é mais provável que você encontre o limite de GSIs por tabela - e então a mesma obsessão de "exatamente uma tabela" gere um desejo equivocado ter exatamente um GSI, o que leva à sobrecarga. Isso cria uma série de problemas. Na verdade, essa técnica não traz nenhum benefício — é uma má ideia. Aqui está o porquê: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Você provavelmente precisará fazer compensações para a projeção de dados que lhe custará dinheiro e prejudicará a escalabilidade do seu design. O menor denominador comum é projetar &lt;code&gt;ALL&lt;/code&gt; e padronizar strings para atributos de chave primária (quando números seriam mais eficientes). Ai!&lt;/li&gt;
&lt;li&gt;Você perde a flexibilidade de poder varrer o índice secundário e recuperar apenas os dados de seu interesse. Essa é uma funcionalidade poderosa que se foi - sem nenhum motivo.&lt;/li&gt;
&lt;li&gt;Uma das grandes vantagens dos GSIs é que você pode excluí-los e recriá-los conforme necessário. Excluir um GSI não custa nada. Se você misturou vários requisitos de índice em um único GSI e em algum momento percebeu que não precisa indexar um de seus padrões, você não pode simplesmente excluir esse GSI. Você terá que (cuidadosamente) voltar para o design da sua tabela e fazer atualizações em massa relativamente caras (e potencialmente arriscadas). Você realmente não quer lidar com isso. &lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Lições importantes
&lt;/h1&gt;

&lt;p&gt;Resumindo, não há benefício na "sobrecarga de GSI" – apenas pontos negativos. Não faça isso! Além disso, se você se preocupa com a otimização de custos e a escalabilidade, considere a &lt;strong&gt;dispersão e a projeção mínima ao projetar índices secundários&lt;/strong&gt; no DynamoDB.&lt;/p&gt;

&lt;p&gt;No próximo artigo desta série, pretendo desenvolver o que aprendemos até agora para explicar onde as coisas deram errado com o "design de tabela única" e por que interpretá-lo exatamente como uma tabela é um erro terrível (que as equipes da Amazon não estão fazendo). &lt;/p&gt;




&lt;p&gt;Se você quiser discutir esse tópico comigo, obter minha opinião sobre uma pergunta de modelagem de dados do DynamoDB que você tem ou sugerir tópicos para eu escrever em artigos futuros, entre em contato comigo no Twitter (&lt;a href="https://twitter.com/pj_naylor"&gt;@pj_naylor&lt;/a&gt;) ou envie-me um [e-mail diretamente]mailto:&lt;a href="mailto:petenaylor@momentohq.com"&gt;petenaylor@momentohq.com&lt;/a&gt;)!&lt;/p&gt;

</description>
      <category>braziliandevs</category>
      <category>aws</category>
      <category>cloud</category>
      <category>dynamodb</category>
    </item>
    <item>
      <title>Parte 02 - Qual tipo de índice secundário do DynamoDB você deve escolher?</title>
      <dc:creator>Eduardo Rabelo</dc:creator>
      <pubDate>Wed, 12 Apr 2023 11:21:47 +0000</pubDate>
      <link>https://forem.com/oieduardorabelo/qual-tipo-de-indice-secundario-do-dynamodb-voce-deve-escolher-3ieg</link>
      <guid>https://forem.com/oieduardorabelo/qual-tipo-de-indice-secundario-do-dynamodb-voce-deve-escolher-3ieg</guid>
      <description>&lt;h2&gt;
  
  
  Créditos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Escrito originalmente por &lt;a href="https://twitter.com/pj_naylor" rel="noopener noreferrer"&gt;Pete Naylor&lt;/a&gt;, em &lt;a href="https://www.gomomento.com/blog/which-flavor-of-dynamodb-secondary-index-should-you-pick" rel="noopener noreferrer"&gt;Which flavor of DynamoDB secondary index should you pick?&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Use o poder dos índices secundários locais e globais.
&lt;/h2&gt;

&lt;p&gt;Este é o segundo artigo de uma série curta sobre modelagem de dados no Amazon DynamoDB (sem os equívocos de "design de tabela única"). Se você não leu o primeiro artigo, eu o encorajo &lt;a href="https://www.gomomento.com/blog/what-really-matters-in-dynamodb-data-modeling" rel="noopener noreferrer"&gt;a dar uma olhada&lt;/a&gt;. Nele, discuto os conceitos mais importantes da modelagem do DynamoDB: &lt;strong&gt;flexibilidade de esquema&lt;/strong&gt; e &lt;strong&gt;coleções de itens&lt;/strong&gt;. Vou us essa base para discutir índices secundários, um dos meus recursos favoritos do DynamoDB. Observe que este artigo assume alguma familiaridade com os principais conceitos do DynamoDB, como partições, itens, tipos de chave primária e tipos de dados. Se você precisa se atualizar sobre eles, aqui está uma &lt;a href="https://www.youtube.com/playlist?list=PLJo-rJlep0EDNtcDeHDMqsXJcuKMcrC5F" rel="noopener noreferrer"&gt;lista de vídeos que eu recomendo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Índices secundários são poderosos! Você pode usá-los para fornecer automaticamente diferentes perspectivas para leituras dos dados em sua tabela. Eles ajudam você a definir e manter relacionamentos adicionais (coleções de itens) dentro dos mesmos dados, permitem que você classifique os dados relacionados com uma dimensão diferente e podem criar filtros incrivelmente eficazes.&lt;/p&gt;

&lt;p&gt;Mas, como qualquer outra ferramenta, é possível utilizar os índices secundários do DynamoDB de um modo ruim.&lt;/p&gt;

&lt;p&gt;Com grandes poderes vêm grandes responsabilidades - e uma grande necessidade de entender suas opções! Neste blog, vou me concentrar em comparar os diferentes tipos de índices secundários e oferecer algumas dicas para escolher entre eles. Deixarei de falar sobre escolhas de projeção de índice secundário e as consequências de custo e escalabilidade para o próximo artigo, além de algumas observações sobre uma tendência recente no uso de índices secundários globais do DynamoDB chamada "sobrecarga" (&lt;em&gt;overloading&lt;/em&gt;) e por que ela deve ser evitada no design das suas tabelas na maioria dos casos.&lt;/p&gt;

&lt;p&gt;Então fique ligado! E segurem seus chapéus - será uma turnê alucinante.&lt;/p&gt;

&lt;h2&gt;
  
  
  Onde vivem os índices secundários? E como eles chegam lá?
&lt;/h2&gt;

&lt;p&gt;Os dados de sua tabela (o índice primário) são projetados em cseus índies secundários pelo DynamoDB com base na definição de índice que você fornece. Você não pode escrever diretamente em um índice secundário, mas ao escrever em um item em sua tabela base, o DynamoDB projetará alterações relevantes em seus índices secundários para você. Existem dois tipos de índice secundário: índices secundários locais (LSIs - Local Secondary Indexes) e índices secundários globais (GSIs - Global Secondary Indexes).&lt;/p&gt;

&lt;h2&gt;
  
  
  Índices secundários locais
&lt;/h2&gt;

&lt;p&gt;Os LSIs residem nas mesmas partições do DynamoDB que a tabela base — eles compartilham o mesmo atributo de chave de partição (mas têm um atributo de chave de classificação diferente) e compartilham a taxa de transferência com a tabela base. Os LSIs são locais porque oferecem uma ordem de classificação diferente para uma coleção de itens dentro da mesma partição. Os LSIs oferecem suporte a &lt;strong&gt;leituras fortemente consistentes após escrever na tabela&lt;/strong&gt; base, se o parâmetro for especificado na hora da solicitação de dados (caso contrário, a consistência eventual padrão é usada).&lt;/p&gt;

&lt;p&gt;Na verdade, os LSIs e as tabelas base compartilham as mesmas coleções de itens — e isso restringe cada coleção de itens a residir em uma única partição. Quando uma tabela tem um ou mais LSIs, cada coleção de itens nunca pode crescer além de aproximadamente 10 GB (todos os dados para o mesmo valor da chave de partição na tabela base e todos os LSIs combinados). A taxa de transferência de leitura e escrita para qualquer coleção de itens é limitada a 3.000 unidades de leitura por segundo e 1.000 unidades de escrita por segundo na tabela e todos os LSIs associados.&lt;/p&gt;

&lt;p&gt;Os LSIs &lt;strong&gt;devem ser definidos quando a tabela é criada&lt;/strong&gt; e não podem ser excluídos sem excluir a tabela base associada. &lt;strong&gt;Pense bem antes de usar LSIs em seu modelo de dados&lt;/strong&gt; - você deve ter um bom motivo (como um requisito válido para leituras fortemente consistentes) e deve saber que nunca terá uma coleção de itens que possa crescer e exigir mais de 10 GB, 3.000 unidades de leitura por segundo ou 1.000 unidades de escrita por segundo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Se você decidir posteriormente que não quer ser limitado pelas propriedades dos LSIs ou não precisa mais de um LSI específico, pode ser necessária uma migração complexa de sua existente tabela para uma tabela de substituição/nova tabela.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Índices secundários globais
&lt;/h2&gt;

&lt;p&gt;Os GSIs são como uma tabela separada — eles podem ter um atributo de chave de partição diferente e têm suas próprias partições e capacidade de taxa de transferência. Eles podem ser criados (com preenchimento/&lt;em&gt;with backfill&lt;/em&gt;) conforme necessário e removidos (sem custo) quando não forem mais necessários. Eles são globais porque permitem que novos relacionamentos (coleções de itens) sejam definidos entre itens em todas as partições da tabela base. As coleções de itens em um GSI podem abranger partições para armazenar mais dados e fornecer maior taxa de transferência (também verdadeiro para a tabela base, desde que não haja LSIs).&lt;/p&gt;

&lt;p&gt;Uma das maiores diferenças entre LSIs e GSIs está em seu comportamento durante a escrita na tabela base. Como a tabela base e quaisquer LSIs compartilham as mesmas partições, quaisquer atualizações nos LSIs são manipuladas atomicamente com a alteração no item da tabela base. Para GSIs, a &lt;strong&gt;alteração é propagada de forma assíncrona&lt;/strong&gt; para uma partição diferente. Isso tem implicações de consistência de leitura após escrita. As leituras de um LSI podem ser solicitadas para serem consistentes, se desejado, mas &lt;strong&gt;uma leitura de um GSI é sempre eventualmente consistente&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Uma consideração adicional para leituras de um GSI é a &lt;a href="https://blog.palantir.com/on-monotonicity-in-relational-databases-and-service-oriented-architecture-90b0a848dd3d" rel="noopener noreferrer"&gt;monotonicidade&lt;/a&gt;. As leituras GSI não são monotônicas. Se você atualizar um item em sua tabela base para incrementar o valor de um atributo de 7 para 8, poderá fazer três leituras sucessivas dos dados projetados em seu GSI e ver o valor como 8 primeiro, depois 7 e, finalmente, de volta para 8. Uma série de leituras para os mesmos dados em um GSI pode retornar resultados que avançam e retrocedem ao longo do tempo. As leituras fortemente consistentes da tabela ou de um LSI são monotônicas.&lt;/p&gt;

&lt;p&gt;Os GSIs são mais flexíveis que os LSIs, e qualquer LSI pode ser facilmente modelado como um GSI. Use LSIs apenas se tiver certeza de que o índice precisará suportar leituras consistentes/monotônicas ou se quiser se beneficiar do recurso de "leitura completa" (&lt;em&gt;read through&lt;/em&gt;) (detalhes sobre isso em um artigo futuro).&lt;/p&gt;

&lt;h2&gt;
  
  
  Propriedades interessantes de chaves de índice secundárias
&lt;/h2&gt;

&lt;p&gt;Em primeiro lugar: o valor da chave de índice não é garantido como único, como a chave primária em sua tabela base. O índice pode ter várias entradas projetadas com os mesmos valores para a chave de partição do índice e a chave de ordenação! A API GetItem não tem suporte para índices secundários porque GetItem implica a leitura de no máximo um item para um determinado valor da chave. Mas em um índice secundário, mesmo quando um valor específico da chave de índice é fornecido, você pode ver muitos itens retornados! Você deve usar Query ou Scan para ler de um LSI ou GSI. LSIs fornecem uma ordenação alternativa (&lt;em&gt;sort key&lt;/em&gt;), GSIs fornecem coleções alternativas e ordenação (opcional).&lt;/p&gt;

&lt;p&gt;Conforme mencionado anteriormente, um LSI deve ter uma chave composta. A tabela base à qual o LSI está anexado também deve ter uma chave composta. A chave de partição do LSI deve ser a mesma da tabela base e o atributo da chave de ordenação deve ser diferente daquele da tabela. Um exemplo simples pode ser uma interface do usuário que lista um conjunto de entradas em formato tabular — as entradas são agrupadas (coleções de itens relacionadas pelo mesmo valor do atributo chave de partição) e ordenadas por um dos valores de seus atributos (a chave de ordenação da tabela base). E se o usuário precisar ordenar o mesmo grupo de entradas por um atributo diferente? É aqui que um LSI pode ajudá-lo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F990c3y1clevgtgtfcze6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F990c3y1clevgtgtfcze6.gif" alt="Um modelo de dados de carrinho de compras usando LSIs para ordernar a lista com um atributo diferente."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GSIs são mais flexíveis: uma chave de partição é necessária, mas pode ser diferente da tabela base - você pode relacionar/agrupar os dados da sua tabela em um atributo diferente! Uma GSI pode ter uma chave simples ou uma chave composta — e o mesmo vale para a tabela base à qual está anexada. Se você não precisa recuperar coleções de itens de seu GSI ordenados (talvez o padrão seja apenas consultar todos os itens que têm um valor comum de chave de partição no índice), não defina uma chave de ordenação - a GSI ainda pode construir coleções de itens para uma busca de dados eficiente. &lt;strong&gt;Definir uma chave de ordenação quando não é necessário pode limitar sua escalabilidade para as coleções de itens.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Detalhando distinções entre tipos de índices secundários
&lt;/h2&gt;

&lt;p&gt;Há muitas nuâncias aqui, mas na verdade se resumem a algumas diferenças simples, então fiz uma folha de dicas que você pode usar na próxima vez que estiver considerando suas opções de índice secundário.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbyc4o2ybp1nupk2o5j5t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbyc4o2ybp1nupk2o5j5t.png" alt="Tabela mostrando as nuâncias dos tipos de índices secundários do DynamoDB"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Fique de olho no próximo artigo desta série de modelagem de dados do DynamoDB (que visa detalhar o "design de tabela única").&lt;/p&gt;

&lt;p&gt;Em artigos futuros: mais sobre indexação secundária e uma discussão resumida sobre onde o "design de tabela única" deu terrivelmente errado.&lt;/p&gt;




&lt;p&gt;Se você quiser discutir esse tópico comigo, obter minha opinião sobre uma pergunta de modelagem de dados do DynamoDB que você tem ou sugerir tópicos para eu escrever em artigos futuros, entre em contato comigo no Twitter (&lt;a href="https://twitter.com/pj_naylor" rel="noopener noreferrer"&gt;@pj_naylor&lt;/a&gt;) ou envie-me um [e-mail diretamente]mailto:&lt;a href="mailto:petenaylor@momentohq.com"&gt;petenaylor@momentohq.com&lt;/a&gt;)!&lt;/p&gt;

</description>
      <category>braziliandevs</category>
      <category>aws</category>
      <category>cloud</category>
      <category>dynamodb</category>
    </item>
    <item>
      <title>Parte 01 - O que realmente importa na modelagem de dados do DynamoDB?</title>
      <dc:creator>Eduardo Rabelo</dc:creator>
      <pubDate>Wed, 12 Apr 2023 11:07:54 +0000</pubDate>
      <link>https://forem.com/oieduardorabelo/o-que-realmente-importa-na-modelagem-de-dados-do-dynamodb-69d</link>
      <guid>https://forem.com/oieduardorabelo/o-que-realmente-importa-na-modelagem-de-dados-do-dynamodb-69d</guid>
      <description>&lt;h2&gt;
  
  
  Créditos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Escrito originalmente por &lt;a href="https://twitter.com/pj_naylor"&gt;Pete Naylor&lt;/a&gt;, em &lt;a href="https://www.gomomento.com/blog/what-really-matters-in-dynamodb-data-modeling"&gt;What really matters in DynamoDB data modeling?&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Spoiler: não é o número de tabelas!
&lt;/h2&gt;

&lt;p&gt;Quando deixei a equipe do DynamoDB alguns meses atrás, decidi que precisava compartilhar o que aprendi sobre como modelar dados para o DynamoDB e operá-lo bem. Durante &lt;strong&gt;meus seis anos trabalhando com clientes internos e externos do DynamoDB na AWS&lt;/strong&gt;, tive a sorte de obter exposição a uma ampla variedade de modelos de dados do DynamoDB. Eu também vi como eles evoluíram na operação: como eles foram dimensionados (ou não) utilização com carga variável, sua flexibilidade para acomodar novos requisitos de aplicativos e se eles se tornaram proibitivos em termos de custo ao longo do tempo.&lt;/p&gt;

&lt;p&gt;Esta é a primeira parte de uma pequena série de artigos que publicarei com o objetivo de corrigir alguns equívocos sobre as práticas recomendadas de modelagem de dados do DynamoDB. Nos últimos anos, recomendações estranhas evoluíram por meio dos rumores das mídias sociais (com a ajuda do marketing da AWS). Vou escrever sobre o mundo real. Darei os mesmos conselhos que a equipe do DynamoDB dá a outras equipes da Amazon para quem o serviço é de missão crítica - as mesmas recomendações de otimização, os mesmos avisos sobre o placebo do "design de tabela única", as mesmas dicas sobre a interpretação de métricas, os mesmos detalhes nas nuâncias de mensuração e medição, e o mesmo foco na excelência operacional.&lt;/p&gt;

&lt;p&gt;Pronto para começar? Nesse primeiro artigo, definirei as bases para a série, compartilhando o molho secreto para o sucesso com o DynamoDB. Reconheço que demorei um pouco para realmente grocar essas coisas. Me siga! Eu conheço alguns atalhos.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que realmente importa se não é tudo sobre o número de tabelas?
&lt;/h2&gt;

&lt;p&gt;Os dois primeiros conceitos que você precisa entender ao aprender modelagem de dados do DynamoDB para levar seu pensamento além dos casos de uso de chave-valor simples são:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Flexibilidade de Esquema&lt;/li&gt;
&lt;li&gt;Coleções de itens&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Flexibilidade de esquema significa que nem todos os itens em uma tabela do DynamoDB exigem a mesma estrutura – cada item pode ter seu próprio conjunto de atributos e tipos de dados – isso abre muitas possibilidades! Na verdade, os únicos atributos para os quais o esquema é obrigatório são os atributos de chave primária — aqueles usados ​​para indexar os dados na tabela. Cada item deve incluir os atributos-chave definidos para a tabela com o tipo de dados correto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Uma coleção de itens é o conjunto de todos os itens que possuem o mesmo valor de atributo que a chave de partição&lt;/strong&gt;; em outras palavras, eles estão todos relacionados pelo valor da chave de partição. Se você estiver familiarizado com bancos de dados relacionais, uma maneira de pensar sobre isso é olhar para uma coleção de itens como sendo um pouco como um &lt;code&gt;JOIN&lt;/code&gt; materializado, onde o valor comum do atributo de chave de partição é algo como uma chave estrangeira. Para otimizar seu modelo de dados do DynamoDB, você deve procurar oportunidades para usar coleções de itens na tabela base ou em índices secundários, especialmente itens que se relacionam e que seriam buscados (seletivamente) juntos. Isso pode parecer óbvio, mas vale a pena deixar claro: &lt;strong&gt;para estar na mesma coleção de itens, os itens também precisam estar na mesma tabela – a coleção de itens é um subconjunto da tabela.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Por exemplo, você pode representar um carrinho de compras como uma coleção de itens, em que o identificador exclusivo do carrinho (Pete's Cart) é o valor da chave de partição e o identificador de cada produto adicionado ao carrinho é o valor da chave de ordenação (&lt;em&gt;sort&lt;/em&gt;). Aparentemente, Pete gosta mais de café do que de vegetais, mas não tanto quanto de chocolates. O número de cada tipo de produto no carrinho é outro atributo (não-chave). A imagem abaixo descreve tal coleção de itens.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OznncuvX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yc7q608eytre9nh7yvv7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OznncuvX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yc7q608eytre9nh7yvv7.png" alt="Um carrinho de compras com itens." width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seu código tem uma definição mais rígida, como a captura de tela do NoSQL Workbench abaixo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X7MKfUU6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7emh6jey0rwjjt0cvgo1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X7MKfUU6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7emh6jey0rwjjt0cvgo1.png" alt="Tela no NoSQL Workbench mostrando a modelagem de dados para um carrinho de compras com Partition Key, Sort Key e Attributes" width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As coleções de itens podem existir no índice primário (a tabela base) e/ou em um índice secundário. Vou me aprofundar nos detalhes dos índices secundários em um artigo futuro, então vamos nos concentrar na tabela base. As coleções de itens na tabela base requerem uma chave primária composta (chave de partição e chave de ordenação). Se não houver índices secundários locais (LSIs), a coleção de itens pode abranger várias partições do DynamoDB — ela não começará dessa forma, mas o DynamoDB dividirá automaticamente a coleção de itens entre as partições para acomodar o crescente volume de dados e, às vezes, ele também pode distribuir a coleção de itens entre as partições para fornecer uma taxa de transferência generalizada ou para isolar itens mais quentes (ou intervalos de itens).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MT9SiRYA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qmernv4byvt4ceu6b7gy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MT9SiRYA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qmernv4byvt4ceu6b7gy.png" alt="Distribuição de caractéres em uma piscina de letras." width="800" height="917"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;É útil pensar em itens e coleções de itens como sendo dois tipos diferentes de registros no DynamoDB. Em uma tabela base com uma chave primária simples (sem chave de ordenação definida), você trabalha inteiramente com itens. Mas se sua tabela tiver uma chave primária composta (chave de partição + chave de ordenação), você trabalhará com coleções de itens. Os itens na coleção são armazenados na ordem dos valores da chave de ordenação e podem ser retornados na ordem descendente ou ascendente.&lt;/p&gt;

&lt;p&gt;Uma coleção de itens é mágica – ela nos permite armazenar e recuperar itens relacionados juntos de forma eficiente. Os itens na coleção podem ter esquemas diferentes (o DynamoDB é flexível, lembra?) — cada um representando uma parte do registro geral da coleção de itens.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Se você tiver um item de 200 KB, atualizar até mesmo uma pequena parte desse item consumirá 200 unidades de escrita. Se, ao invés disso, esse item for armazenado como uma coleção de partes de itens em uma coleção, você poderá atualizar qualquer pequena parte para um consumo mínimo de unidade de escrita. Ei, mas isso é só o começo!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Você pode usar a operação &lt;code&gt;Query&lt;/code&gt; para recuperar todos os itens da coleção ou apenas aqueles com um intervalo específico de valores de chave de ordenação. A especificação para o intervalo é chamada de &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.KeyConditionExpressions"&gt;condição de chave de ordenação (&lt;em&gt;sort key condition&lt;/em&gt;)&lt;/a&gt;. Dentro da coleção de itens, você pode limitar os itens recuperados para serem aqueles com uma chave de ordenação menor que um valor escolhido, ou maior que, ou mesmo entre dois valores.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E8H7fiRl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zzmmvn7m6ele0qk1g6gq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E8H7fiRl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zzmmvn7m6ele0qk1g6gq.png" alt="Ilustração de como o sort key condition funciona em uma coleção de itens." width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para chaves de ordenação em strings, você também pode usar &lt;code&gt;begin_with&lt;/code&gt; (que na verdade é apenas uma variação de &lt;code&gt;between&lt;/code&gt;, se você pensar bem). A aplicação de uma condição de chave de ordenação limita efetivamente o intervalo de itens a serem retornados da coleção de itens. É importante ressaltar que apenas os itens da coleção que correspondem à condição da chave de ordenação serão incluídos na medição das unidades de leitura (&lt;em&gt;read units&lt;/em&gt;). Isso contrasta com uma &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.FilterExpression"&gt;expressão de filtro&lt;/a&gt; que se aplica após a avaliação das unidades de leitura - portanto, &lt;strong&gt;você ainda paga para ler os itens filtrados&lt;/strong&gt;. Quando você recupera vários itens usando &lt;code&gt;Query&lt;/code&gt; (ou &lt;code&gt;Scan&lt;/code&gt;), a medição do consumo da unidade de leitura adiciona os tamanhos de todos os itens e &lt;em&gt;depois&lt;/em&gt; arredonda para o próximo limite de 4KB (ao invés de arredondar para cada item como &lt;code&gt;GetItem&lt;/code&gt; e &lt;code&gt;BatchGetItem&lt;/code&gt; fazem). Assim, as coleções de itens permitem que você &lt;a href="https://aws.amazon.com/blogs/database/use-vertical-partitioning-to-scale-data-efficiently-in-amazon-dynamodb/"&gt;armazene registros muito grandes, atualize partes seletivas a baixo custo e recupere-as com eficiência ideal&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Coleções de itens (&lt;code&gt;Query&lt;/code&gt;) e &lt;code&gt;Scan&lt;/code&gt; são, na verdade, os únicos diferenciadores atraentes de custo e desempenho que defendem o armazenamento de dois itens na mesma tabela. Ambos permitem a recuperação de vários itens com os tamanhos de itens agregados arredondados para o próximo limite de 4 KB. Se você não for indexar dois itens juntos na mesma coleção de itens (tabela ou índice secundário) e não quiser retornar ambos de cada &lt;code&gt;Scan&lt;/code&gt;, não há vantagem em mantê-los na mesma tabela. Mas certamente existem algumas desvantagens que abordarei um pouco mais tarde. Para todas as outras operações de dados (incluindo vários itens como &lt;code&gt;BatchGetItem&lt;/code&gt;, &lt;code&gt;TransactWriteItems&lt;/code&gt;, etc), o DynamoDB não se importa se os itens estão na mesma tabela ou não - você verá a mesma medição de armazenamento/taxa de transferência e o mesmo desempenho de qualquer jeito.&lt;/p&gt;

&lt;p&gt;‍As vezes, para reunir os dados na mesma coleção de itens, você precisa fazer alguns ajustes nos valores da chave primária. Para ser armazenado na mesma tabela, a mesma definição de chave primária deve ser usada. Exemplos disso incluem armazenar um valor numérico como uma string para corresponder ao tipo de dados da chave de partição ou chave de ordenação ou criar um valor exclusivo para a chave de ordenação onde você não necessariamente tem algo importante para armazenar (usando o mesmo valor que o atributo de chave de partição é comum para isso ao criar um item de "metadados" na coleção).&lt;/p&gt;

&lt;p&gt;‍&lt;br&gt;
Imagine uma tabela para armazenar pedidos de clientes para envio. A chave de partição é um identificador de pedido numérico exclusivo (o tipo de dados é número) e você deseja usar uma coleção para armazenar um registro por item de itens do carrinho (identificado por um SKU numérico) e eventos de rastreamento (identificados por um UUID ordenável, como &lt;a href="https://github.com/segmentio/ksuid"&gt;ksuid&lt;/a&gt;) para processamento de pedidos. Você deseja recuperá-los juntos para eficiência porque o padrão mais comum é fornecer uma página de rastreamento com os itens comprados mostrados além dos detalhes de rastreamento. Nesse caso, você precisará definir a chave de ordenação com tipo de dados string e converter valores para armazenar esses SKUs numéricos como strings. Há um custo para isso (porque armazenar um número como uma string consome mais bytes), mas vale a pena para obter o benefício da coleção de itens.&lt;/p&gt;

&lt;p&gt;Como é fácil acomodar essas alterações ao escrever seus itens na tabela, um desenvolvedor pode fazer isso apenas para todos os itens e coleções de itens, certo? E colocá-los todos na mesma tabela? Faria sentido para todos os clientes do DynamoDB se unirem e armazenarem seus dados em uma tabela &lt;em&gt;multi-tenant&lt;/em&gt; gigante? Não, claro que não. Essas técnicas têm um custo e só devem ser usadas quando puderem ser justificadas - para colher os benefícios das coleções de itens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finalizando
&lt;/h2&gt;

&lt;p&gt;A parte valiosa e válida da orientação de "design de tabela única" é simplesmente esta:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a flexibilidade de esquema e as coleções de itens do DynamoDB para otimizar seu modelo de dados para os padrões de acesso que você precisa cobrir.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se você estiver migrando de um banco de dados relacional, provavelmente terminará com menos tabelas do que seu antigo modelo totalmente normalizado. Você provavelmente terá mais de uma tabela e deve usar qualquer número de Índices Secundários Globais (GSIs) necessários para atender aos seus padrões secundários.&lt;/p&gt;

&lt;p&gt;Sim, é isso. Realmente nunca houve necessidade de criar uma nova terminologia. O "design de tabela única" confundiu algumas pessoas e levou muitas outras a um caminho doloroso, complexo e caro (detalhes sobre isso em um próximo artigo) quando foi interpretado literalmente e, em seguida, deturpado como "melhor prática".&lt;/p&gt;

&lt;p&gt;É hora de deixar essa terminologia de lado - ficar com a flexibilidade do esquema e as coleções de itens.&lt;/p&gt;




&lt;p&gt;Se você quiser discutir esse tópico comigo, obter minha opinião sobre uma pergunta de modelagem de dados do DynamoDB que você tem ou sugerir tópicos para eu escrever em artigos futuros, entre em contato comigo no Twitter (&lt;a href="https://twitter.com/pj_naylor"&gt;@pj_naylor&lt;/a&gt;) ou envie-me um [e-mail diretamente]mailto:&lt;a href="mailto:petenaylor@momentohq.com"&gt;petenaylor@momentohq.com&lt;/a&gt;)!&lt;/p&gt;




&lt;p&gt;Fique de olho em artigos futuros em que discutirei as nuâncias de LSIs e GSIs e explicarei por que a "sobrecarga de GSI" é um padrão de modelagem de araque.&lt;/p&gt;




&lt;p&gt;Se você quiser discutir esse tópico comigo, obter minha opinião sobre uma pergunta de modelagem de dados do DynamoDB que você tem ou sugerir tópicos para eu escrever em artigos futuros, entre em contato comigo no Twitter (&lt;a href="https://twitter.com/pj_naylor"&gt;@pj_naylor&lt;/a&gt;) ou envie-me um [e-mail diretamente]mailto:&lt;a href="mailto:petenaylor@momentohq.com"&gt;petenaylor@momentohq.com&lt;/a&gt;)!&lt;/p&gt;




&lt;h2&gt;
  
  
  Apêndice: O que eu saberia sobre o DynamoDB afinal?
&lt;/h2&gt;

&lt;p&gt;Até recentemente, eu trabalhava na AWS. Comecei como gerente técnico de contas em 2016 na equipe de contas que oferece suporte à Amazon.com como cliente corporativo da AWS. Minha área de foco era ajudar a Amazon a atingir algumas metas organizacionais ambiciosas: 1) migrar projetos críticos de bancos de dados transacionais de bancos de dados relacionais tradicionais para bancos de dados distribuídos desenvolvidos especificamente para computação em nuvem (como DynamoDB); 2) cargas transacionais de segundo nível "lift and shift" do Oracle para o Aurora; e 3) mover todo o armazenamento de dados do Oracle para o Redshift. Foi uma experiência fantástica em geral, mas a parte que mais gostei foi testemunhar a redução drástica na carga operacional e as melhorias na disponibilidade e latência que o DynamoDB proporcionou. Trabalhei com centenas de equipes de desenvolvedores da Amazon.com para revisar seus modelos de dados, ensiná-los sobre como gerenciar limites, dimensionamento, alarmes e painéis do DynamoDB. Sentei-me nas salas de guerra para eventos de pico, como o Prime Day, como ponto de contato para qualquer tipo de preocupação relacionada ao DynamoDB que surgisse com o rápido crescimento do tráfego de eventos. Não foi fácil para todos fazer a mudança de paradigma do modelo de dados, mas &lt;a href="https://aws.amazon.com/blogs/aws/migration-complete-amazons-consumer-business-just-turned-off-its-final-oracle-database/"&gt;quando esse programa foi concluído&lt;/a&gt;, todos os desenvolvedores rapidamente passaram a considerar todas as vantagens garantidas do DynamoDB. Eles passaram a dedicar muito mais tempo às coisas que faziam a diferença para seus clientes — ver isso me impressionou profundamente.&lt;/p&gt;

&lt;p&gt;Em 2018, tornei-me um arquiteto de soluções especializado em DynamoDB — parte de uma pequena equipe que trabalha com os maiores clientes corporativos (externos) da AWS. Eu os ajudei a desenvolver modelos de dados DynamoDB eficazes e eficientes para uma ampla variedade de casos de uso e padrões de acesso, e ensinei muitos desenvolvedores a operar bem com o DynamoDB. Meus últimos 18 meses na AWS foram gastos como gerente de produto para o serviço DynamoDB. Também dei centenas de consultorias a engenheiros de várias equipes de serviço da AWS como parte de um programa de "office hours" para o DynamoDB — revisando modelos de dados, fornecendo orientação de arquitetura mais ampla e ensinando sobre como o DynamoDB funciona e os insights revelados pelas métricas.&lt;/p&gt;

&lt;p&gt;Tudo isso é para dizer que já vi muitos casos de uso de produção para o DynamoDB (de centenas de solicitações por dia a milhões de solicitações por segundo) e tenho uma forte noção da maneira como os desenvolvedores (na Amazon.com, dentro da AWS e em muitas outras empresas ao redor do mundo) estão realmente usando o DynamoDB na prática.&lt;/p&gt;

</description>
      <category>braziliandevs</category>
      <category>aws</category>
      <category>cloud</category>
      <category>dynamodb</category>
    </item>
    <item>
      <title>Construindo Arquiteruras Orientadas a Eventos</title>
      <dc:creator>Eduardo Rabelo</dc:creator>
      <pubDate>Tue, 14 Mar 2023 00:11:57 +0000</pubDate>
      <link>https://forem.com/oieduardorabelo/construindo-arquiteruras-orientadas-a-eventos-4pb</link>
      <guid>https://forem.com/oieduardorabelo/construindo-arquiteruras-orientadas-a-eventos-4pb</guid>
      <description>&lt;p&gt;🇧🇷 Tradução completa do &lt;a href="https://serverlessland.com/event-driven-architecture/intro"&gt;Building Event Driven&lt;br&gt;
Architectures&lt;/a&gt; do &lt;a href="https://serverlessland.com/"&gt;Serverless Land&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Essa é uma tradução livre, não oficial, realizada no meu tempo livre - Eduardo Rabelo.&lt;/p&gt;
&lt;h1&gt;
  
  
  Construindo Arquiteruras Orientadas a Eventos
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://serverlessland.com/event-driven-architecture"&gt;As arquiteturas orientadas a eventos&lt;/a&gt; são um estilo de arquitetura que pode ajudá-lo a aumentar a agilidade de criar aplicativos confiáveis ​​e escaláveis. Serviços serverless como EventBridge, Step Functions, SQS, SNS e Lambda têm uma afinidade natural com arquiteturas orientadas a eventos - elas são invocadas por eventos, emitem eventos e possuem recursos nativos para construir com eventos.&lt;/p&gt;

&lt;p&gt;&lt;a href="//./src/01-01-introducao-a-arquitetura-orientada-a-eventos.md"&gt;Saiba mais&lt;/a&gt; sobre arquiteturas orientadas a eventos, incluindo conceitos-chave, práticas recomendadas, serviços da AWS e recursos de introdução.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Qforsdhr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://serverlessland.com/assets/images/eda/event-producer-broker-consumer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Qforsdhr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://serverlessland.com/assets/images/eda/event-producer-broker-consumer.png" alt="Imagem mostrando comunicação do cliente (produtor de eventos) com o agente de eventos (event broker) e os consumidores de eventos" width="801" height="171"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Este guia irá te apresentar a arquiteturas orientadas a eventos. Você vai compreender os padrões das arquiteturas orientadas a eventos e os serviços da AWS usados para implementá-los. Aprenda as práticas recomendadas para criar arquiteturas orientadas a eventos, desde a criação de esquemas de eventos até o tratamento de conceitos como a idempotência. Ao concluir esse guia, você encontrará recursos extras para expandir seu conhecimento em como criar arquiteturas orientadas a eventos na AWS.&lt;/p&gt;
&lt;h1&gt;
  
  
  A tradução completa no GitHub
&lt;/h1&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/oieduardorabelo"&gt;
        oieduardorabelo
      &lt;/a&gt; / &lt;a href="https://github.com/oieduardorabelo/construindo-arquiteruras-orientadas-a-eventos-serverless-land"&gt;
        construindo-arquiteruras-orientadas-a-eventos-serverless-land
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      🇧🇷 Tradução completa do Building Event Driven Architectures do Serverless Land.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;🇧🇷 Tradução completa do &lt;a href="https://serverlessland.com/event-driven-architecture/intro" rel="nofollow"&gt;Building Event Driven
Architectures&lt;/a&gt; do &lt;a href="https://serverlessland.com/" rel="nofollow"&gt;Serverless Land&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Essa é uma tradução livre, não oficial, realizada no meu tempo livre - Eduardo Rabelo.&lt;/p&gt;
&lt;h1&gt;
Construindo Arquiteruras Orientadas a Eventos&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://serverlessland.com/event-driven-architecture" rel="nofollow"&gt;As arquiteturas orientadas a eventos&lt;/a&gt; são um estilo de arquitetura que pode ajudá-lo a aumentar a agilidade de criar aplicativos confiáveis ​​e escaláveis. Serviços serverless como EventBridge, Step Functions, SQS, SNS e Lambda têm uma afinidade natural com arquiteturas orientadas a eventos - elas são invocadas por eventos, emitem eventos e possuem recursos nativos para construir com eventos.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/oieduardorabelo/construindo-arquiteruras-orientadas-a-eventos-serverless-land./src/01-01-introducao-a-arquitetura-orientada-a-eventos.md"&gt;Saiba mais&lt;/a&gt; sobre arquiteturas orientadas a eventos, incluindo conceitos-chave, práticas recomendadas, serviços da AWS e recursos de introdução.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/0ea5387beeffc1149a7bd3a0c0a7f99a36959963f94d35f3a26e86d4395c67ec/68747470733a2f2f7365727665726c6573736c616e642e636f6d2f6173736574732f696d616765732f6564612f6576656e742d70726f64756365722d62726f6b65722d636f6e73756d65722e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/0ea5387beeffc1149a7bd3a0c0a7f99a36959963f94d35f3a26e86d4395c67ec/68747470733a2f2f7365727665726c6573736c616e642e636f6d2f6173736574732f696d616765732f6564612f6576656e742d70726f64756365722d62726f6b65722d636f6e73756d65722e706e67" alt="Imagem mostrando comunicação do cliente (produtor de eventos) com o agente de eventos (event broker) e os consumidores de eventos"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Este guia irá te apresentar a arquiteturas orientadas a eventos. Você vai compreender os padrões das arquiteturas orientadas a eventos e os serviços da AWS usados para implementá-los. Aprenda as práticas recomendadas para criar arquiteturas orientadas a eventos, desde a criação…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/oieduardorabelo/construindo-arquiteruras-orientadas-a-eventos-serverless-land"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>braziliandevs</category>
      <category>cloud</category>
      <category>aws</category>
      <category>serverless</category>
    </item>
    <item>
      <title>10 melhores práticas para nomes de arquivos e buckets no Amazon S3</title>
      <dc:creator>Eduardo Rabelo</dc:creator>
      <pubDate>Mon, 13 Mar 2023 03:44:07 +0000</pubDate>
      <link>https://forem.com/oieduardorabelo/10-melhores-praticas-para-nomes-de-arquivos-e-buckets-no-amazon-s3-20eo</link>
      <guid>https://forem.com/oieduardorabelo/10-melhores-praticas-para-nomes-de-arquivos-e-buckets-no-amazon-s3-20eo</guid>
      <description>&lt;h2&gt;
  
  
  O Amazon S3 é um serviço de armazenamento simples que oferece escalabilidade, disponibilidade de dados, segurança e desempenho que são líderes do setor. Ao nomear seus buckets do S3, siga estas práticas recomendadas para garantir um gerenciamento de dados tranquilo e eficiente.
&lt;/h2&gt;

&lt;p&gt;Amazon S3 é um popular serviço de armazenamento em nuvem fornecido pela Amazon Web Services (AWS). O S3 é um armazenamento de valor-chave simples com o benefício adicional de oferecer baixa latência e alta durabilidade.&lt;/p&gt;

&lt;p&gt;Ao trabalhar com o S3, é importante seguir as práticas recomendadas para nomear objetos armazenados nos buckets. Isso ajudará você a manter seus dados organizados e facilitará a localização do que você está procurando.&lt;/p&gt;

&lt;p&gt;Neste artigo, discutiremos as 10 melhores práticas para nomes de arquivos e buckets no Amazon S3.&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Use uma convenção de nomenclatura consistente
&lt;/h1&gt;

&lt;p&gt;Quando você tem uma convenção de nomenclatura consistente, é muito mais fácil encontrar os arquivos que está procurando. Por exemplo, se todos os seus arquivos de imagem forem nomeados com a data primeiro, seguida por uma descrição da imagem, será muito mais fácil encontrar uma imagem específica do que se todos fossem nomeados aleatoriamente.&lt;/p&gt;

&lt;p&gt;Também é importante usar uma convenção de nomenclatura consistente porque facilita a automatização de tarefas. Por exemplo, se você sabe que todos os seus arquivos de imagem são nomeados da mesma maneira, você pode facilmente escrever um script que faça backup deles automaticamente em outro local.&lt;/p&gt;

&lt;p&gt;Por fim, usar uma convenção de nomenclatura consistente facilita o compartilhamento de arquivos com outras pessoas. Se você sabe que todos os seus arquivos são nomeados da mesma maneira, basta dizer a alguém para ir ao seu site e procurar o nome do arquivo que está procurando. Isso é muito mais fácil do que tentar explicar onde encontrar um arquivo quando os nomes estão por toda parte.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Evite caracteres especiais em nomes de buckets
&lt;/h1&gt;

&lt;p&gt;Quando você cria um bucket, a Amazon atribui automaticamente a ele um endpoint de site. Este é o URL que você usa para acessar o conteúdo do seu bucket quando está usando o recurso de hospedagem de site estático do S3.&lt;/p&gt;

&lt;p&gt;No entanto, se o nome do seu bucket contiver caracteres especiais, como pontos ou hífens, o endpoint do site não funcionará. Isso significa que qualquer pessoa que tentar visitar seu site receberá uma mensagem de erro.&lt;/p&gt;

&lt;p&gt;Para evitar esse problema, certifique-se de que seus nomes de bucket não contenham nenhum caractere especial.&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Não use pontos nos nomes dos buckets
&lt;/h1&gt;

&lt;p&gt;Quando você cria um bucket com um ponto no nome, como &lt;code&gt;"meu.projeto"&lt;/code&gt;, o S3 irá tratá-lo como dois buckets diferentes: &lt;code&gt;"meu"&lt;/code&gt; e &lt;code&gt;"projeto"&lt;/code&gt;. Isso pode causar problemas porque, ao tentar acessar o bucket &lt;code&gt;"meu.projeto"&lt;/code&gt;, você pode ser redirecionado para o bucket &lt;code&gt;"projeto"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Para evitar esse problema, simplesmente não use pontos em seus nomes de bucket.&lt;/p&gt;

&lt;h1&gt;
  
  
  4. Os nomes dos buckets são globalmente únicos
&lt;/h1&gt;

&lt;p&gt;Quando você cria um novo bucket, o Amazon S3 verifica se o nome escolhido está disponível como um subdomínio DNS (Domain Name System). Se o nome estiver disponível, o Amazon S3 atribuirá esse nome ao seu bucket. No entanto, se o nome escolhido não estiver disponível, o Amazon S3 retornará uma mensagem de erro.&lt;/p&gt;

&lt;p&gt;Isso é importante porque significa que você poderia criar inadvertidamente dois buckets com o mesmo nome e esses buckets seriam acessíveis a partir de URLs diferentes. Por exemplo, digamos que você crie um bucket chamado &lt;code&gt;"example-bucket"&lt;/code&gt; na região Leste dos EUA (N. Virgínia, &lt;code&gt;us-east-1&lt;/code&gt;). Em seguida, outra pessoa cria um bucket com o mesmo nome na região da UE (Irlanda, &lt;code&gt;eu-west-1&lt;/code&gt;). Embora sejam dois buckets diferentes, ambos podem ser acessados ​​usando a URL &lt;code&gt;"example-bucket.s3.amazonaws.com"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Para evitar esse problema, escolha um nome único para seu bucket e use o identificador de região ao criar buckets em diferentes regiões. Por exemplo, você pode nomear seu bucket "example-bucket-us-east-1" na região US East (N. Virginia) e "example-bucket-eu-west-1" na região EU (Irlanda).&lt;/p&gt;

&lt;h1&gt;
  
  
  5. Mantenha seus buckets organizados
&lt;/h1&gt;

&lt;p&gt;Quando você tem muitos dados no S3, pode ser difícil acompanhar tudo sem uma estrutura organizacional clara. Ao usar uma convenção de nomenclatura consistente para seus buckets, você pode garantir que todos os seus dados sejam fáceis de localizar e gerenciar.&lt;/p&gt;

&lt;p&gt;Uma boa maneira de organizar seus buckets é usar um prefixo ou sufixo que indique o tipo de dados armazenados em cada bucket. Por exemplo, você pode usar o prefixo &lt;code&gt;"dados-brutos"&lt;/code&gt; (&lt;em&gt;raw-data&lt;/em&gt;) para buckets contendo arquivos não tratados, &lt;code&gt;"dados-processados"&lt;/code&gt; para buckets contendo arquivos de dados processados ​​e assim por diante.&lt;/p&gt;

&lt;p&gt;Ao usar uma convenção de nomenclatura consistente, você pode tornar mais fácil para você e para outras pessoas encontrar e usar os dados armazenados em seus buckets do S3.&lt;/p&gt;

&lt;h1&gt;
  
  
  6. Crie e aplique políticas para o gerenciamento do ciclo de vida de objetos no S3
&lt;/h1&gt;

&lt;p&gt;Os objetos no S3, por padrão, são colocados em uma classe de armazenamento &lt;em&gt;Standard&lt;/em&gt;, o que significa que eles são armazenados indefinidamente, a menos que você especifique uma classe de armazenamento diferente ou exclua o objeto. No entanto, você pode não querer manter todos os objetos do S3 para sempre. Por exemplo, você pode precisar acessar determinados objetos apenas por um tempo limitado, após o qual poderá excluí-los.&lt;/p&gt;

&lt;p&gt;A criação e aplicação de políticas para o gerenciamento do ciclo de vida de objetos no S3 permite que você exclua objetos automaticamente após um determinado período de tempo, o que pode ajudar a economizar nos custos de armazenamento. Ele também pode ajudar a melhorar a segurança, garantindo que objetos antigos e não utilizados não sejam deixados espalhados onde possam ser acessados ​​por usuários não autorizados.&lt;/p&gt;

&lt;p&gt;Para criar e aplicar políticas para o gerenciamento do ciclo de vida do objeto S3, você pode usar o AWS Management Console, os SDKs da AWS ou a AWS Command Line Interface (AWS CLI).&lt;/p&gt;

&lt;h1&gt;
  
  
  7. Habilite o versionamento em todos os buckets
&lt;/h1&gt;

&lt;p&gt;O controle de versão é um recurso crítico do S3 que permite manter várias versões de um objeto no mesmo bucket. Isso é útil por vários motivos, como poder reverter para versões anteriores de um objeto ou recuperar-se de exclusões acidentais.&lt;/p&gt;

&lt;p&gt;Habilitar o controle de versão em um bucket é fácil e leva apenas alguns cliques no console AWS. Depois de ativados, todos os objetos armazenados no bucket terão seu próprio ID de versão única. Esses IDs podem ser usados ​​para recuperar versões específicas de um objeto quando necessário.&lt;/p&gt;

&lt;h1&gt;
  
  
  8. Criptografar dados em repouso (&lt;em&gt;Encrypt data at rest&lt;/em&gt;)
&lt;/h1&gt;

&lt;p&gt;Quando os dados são armazenados no S3, eles são armazenados fisicamente em servidores pertencentes e operados pela Amazon. Embora a Amazon faça um ótimo trabalho protegendo seus servidores, eles não podem garantir que os servidores nunca sejam comprometidos.&lt;/p&gt;

&lt;p&gt;Se os dados forem criptografados em repouso, mesmo que os servidores sejam comprometidos, os dados ainda estarão seguros porque serão ilegíveis sem as chaves de criptografia.&lt;/p&gt;

&lt;p&gt;Há duas maneiras principais de criptografar dados em repouso no S3: criptografia do lado do servidor e criptografia do lado do cliente.&lt;/p&gt;

&lt;p&gt;A criptografia do lado do servidor é quando a Amazon criptografa os dados antes de serem gravados no servidor. Os dados são descriptografados automaticamente quando são lidos do servidor.&lt;/p&gt;

&lt;p&gt;A criptografia do lado do cliente é quando os dados são criptografados pelo cliente antes de serem enviados para a Amazon. Os dados permanecem criptografados enquanto são armazenados no servidor e só são descriptografados quando o cliente os recupera.&lt;/p&gt;

&lt;p&gt;Ambos os métodos são igualmente seguros, mas a criptografia do lado do cliente requer mais trabalho para configurar.&lt;/p&gt;

&lt;h1&gt;
  
  
  9. Monitore e audite o acesso aos seus buckets
&lt;/h1&gt;

&lt;p&gt;Se você não estiver monitorando e auditando o acesso aos seus buckets, não terá como saber quem está acessando seus dados ou o que eles estão fazendo com eles. Isso pode levar a violações de dados, vazamentos de dados ou até mesmo perda de dados.&lt;/p&gt;

&lt;p&gt;Para evitar esses riscos, você deve sempre monitorar e auditar o acesso aos seus buckets do S3. A AWS fornece um serviço chamado CloudTrail que facilita isso. Com o CloudTrail, você pode ver quem acessou seus buckets, o que eles fizeram e quando o fizeram.&lt;/p&gt;

&lt;p&gt;Você também pode usar o CloudTrail para configurar alertas para ser notificado imediatamente se alguém tentar acessar seus buckets sem permissão. Dessa forma, você pode agir rapidamente para evitar qualquer dano.&lt;/p&gt;

&lt;h1&gt;
  
  
  10. Limite o acesso público aos seus buckets
&lt;/h1&gt;

&lt;p&gt;Quando você cria um novo bucket do S3, ele é automaticamente privado. No entanto, existem várias maneiras de alguém tornar seu bucket público sem que você perceba. Por exemplo, se você usar um console do Amazon S3 para fazer upload de arquivos para um bucket e esquecer de definir as permissões, qualquer pessoa poderá acessar esses arquivos.&lt;/p&gt;

&lt;p&gt;Para evitar isso, é importante limitar o acesso público aos seus buckets. Você pode fazer isso configurando uma política de bucket que nega todo o acesso público ou usando Amazon S3 Block Public Access.&lt;/p&gt;

&lt;p&gt;Ambos os métodos ajudarão a garantir que apenas usuários autorizados possam acessar seus buckets S3 e que seus dados estejam seguros.&lt;/p&gt;




&lt;h1&gt;
  
  
  Créditos
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Escrito originalmente por &lt;a href="https://climbtheladder.com/users/1762/profile/daryl-pilkington/"&gt;Janet Maples&lt;/a&gt;, em &lt;a href="https://climbtheladder.com/10-s3-bucket-naming-convention-best-practices/"&gt;10 S3 Naming Convention Best Practices&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>braziliandevs</category>
      <category>cloud</category>
      <category>aws</category>
      <category>devops</category>
    </item>
    <item>
      <title>Como construir uma aplicação WebSocket Serverless</title>
      <dc:creator>Eduardo Rabelo</dc:creator>
      <pubDate>Wed, 08 Feb 2023 22:02:18 +0000</pubDate>
      <link>https://forem.com/oieduardorabelo/como-construir-uma-aplicacao-websocket-serverless-69c</link>
      <guid>https://forem.com/oieduardorabelo/como-construir-uma-aplicacao-websocket-serverless-69c</guid>
      <description>&lt;p&gt;Ao criar aplicativos da web modernos, é cada vez mais importante ser capaz de lidar com dados em tempo real com uma arquitetura orientada a eventos para propagar mensagens para todos os clientes conectados instantaneamente.&lt;/p&gt;

&lt;p&gt;Vários protocolos estão disponíveis, mas o WebSocket é indiscutivelmente o mais usado, pois é otimizado para sobrecarga mínima e baixa latência. O protocolo WebSocket oferece suporte à comunicação bidirecional full-duplex entre cliente e servidor em uma conexão persistente de soquete único. Com uma conexão WebSocket, você pode eliminar o polling e enviar atualizações para um cliente assim que um evento ocorrer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como integrar WebSocket em sua aplicação
&lt;/h2&gt;

&lt;p&gt;Você pode fornecer uma arquitetura orientada a eventos configurando um servidor WebSocket dedicado para os clientes se conectarem e receberem atualizações. No entanto, essa arquitetura tem várias desvantagens, incluindo a necessidade de gerenciar e &lt;a href="https://ably.com/topic/the-challenge-of-scaling-websockets" rel="noopener noreferrer"&gt;escalar o servidor&lt;/a&gt; e a latência inerente no envio de atualizações desse servidor para clientes, que podem ser distribuídos mundialmente.&lt;/p&gt;

&lt;p&gt;Para expandir seu hardware em todo o mundo, você pode delegar a responsabilidade e o custo de hardware ao alugar recursos de um provedor de serviços em nuvem. Por exemplo, você pode usar um serviço de nuvem gerenciado, como o Amazon ECS, ou pode optar por uma solução WebSocket Serverless.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Um aplicativo serverless é aquele que não custa nada para você executar quando ninguém o está usando, excluindo os custos de armazenamento de dados.&lt;/em&gt; - &lt;a href="https://pauldjohnston.medium.com/cloud-2-0-code-is-no-longer-king-serverless-has-dethroned-it-c6dc955db9d5" rel="noopener noreferrer"&gt;Cloud 2.0: Code is no longer King — Serverless has dethroned it&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Economizar nos custos operacionais é um excelente motivo para considerar uma estrutura serverless: você colhe os benefícios de escala e elasticidade, mas não precisa provisionar ou gerenciar os servidores.&lt;/p&gt;

&lt;p&gt;Este artigo explica como criar uma aplicação básica de WebSocket Serverless, ideal para aplicativos simples, como bate-papo, usando o AWS API Gateway para criar um endpoint WebSocket e o AWS Lambda para gerenciamento de conexão e lógica de negócios de back-end.&lt;/p&gt;

&lt;h2&gt;
  
  
  A arquitetura básica de uma aplicação WebSocket Serverless
&lt;/h2&gt;

&lt;p&gt;Construir uma aplicação simples de WebSocket Serverless é relativamente direto, usando o AWS API Gateway para criar APIs WebSocket e funções Lambda para processamento do back-end, armazenando metadados de mensagem no Amazon DynamoDB.&lt;/p&gt;

&lt;h3&gt;
  
  
  API Gateway
&lt;/h3&gt;

&lt;p&gt;O Amazon API Gateway é um serviço para criar e gerenciar APIs. Você pode usá-lo como um proxy com estado para encerrar conexões WebSocket persistentes e transferir dados entre seus clientes e back-ends baseados em HTTP, como AWS Lambda, Amazon Kinesis ou qualquer outro endpoint HTTP.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Lambda
&lt;/h3&gt;

&lt;p&gt;O AWS Lambda é um serviço de computação serverless que permite executar código sem provisionar ou gerenciar servidores.&lt;/p&gt;

&lt;p&gt;As APIs WebSocket podem invocar funções do Lambda, por exemplo, para entregar mensagens para processamento adicional e, em seguida, retornar o resultado ao cliente. As funções do Lambda são executadas apenas pelo tempo necessário para lidar com qualquer tarefa individual. Para autorização de conexões de clientes, a solicitação de conexão deve incluir credenciais a serem verificadas por uma &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html" rel="noopener noreferrer"&gt;função de autorizador do Lambda&lt;/a&gt; que retorna a política AWS IAM apropriada para direitos de acesso.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exemplo: aplicativo de bate-papo com WebSocket Serverless
&lt;/h2&gt;

&lt;p&gt;Como é tradicional, vamos ver um exemplo de aplicativo de bate-papo para ilustrar como a solução WebSocket Serverless alimenta um aplicativo simples de tempo real. Primeiro, considere a sequência pela qual uma conexão WebSocket do cliente é feita e armazenada.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Um dispositivo cliente estabelece uma única conexão WebSocket com o API Gateway. Cada conexão WebSocket ativa tem um URL de retorno individual no API Gateway que é usado para enviar mensagens de volta ao cliente correspondente.&lt;/li&gt;
&lt;li&gt; A mensagem de conexão é enviada para uma função de autoriazação no AWS Lambda do API Gateway para confirmar se o dispositivo possui as credenciais corretas para se conectar ao serviço.&lt;/li&gt;
&lt;li&gt; A mensagem de conexão e os metadados são enviados para um AWS Lambda separado quando ele é autenticado.&lt;/li&gt;
&lt;li&gt; O manipulador Lambda inspeciona os metadados e armazena as informações apropriadas no Amazon DynamoDB, um banco de dados de documentos NoSQL.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Em um aplicativo de bate-papo, os usuários podem se envolver em várias conversas envolvendo um ou mais participantes. Este é um caso de uso típico para o padrão de publicação e assinatura (&lt;em&gt;pub/sub&lt;/em&gt;). Quando um usuário envia uma mensagem para uma conversa ou canal específico, o aplicativo de bate-papo &lt;em&gt;publica&lt;/em&gt; a mensagem para todos os outros usuários que &lt;em&gt;se inscrevem&lt;/em&gt; nesse canal. Cada assinante desse canal recebe a mensagem em seu aplicativo.&lt;/p&gt;

&lt;p&gt;Quando o aplicativo de bate-papo inicia uma conexão WebSocket com o API Gateway, deve haver uma maneira de determinar os canais para os quais essa conexão está autorizada a publicar e se inscrever. O código do cliente deve enviar essas informações dentro dos metadados da conexão.&lt;/p&gt;

&lt;p&gt;Agora vamos considerar o ponto em que o usuário envia uma mensagem e outro usuário a recebe:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Um cliente envia uma mensagem de bate-papo, incluindo os metadados para identificar o canal específico no qual publicar.&lt;/li&gt;
&lt;li&gt; No recebimento, o API Gateway o envia para a função Lambda apropriada.&lt;/li&gt;
&lt;li&gt; Após a chamada, o Lambda verifica as conexões inscritas nesse canal e envia ao API Gateway o ID de cada assinante e a mensagem enviada.&lt;/li&gt;
&lt;li&gt; O API Gateway usa as URLs de retorno para cada conexão de assinante para enviar a mensagem ao cliente da conexão WebSocket estabelecida.&lt;/li&gt;
&lt;li&gt; Após a desconexão, uma função do Lambda é invocada para limpar o ID de conexão do banco de dados.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h2&gt;
  
  
  As limitações dessa implementação simples de WebSocket Serverless
&lt;/h2&gt;

&lt;p&gt;O exemplo acima é um exemplo simplista de um aplicativo orientado a eventos em tempo real usando WebSocket Serverless. Algumas das limitações incluem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O rastreamento do estado da conexão não escala:&lt;/strong&gt; O API Gateway não rastreia metadados de conexão, e é por isso que ele precisa ser armazenado em um banco de dados como o &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html" rel="noopener noreferrer"&gt;Amazon DynamoDB&lt;/a&gt; usando uma função Lambda para atualizar o banco para cada conexão/desconexão. Para um grande número de clientes conectados, você atingiria os limites de escala do Lambda. Ao invés disso, você pode usar a &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-integration-settings.html" rel="noopener noreferrer"&gt;integração nativa do API Gateway para chamar o DynamoDB diretamente&lt;/a&gt;, mas você precisará ficar atento ao alto nível de uso do banco de dados.  &lt;/p&gt;

&lt;p&gt;Outro aspecto que não abordamos é o da desconexão abrupta. Nos casos em que uma conexão do cliente cai sem aviso, a conexão ativa do WebSocket não é devidamente limpa. O banco de dados provavelmente armazenará identificadores de conexão que não estão mais presentes, o que pode causar ineficiências.  &lt;/p&gt;

&lt;p&gt;Uma maneira de evitar conexões zumbis é verificar periodicamente se uma conexão está "ativa" usando um mecanismo de pulsação, como TCP keepalives ou &lt;a href="https://tools.ietf.org/html/rfc6455#page-36" rel="noopener noreferrer"&gt;controle de frames em ping/pong&lt;/a&gt;. O envio de pulsações afeta negativamente a escalabilidade e a confiabilidade do sistema, especialmente ao lidar com milhões de conexões WebSocket simultâneas, pois cria uma carga extra no sistema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Você não pode transmitir mensagens para todos os clientes conectados:&lt;/strong&gt; Neste design simples, não há como enviar uma mensagem simultaneamente para várias conexões, como seria de esperar de um canal ou tópico pub/sub padrão. Distribuir uma atualização publicando-a para milhares de clientes exigiria que você buscasse os IDs de conexão do banco de dados e, em seguida, fizesse uma chamada de API para cada um, o que não é uma abordagem escalável. Como alternativa, você pode considerar o Amazon SNS para adicionar pub/sub ao seu aplicativo, mas ainda há limitações e a desvantagem da complexidade adicional.  Para um exemplo de aplicativo de bate-papo, você pode supor que os canais típicos terão um número baixo de inscritos, a menos que sejam usados em sessões interativas de transmissão ao vivo com milhares de usuários. Outros cenários que podem tolerar a falta de transmissão incluem notificações de atualização individuais e recursos de interatividade que funcionam item por item. Os casos de uso de &lt;em&gt;fan-out&lt;/em&gt; de dados ao vivo não podem suportar essa limitação se houver um grande número de assinantes em um canal para receber o placar esportivo ou atualização de notícias mais recente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Há um limite para o número de conexões WebSocket permitidas por segundo:&lt;/strong&gt; o AWS API Gateway define um limite por região de &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/limits.html" rel="noopener noreferrer"&gt;500 novas conexões por segundo por conta&lt;/a&gt; e 10.000 conexões simultâneas. Isso pode ser insuficiente se você planeja escalar seu aplicativo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A aplicação está vinculada a uma única região da AWS:&lt;/strong&gt; As conexões WebSocket oferecidas pelo API Gateway estão vinculadas a uma única região, resultando em baixo desempenho devido à latência para clientes distantes geograficamente dessa região. É possível usar o &lt;a href="https://ably.zoom.us/j/87643486944?pwd=ZEh1MzgxQUFOKytQSEFrbUNyS2UxUT09" rel="noopener noreferrer"&gt;Amazon EventBridge&lt;/a&gt;, um &lt;em&gt;event bus&lt;/em&gt; serverless, para roteamento de eventos entre regiões para replicar eventos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Desafios da construção de uma aplicação WebSocket Serverless pronta para produção
&lt;/h2&gt;

&lt;p&gt;A arquitetura acima tem potencial se usada para criar uma aplicação simples de mensagens em tempo real, embora já tenhamos visto que ela tem sérias limitações em escala. Se você precisar de um sistema pronto para produção, precisará escala para milhões de conexões, mas também precisará considerar a confiabilidade de sua solução em termos de desempenho, integridade da entrega de mensagens, tolerância a falhas e suporte de fallback, todos os quais podem ser complexos e demorados. consumindo tempo para resolver.&lt;/p&gt;

&lt;h3&gt;
  
  
  Desempenho
&lt;/h3&gt;

&lt;p&gt;Em um sistema distribuído, a latência se deteriora à medida que avança, portanto, os dados devem ser mantidos o mais próximo possível dos usuários por meio de datacenters gerenciados e &lt;a href="https://ably.com/blog/what-is-edge-messaging" rel="noopener noreferrer"&gt;edge acceleration points&lt;/a&gt; . No entanto, não é suficiente para minimizar a latência. A experiência do usuário precisa que a variação de latência seja mínima para garantir a previsibilidade.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integridade da mensagem
&lt;/h3&gt;

&lt;p&gt;Quando a conexão de um usuário cai e se reconecta, o aplicativo precisa continuar com o mínimo de atrito do ponto antes de ser desconectado. Quaisquer mensagens perdidas precisam ser entregues sem duplicar as já processadas. Toda a experiência precisa ser &lt;a href="https://ably.com/blog/achieving-exactly-once-message-processing-with-ably" rel="noopener noreferrer"&gt;totalmente integrada&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tolerância a falhas e escalabilidade
&lt;/h3&gt;

&lt;p&gt;A demanda por um serviço pode ser imprevisível e é um desafio planejar e fornecer capacidade suficiente. Uma solução em tempo real deve estar altamente disponível em todos os momentos para suportar picos de dados. O desafio é escalar horizontalmente e absorver rapidamente milhões de conexões sem a necessidade de pré-provisionamento.&lt;/p&gt;

&lt;p&gt;Sua solução também deve ser &lt;a href="https://ably.com/blog/engineering-dependability-and-fault-tolerance-in-a-distributed-system" rel="noopener noreferrer"&gt;tolerante&lt;/a&gt; a falhas para continuar operando mesmo se um componente falhar. É necessário haver vários componentes capazes de manter o sistema se alguns forem perdidos. Não deve haver nenhum ponto único de congestionamento e nenhum ponto único de falha.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conexões alternativas
&lt;/h3&gt;

&lt;p&gt;Apesar do amplo suporte, o protocolo WebSocket não é suportado por todos os proxies ou navegadores, e alguns firewalls corporativos até bloqueiam portas específicas, o que pode afetar o acesso ao WebSocket. Você pode considerar oferecer suporte a conexões alternativas, como streaming XHR, XHR polling ou long-polling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Desvio de recursos
&lt;/h3&gt;

&lt;p&gt;Outro desafio para o uso de WebSocket Serverless puro em um sistema pronto para produção pode se apresentar apenas depois de usá-lo com sucesso na primeira iteração do produto. Embora você tenha adicionado o recurso essencial para receber atualizações em tempo real, o aumento de recursos geralmente significa que os recursos geram requisitos adicionais para experiências ao vivo compartilhadas e recursos colaborativos.&lt;/p&gt;

&lt;p&gt;Construir e manter uma solução proprietária de WebSocket para suportar as futuras necessidades em tempo real de um produto pode ser um desafio. A infraestrutura que sustenta o sistema deve ser estável e confiável e requer engenheiros experientes para construí-la e mantê-la. Uma equipe de desenvolvimento pode descobrir que tem um compromisso de longo prazo para oferecer suporte à solução em tempo real, em vez de se concentrar nos recursos que aumentam o produto principal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ably: um WebSocket PaaS sem servidor criado para escala
&lt;/h2&gt;

&lt;p&gt;Adotar uma solução WebSocket Serverless de um fornecedor terceirizado faz mais sentido financeiro para muitas organizações. Caso contrário, você pode gastar vários meses e muito dinheiro. Você acabaria sobrecarregado com o alto custo de propriedade da infraestrutura, dívida técnica e demandas contínuas de investimento em engenharia.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.ably.com" rel="noopener noreferrer"&gt;O Ably&lt;/a&gt; possui infraestrutura global elástica tolerante a falhas e altamente disponível para escalabilidade sem esforço e baixa complexidade. Nossa plataforma WebSocket Serverless é modelada matematicamente em torno &lt;a href="https://ably.com/four-pillars-of-dependability" rel="noopener noreferrer"&gt;dos Quatro Pilares de Confiabilidade&lt;/a&gt;. Podemos garantir que as mensagens sejam entregues com baixa latência em uma rede de ponta segura, confiável e global. Não há sobrecarga de DevOps e não há infraestrutura para gerenciar.&lt;/p&gt;

&lt;p&gt;A plataforma Ably abstrai a preocupação de construir uma solução em tempo real para que você possa priorizar o roadmap do seu produto e desenvolver recursos para se manter competitivo.&lt;/p&gt;

&lt;p&gt;Embora o protocolo nativo de Ably seja o WebSocket, raramente existe um protocolo de tamanho único: protocolos diferentes atendem a propósitos diferentes melhor do que outros. O Ably oferece vários &lt;a href="https://ably.com/protocols" rel="noopener noreferrer"&gt;protocolos&lt;/a&gt; , como WebSocket, MQTT, SSE e HTTP puro.&lt;/p&gt;

&lt;p&gt;Para oferecer suporte a experiências ao vivo, Ably adiciona recursos como &lt;a href="https://ably.com/docs/core-features/presence" rel="noopener noreferrer"&gt;presença de dispositivo&lt;/a&gt; , &lt;a href="https://ably.com/docs/realtime/history" rel="noopener noreferrer"&gt;histórico de transmissão&lt;/a&gt; , &lt;a href="https://ably.com/docs/realtime/channels/channel-parameters/rewind" rel="noopener noreferrer"&gt;rebobinar&lt;/a&gt; canais e tratamento de &lt;a href="https://ably.com/docs/realtime/connection#connection-state-recovery" rel="noopener noreferrer"&gt;desconexões abruptas&lt;/a&gt;, facilitando a criação de aplicativos avançados de tempo real. Há uma variedade de integrações de webhook para acionar a lógica de negócios em tempo real. Oferecemos um gateway para funções serverless de seus provedores de serviços de nuvem preferidos. Você pode implantar as melhores ferramentas da categoria em toda a sua pilha e criar aplicativos orientados a eventos usando os ecossistemas nos quais já investiu.&lt;/p&gt;

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

&lt;p&gt;Ably permite que você ofereça uma experiência ao vivo sem atrasos, custos descontrolados ou usuários insatisfeitos. O uso do Ably permite que suas equipes de engenharia se concentrem na inovação do produto principal sem precisar provisionar e manter a infraestrutura em tempo real. &lt;a href="https://ably.com/case-studies" rel="noopener noreferrer"&gt;Nossos clientes existentes&lt;/a&gt; já criam aplicativos em tempo real massivamente escaláveis, com entrada rápida no mercado e economia média de US$ 500.000 no custo de construção e manutenção.&lt;/p&gt;

&lt;p&gt;Faça o download do nosso relatório &lt;a href="https://ably.com/resources/reports/the-state-of-serverless-websocket-infrastructure" rel="noopener noreferrer"&gt;"O estado da infraestrutura WebSocket Serverless"&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Neste artigo, revisamos a arquitetura de uma plataforma simples de WebSocket Serverless usando o Amazon API Gateway e o AWS Lambda. Também consideramos algumas das limitações desse design, como falta de suporte para transmissão de mensagens em larga escala, deploy de região única e gerenciamento de conexão.&lt;/p&gt;

&lt;p&gt;Apresentamos a plataforma WebSocket Serverless da Ably, que lida de forma confiável com a distribuição de dados em tempo real em alta escala para aplicativos móveis e da web.&lt;/p&gt;

&lt;p&gt;Você &lt;a href="https://www.ably.io/contact?__hstc=12655464.d4d5789854ba0058155a69ee37650d08.1632475645006.1661951951785.1661956486878.594&amp;amp;__hssc=12655464.13.1661956486878&amp;amp;__hsfp=2252184059" rel="noopener noreferrer"&gt;pode entrar em contato&lt;/a&gt; para saber como podemos trabalhar com você!&lt;/p&gt;




&lt;h1&gt;
  
  
  Créditos
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Escrito originalmente por &lt;a href="https://ably.com/blog/author/jo-stichbury" rel="noopener noreferrer"&gt;Jo Stichbury&lt;/a&gt;, em &lt;a href="https://ably.com/blog/how-to-build-a-serverless-websocket-platform" rel="noopener noreferrer"&gt;How to build a serverless WebSockets platform&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>codenewbie</category>
      <category>help</category>
    </item>
    <item>
      <title>Um guia prático para testar o AWS Step Functions</title>
      <dc:creator>Eduardo Rabelo</dc:creator>
      <pubDate>Tue, 07 Feb 2023 06:50:44 +0000</pubDate>
      <link>https://forem.com/oieduardorabelo/um-guia-pratico-para-testar-o-aws-step-functions-25nj</link>
      <guid>https://forem.com/oieduardorabelo/um-guia-pratico-para-testar-o-aws-step-functions-25nj</guid>
      <description>&lt;p&gt;Testar Step Functions pode ser uma tarefa assustadora. No entanto, com um pouco de preparação e esforço, o processo de teste pode ser simplificado e otimizado. Neste artigo, forneceremos uma estratégia prática sobre como testar Step Functions. Então, vamos começar preparando o cenário e apresentando os jogadores.&lt;/p&gt;

&lt;h1&gt;
  
  
  O que torna o Step Functions difícil de testar?
&lt;/h1&gt;

&lt;p&gt;As Step Functions são complicadas por natureza porque consistem em muitas partes diferentes que precisam ser testadas individualmente.&lt;/p&gt;

&lt;p&gt;Por exemplo, uma máquina de estado típica do Step Functions pode incluir ramificação condicional que pode direcionar a execução para diferentes caminhos. Todos esses caminhos precisam ser testados para que possamos ter certeza de que estão funcionando conforme o esperado.&lt;/p&gt;

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

&lt;p&gt;Também podemos incorporar a lógica &lt;code&gt;try..catch&lt;/code&gt; em nossa máquina de estado para nos ajudar a recuperar de erros que possam ocorrer em tempo de execução. Essa lógica também precisa ser testada minuciosamente para garantir que funcione corretamente.&lt;/p&gt;

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

&lt;p&gt;Outra coisa importante a observar é que o Step Functions funciona em um ambiente altamente distribuído. Podemos usar as funções do Lambda para executar a lógica de negócios personalizada, bem como integrar diretamente com mais de 200 serviços da AWS. Toda essa funcionalidade precisa ser testada minuciosamente para garantir que ela se comporte adequadamente quando lançarmos nosso aplicativo em produção.&lt;/p&gt;

&lt;p&gt;Além do mais, você pode usar &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/callback-task-sample-sqs.html" rel="noopener noreferrer"&gt;padrões de retorno&lt;/a&gt; de chamada para suspender a execução enquanto esperamos que os processos externos sejam concluídos com êxito antes de prosseguir com nosso caminho de execução.&lt;/p&gt;

&lt;p&gt;Esse padrão também pode ser muito útil para integrar etapas manuais em um processo automatizado. Por exemplo, para permitir que um operador humano aprove ou rejeite uma solicitação para implantar nosso aplicativo depois que o commit for enviado ao GitHub.&lt;/p&gt;

&lt;p&gt;Finalmente, você tem os estados &lt;code&gt;Wait&lt;/code&gt;. O que pode adicionar uma quantidade configurável de atraso à execução e dificultar o teste de ponta a ponta. Por exemplo, se houver um estado de espera que espere por uma hora, seu teste também precisaria dormir por tanto tempo? E você ficaria feliz em esperar uma hora para executar todos os casos de teste em sua suíte de testes? Provavelmente não!&lt;/p&gt;

&lt;p&gt;Todos esses fatores tornam o Step Functions notoriamente difícil de testar. Mas existem maneiras de superar esses desafios (pelo menos até certo ponto) e produzir casos de teste de alta qualidade.&lt;/p&gt;

&lt;h1&gt;
  
  
  Testando com Step Functions Local
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html" rel="noopener noreferrer"&gt;Step Functions Local&lt;/a&gt; é um simulador local para Step Functions e pode executar nossas máquinas de estado localmente. Eu geralmente evito simuladores locais (como &lt;a href="https://localstack.cloud/" rel="noopener noreferrer"&gt;localstack&lt;/a&gt;) porque eles geralmente são mais problemáticos do que valem a pena. No entanto, abro uma exceção para Step Functions Local porque sua capacidade de simulação é quase uma necessidade se você deseja obter uma boa cobertura de teste para Step Functions.&lt;/p&gt;

&lt;p&gt;Por padrão, o Step Functions Local executa os estados &lt;code&gt;Task&lt;/code&gt; contra os serviços reais da AWS (por exemplo, S3 ou DynamoDB). Podemos substituir os endpoints dos serviços AWS na configuração do Step Functions Local.&lt;/p&gt;

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

&lt;p&gt;Usando esse recurso, podemos substituir as chamadas de serviços por simulações locais durante a execução de nossos testes. Embora isso possa parecer útil, apenas um pequeno número de serviços é suportado e isso significaria que teríamos que usar outras ferramentas para simular esses serviços da AWS.&lt;/p&gt;

&lt;p&gt;O mais importante, o Step Functions Local nos permite mockar a saída dos estados &lt;code&gt;Task&lt;/code&gt;. Essas respostas simuladas devem ser fornecidas quando você inicia o Step Functions Local. Mas podemos usá-las para ajudar a conduzir a execução da máquina de estado por um caminho específico que queremos testar. Por exemplo, lançando o erro certo em um estado &lt;code&gt;Tasks&lt;/code&gt; para nos ajudar a testar o caminho de erro em nossa máquina de estado. Ou fornecendo a saída certa de um estado &lt;code&gt;Task&lt;/code&gt; que alimenta um estado &lt;code&gt;Choice&lt;/code&gt; para ter certeza de que seguiríamos o ramo certo.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  Problemas com Step Functions Local
&lt;/h1&gt;

&lt;p&gt;Embora seja uma ferramenta útil e que você deve ter em seu kit, ainda existem alguns problemas notáveis ​​com ela.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Não há garantia de que as chamadas simuladas funcionarão exatamente da mesma maneira que na produção. Este é um problema com todas as simulações e simuladores locais. Que eles não são uma réplica perfeita do ambiente de execução real e são propensos a apresentar atrasos e falsos negativos ou positivos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ele não simula o IAM e, portanto, não pode nos ajudar a detectar erros relacionados à permissão em nossa máquina de estado. Isso é importante, especialmente em uma máquina de estado complexa que interage com muitos serviços da AWS por meio de integrações diretas de serviço.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Não suporta a funcionalidade de avanço rápido através de um estado &lt;code&gt;Wait&lt;/code&gt; ou simulando um tempo esgotado nos estados &lt;code&gt;Task&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Por último e talvez o mais importante, ele não oferece suporte a referências do CloudFormation. Quando você cria uma máquina de estado no Step Functions Local, o ASL deve usar ARNs ao invés de referências do CloudFormation porque a máquina de estado não é implantada como parte de uma pilha do CloudFormation. Isso significa que você não pode usar referências a outros recursos na máquina de estado. Por exemplo, você não poderia fazer referência a um bucket do Amazon S3 se quisesse que sua máquina de estado gravasse dados nele.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Este último ponto cria atrito em seu fluxo de trabalho de desenvolvimento. Isso significa que "testar antes de deployar" não é realmente viável sem primeiro deployar o projeto e criar os recursos referenciados pela máquina de estado.&lt;/p&gt;

&lt;h1&gt;
  
  
  Teste de ponta-a-ponta
&lt;/h1&gt;

&lt;p&gt;Para executar testes de ponta-a-ponta, precisamos deployar o projeto na AWS e criamos uma máquina de estado e todos os recursos aos quais ela faz referência. Em seguida, executaríamos a máquina de estado com diferentes entradas para cobrir diferentes caminhos.&lt;/p&gt;

&lt;p&gt;No entanto, muitas vezes é difícil ou impossível cobrir todos os caminhos de execução usando testes de ponta-a-ponta. Por exemplo, uma lógica de ramificação pode depender do resultado de uma chamada de API para uma API de terceiros, como Stripe ou Paypal. Ou talvez um caminho de erro dependa do DynamoDB lançar um erro. Estes são apenas alguns exemplos de cenários que não podemos cobrir facilmente usando testes de ponta-a-ponta.&lt;/p&gt;

&lt;p&gt;Para alguns desses cenários, podemos usar APIs simuladas e retornar resultados fictícios para nossa lógica de ramificação.&lt;/p&gt;

&lt;p&gt;Por exemplo, você pode usar o &lt;a href="https://www.apidog.com/" rel="noopener noreferrer"&gt;Apidog&lt;/a&gt; para hospedar uma API Stripe simulada para testar o fluxo de pagamento de sua máquina de estado. Você também pode hospedar uma API simulada local e expô-la publicamente usando &lt;a href="https://ngrok.com/" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Em ambos os casos, não estamos realmente interagindo com a API real de terceiros. Portanto, qualquer falha em nossa lógica, isso não afetará nossos clientes, pois eles não receberão um pagamento com falha em um cartão de crédito real. Mas a API simulada nos informará que nossa lógica está falhando quando não deveria.&lt;/p&gt;

&lt;p&gt;No entanto, precisamos de uma maneira de "convencer" nossa máquina de estado a usar a API fictícia em vez da API real de terceiros. Uma maneira de fazer isso é adicionar a URL da API fictícia como variável em tempo de execução, e fazer com que nossas funções do Lambda a usem sempre que for especificada.&lt;/p&gt;

&lt;h1&gt;
  
  
  Teste de componentes para as funções do Lambda
&lt;/h1&gt;

&lt;p&gt;Se uma máquina de estado consistir principalmente de funções Lambda, podemos testar cada função separadamente usando técnicas de teste baseadas em componentes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Encapsule a lógica do domínio em seus próprios módulos e escreva testes de unidade para eles.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Escreva testes de integração (também conhecidos como testes de usuário) que invoquem o código da função Lambda localmente, mas que se comuniquem com os serviços reais da AWS (sem simulações ou simuladores locais!). Esses testes permitem iterar rapidamente no código funcional sem precisar implantá-lo na AWS após cada alteração.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Depois de ganhar confiança com os testes locais, implante tudo na AWS e teste as funções do Lambda como parte dos testes de ponta-a-ponta na máquina de estado.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para saber mais sobre essa abordagem para testar as funções Lambda, confira meu artigo: &lt;a href="https://theburningmonk.com/2022/05/my-testing-strategy-for-serverless-applications" rel="noopener noreferrer"&gt;My strategy for testing serverless applications&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ou, se você quiser um passo a passo mais aprofundado e ver isso em ação, confira meu novo curso &lt;a href="https://testserverlessapps.com/?utm_campaign=practical-guide-to-testing-sfn&amp;amp;utm_source=blog" rel="noopener noreferrer"&gt;"Testing Serverless Architectures"&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Uma estratégia para testar Step Functions
&lt;/h1&gt;

&lt;p&gt;Ok, até agora nós cobrimos os desafios com o teste de Step Functions e apresentamos três abordagens para testá-los:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usando Step Functions Local.&lt;/li&gt;
&lt;li&gt;Usando testes de ponta-a-ponta.&lt;/li&gt;
&lt;li&gt;Teste de componentes em funções individuais do Lambda.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vamos combiná-los para chegar a uma estratégia que nos dê o melhor de todas as três abordagens.&lt;/p&gt;

&lt;p&gt;Primeiro, use o teste de componentes para as funções individuais do Lambda.&lt;/p&gt;

&lt;p&gt;Em seguida, tente cobrir o maior número possível de caminhos de execução usando testes de ponta-a-ponta. No entanto, lembre-se de que os testes de ponta-a-ponta não podem cobrir todos os caminhos de execução possíveis para uma máquina de estado. Ou, no mínimo, será muito desafiador atingir 100% de cobertura com testes de ponta-a-ponta. Portanto, é provável que haja algumas lacunas em nossa cobertura de teste. Por exemplo, alguns caminhos de difícil acesso com &lt;code&gt;Choice&lt;/code&gt; e caminhos de erro.&lt;/p&gt;

&lt;p&gt;Finalmente, use Step Functions Local e respostas simuladas para preencher as lacunas na cobertura dos testes de ponta-a-ponta. Onde não podemos direcionar os testes de ponta-a-ponta para um caso de teste que queremos executar, podemos usar as respostas simuladas no Step Functions Local para direcionar a máquina de estado para esses caminhos de execução.&lt;/p&gt;

&lt;p&gt;Mas espere!&lt;/p&gt;

&lt;p&gt;O objetivo do Step Functions Local não é nos permitir testar nossas máquinas de estado localmente sem fazer o deploy na AWS?&lt;/p&gt;

&lt;p&gt;Na prática, isso é muito difícil de conseguir porque ela não oferece suporte a referências do CloudFormation. Em vez disso, acho que a melhor maneira de usar o Step Functions Local é preencher as lacunas em nossos testes de ponta-a-ponta.&lt;/p&gt;

&lt;p&gt;Neste caso, eu faria:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Faça o deploy da máquina de estado e todos os outros recursos dos quais ela depende na AWS. A máquina de estado deployada conteria os ARNs totalmente qualificados em vez das referências do CloudFormation.&lt;/li&gt;
&lt;li&gt;Inicie o Step Functions Local com as respostas fictícias necessárias para meus casos de teste.&lt;/li&gt;
&lt;li&gt;Crie a máquina de estado no Step Functions Local, usando a definição da máquina de estado que foi deployada.&lt;/li&gt;
&lt;li&gt;Execute casos de teste no Step Functions Local.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A combinação de testes de ponta-a-ponta com o Step Functions Local dessa forma forneceria quase 100% de cobertura de todos os caminhos de execução.&lt;/p&gt;

&lt;p&gt;No entanto, ainda pode haver algumas lacunas deixadas em nossa cobertura de teste. Especificamente, quando estados &lt;code&gt;Wait&lt;/code&gt; e timeouts &lt;code&gt;Task&lt;/code&gt; estão sendo testados. Como não é viável escrever casos de teste que teriam que esperar indefinidamente, e o Step Functions Local não oferece suporte ao avanço rápido por meio desses estados &lt;code&gt;Wait&lt;/code&gt;. A única solução viável que encontrei é usar o Step Functions Local e reescrever o relevante estados &lt;code&gt;Wait&lt;/code&gt; para &lt;em&gt;esperar apenas um segundo&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Podemos fazer isso na etapa 3 acima, quando criamos a máquina de estado no Step Functions Local. O mesmo pode ser feito para diminuir os tempos limite e, portanto, tornar viável o teste dos caminhos de erro.&lt;/p&gt;

&lt;h1&gt;
  
  
  Finalizando
&lt;/h1&gt;

&lt;p&gt;A estratégia que descrevi acima nos dá o melhor dos dois mundos — teste de ponta-a-ponta combinado com a execução local de alguns casos de teste que requerem atenção especial.&lt;/p&gt;

&lt;p&gt;Espero que este post tenha sido útil para você. Se você quiser aprender mais sobre como testar arquiteturas serverless, incluindo Step Functions, verifique meu novo curso &lt;a href="https://testserverlessapps.com/?utm_campaign=widget&amp;amp;utm_source=blog" rel="noopener noreferrer"&gt;"Testing Serverless Architectures"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Se você se inscrever antes de 1º de janeiro de 2023, também poderá obter 30% de desconto com nosso desconto de acesso antecipado! Espero vê-lo no curso :-)&lt;/p&gt;

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




&lt;h1&gt;
  
  
  Créditos
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Escrito originalmente por &lt;a href="https://twitter.com/theburningmonk" rel="noopener noreferrer"&gt;Yan Cui&lt;/a&gt;, em &lt;a href="https://theburningmonk.com/2022/12/a-practical-guide-to-testing-aws-step-functions/" rel="noopener noreferrer"&gt;A practical guide to testing AWS Step Functions&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>Como é o fluxo de dados no Remix</title>
      <dc:creator>Eduardo Rabelo</dc:creator>
      <pubDate>Fri, 03 Feb 2023 08:50:09 +0000</pubDate>
      <link>https://forem.com/oieduardorabelo/como-e-o-fluxo-de-dados-no-remix-1ihc</link>
      <guid>https://forem.com/oieduardorabelo/como-e-o-fluxo-de-dados-no-remix-1ihc</guid>
      <description>&lt;p&gt;Quando o React apareceu pela primeira vez, um de seus recursos mais atraentes era seu "fluxo de dados unidirecional". Isso ainda está descrito nos documentos do React na página &lt;a href="https://reactjs.org/docs/thinking-in-react.html" rel="noopener noreferrer"&gt;"Thinking in React"&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;O componente no topo da hierarquia receberá seus dados como props. Se você fizer uma alteração em seus dados e chamar &lt;code&gt;root.render()&lt;/code&gt; novamente, a UI será atualizada. Você percebe como sua interface do usuário é atualizada e onde fazer alterações são feitas. O fluxo de dados unidirecional (também chamado de ligação unidirecional) do React mantém tudo modular e rápido.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A ideia é que os dados só possam fluir em um sentido através de seu aplicativo, o que, portanto, torna seu aplicativo muito mais fácil de intuir, entender e raciocinar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremix.run%2Fblog-images%2Fposts%2Fremix-data-flow%2Fview-action-state.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremix.run%2Fblog-images%2Fposts%2Fremix-data-flow%2Fview-action-state.png" alt="Ilustração da ideia de fluxo de dados unidirecional representando linhas desenhando um fluxo circular da exibição para a ação e para o estado."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Isso veio a ser resumido na frase: "UI é uma função do estado", ou &lt;code&gt;ui = fn(state)&lt;/code&gt;. Sempre que algum estado muda, devido a uma ação, a interface é renderizada novamente. Até o momento, várias soluções sofisticadas de "gerenciamento de estado" foram criadas para facilitar a construção de aplicativos com esse modelo mental.&lt;/p&gt;

&lt;p&gt;Um problema raramente reconhecido aqui, no entanto, é que esse "fluxo de dados unidirecional" é um pouco impróprio. É realmente um fluxo de dados unidirecional no cliente. Mas ter dados exclusivamente no cliente raramente é prático. Na maioria das vezes, você precisa manter os dados - para sincronizá-los - o que significa que você precisa que os dados fluam de duas maneiras: entre o cliente e o servidor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremix.run%2Fblog-images%2Fposts%2Fremix-data-flow%2Fview-action-state-server-client.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremix.run%2Fblog-images%2Fposts%2Fremix-data-flow%2Fview-action-state-server-client.png" alt="Ilustração do fluxo "&gt;&lt;/a&gt; Ação -&amp;gt; Estado" enquadrado em um navegador à esquerda. À direita está uma ilustração de um servidor com um banco de dados. Duas setas conectam essas duas ilustrações denotando transferência de rede."/&amp;gt;&lt;/p&gt;

&lt;p&gt;Muitas ferramentas de gerenciamento de estado ajudam você a gerenciar o estado apenas no cliente, mas não o ajudam a cruzar efetivamente &lt;a href="https://kentcdodds.com/blog/remix-the-yang-to-react-s-yin" rel="noopener noreferrer"&gt;o abismo da rede&lt;/a&gt;: a lacuna entre o estado no cliente e o estado no servidor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremix.run%2Fblog-images%2Fposts%2Fremix-data-flow%2Fview-action-state-network.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremix.run%2Fblog-images%2Fposts%2Fremix-data-flow%2Fview-action-state-network.png" alt="Ilustração do fluxo “Interface -&amp;gt; Ação -&amp;gt; Estado” enquadrado em um navegador à esquerda. À direita está uma ilustração de um servidor com um banco de dados. Duas setas conectam essas duas ilustrações denotando transferência de rede. A ênfase visual está na parte de rede do gráfico com “?”s ao seu redor."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://remix.run/docs/en/v1/guides/data-loading" rel="noopener noreferrer"&gt;Entra o Remix&lt;/a&gt;: "Um dos principais recursos do Remix é simplificar as interações com o servidor para obter dados em componentes." O Remix estende o fluxo de dados pela rede, tornando-o verdadeiramente unidirecional e cíclico: do servidor (estado), para o cliente (interface) e de volta ao servidor (ação).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremix.run%2Fblog-images%2Fposts%2Fremix-data-flow%2Fview-action-state-server-client-network.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremix.run%2Fblog-images%2Fposts%2Fremix-data-flow%2Fview-action-state-server-client-network.png" alt="Ilustração do fluxo “Interface -&amp;gt; Action -&amp;gt; State” atravessando o navegador e o servidor."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Quando é dizemos que sua “UI é uma função do estado”, uma maneira de desembaraçar as suposições dessa afirmação seria: A UI é uma função do seu estado remoto e do seu estado local. Em um aplicativo React tradicional, todos os estados residem no cliente e as partes que você deseja persistir devem pular para fora do "fluxo de dados unidirecional" e ser sincronizadas na rede para um servidor. Como você pode imaginar, esta é uma área propensa a bugs.&lt;/p&gt;

&lt;p&gt;No Remix, no entanto, a ideia de "UI como uma função do estado" é transformada porque o estado remoto pode ser mais facilmente separado do estado local. "Qual é a diferença", você pergunta? Pense desta maneira.&lt;/p&gt;

&lt;p&gt;O "estado remoto" é qualquer dado que precisa persistir, como dados do usuário. Esse estado (por exemplo, quantas notificações não lidas o usuário tem?) é armazenado no cliente e reconciliado em seu aplicativo a partir de mecanismos Remix, como &lt;a href="https://remix.run/docs/en/v1/guides/data-loading" rel="noopener noreferrer"&gt;&lt;code&gt;loaders&lt;/code&gt;&lt;/a&gt; e &lt;a href="https://remix.run/docs/en/v1/guides/data-writes" rel="noopener noreferrer"&gt;actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;(Observação: o Remix ajuda você a cruzar o abismo da rede, fornecendo informações sobre o estado da transmissão de seus dados usando &lt;a href="https://remix.run/docs/en/v1.5.1/api/remix#usetransition" rel="noopener noreferrer"&gt;&lt;code&gt;transitions&lt;/code&gt;&lt;/a&gt; e &lt;a href="https://remix.run/docs/en/v1/api/remix#usefetcher" rel="noopener noreferrer"&gt;&lt;code&gt;fetchers&lt;/code&gt;&lt;/a&gt; - não há necessidade de rastrear o status de cada solicitação de rede por meio de booleanos como &lt;code&gt;isLoading&lt;/code&gt; ou enums como &lt;code&gt;initial | loading | success | failed&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Por outro lado, o estado local são dados efêmeros que podem ser perdidos (por exemplo, por meio de uma atualização) sem afetar negativamente a experiência do usuário. Esse estado (por exemplo, o menu suspenso aberto que revela as notificações do usuário?) é armazenado no cliente por meio de mecanismos como &lt;code&gt;useState&lt;/code&gt; em React ou &lt;em&gt;local storage&lt;/em&gt; nos navegadores. É importante ressaltar que ele não precisa persistir e sincronizar pela rede (para o servidor), reduzindo assim a complexidade e o potencial de bugs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremix.run%2Fblog-images%2Fposts%2Fremix-data-flow%2Fview-action-state-local-vs-remote.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremix.run%2Fblog-images%2Fposts%2Fremix-data-flow%2Fview-action-state-local-vs-remote.png" alt="Ilustração representando um fluxo unidirecional de “dados remotos” entre o cliente/servidor facilitado pelo Remix. Um fluxo unidirecional de “dados locais” está no cliente facilitado exclusivamente pelo React e/ou localStorage."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Formulários, &lt;em&gt;fetchers&lt;/em&gt;, &lt;em&gt;loaders&lt;/em&gt;, &lt;em&gt;actions&lt;/em&gt;, todos são soluções de “gerenciamento de estado” no Remix (embora não os chamemos assim). Eles fornecem as ferramentas para manter o estado persistente em sincronia entre o cliente e o servidor, garantindo que os dados fluam ciclicamente em um sentido único por meio de seu aplicativo e pela rede: de &lt;em&gt;loaders&lt;/em&gt; a um componente para uma &lt;em&gt;action&lt;/em&gt; e vice-versa.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremix.run%2Fblog-images%2Fposts%2Fremix-data-flow%2Floader-action-component.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremix.run%2Fblog-images%2Fposts%2Fremix-data-flow%2Floader-action-component.png" alt="As palavras “Loader” -&amp;gt; “Action” -&amp;gt; “Component” mostradas em um diagrama circular."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Com o Remix, sua UI se torna uma função de estado em toda a rede, não apenas localmente. &lt;a href="https://discord.com/channels/770287896669978684/770287896669978687/980184501726642186" rel="noopener noreferrer"&gt;Uma analogia interessante&lt;/a&gt; com as abstrações de dados que o Remix fornece, é a abstração do DOM Virtual do React.&lt;/p&gt;

&lt;p&gt;No React, você não se preocupa em atualizar o DOM sozinho. Você define o estado e o DOM Virtual faz toda a diferença para descobrir como fazer atualizações eficientes no DOM. O Remix estende essa idéia para a camada de API para persistir dados.&lt;/p&gt;

&lt;p&gt;No Remix, você não se preocupa em manter o estado do lado do cliente sincronizado com o servidor. Você "define o estado" com uma mutação e os &lt;em&gt;loaders&lt;/em&gt; assumem o controle para buscar novamente os dados mais atualizados e fazer atualizações em seus componentes de interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremix.run%2Fblog-images%2Fposts%2Fremix-data-flow%2Floader-action-component-code.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremix.run%2Fblog-images%2Fposts%2Fremix-data-flow%2Floader-action-component-code.png" alt="Captura de tela do exemplo de código no Remix ilustrando o fluxo cíclico unidirecional de dados por meio de um aplicativo. Há uma função loader cujo código flui para o componente de rota cujo código, por meio de um &amp;lt;Form&amp;gt;, flui para a função action, cujo código flui de volta para uma função loader novamente."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Espero que isso ajude a ilustrar como o Remix ajuda a reduzir drasticamente a quantidade de complexidade necessária para criar sites melhores. Como Kent &lt;a href="https://youtu.be/zED9ePuht4g?t=24852" rel="noopener noreferrer"&gt;disse em sua palestra no RenderATL&lt;/a&gt;, o Remix funciona antes do JavaScript, isso é uma vitória para seus usuários porque eles obtêm uma experiência de aprimoramento progressivo. Mas também é uma vitória para você como desenvolvedor, porque não precisa criar toda a complexidade tradicionalmente associada às soluções de gerenciamento de estado.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Você não precisa se preocupar com o gerenciamento de estados ao usar o Remix. Redux, Apollo, por mais legais que essas ferramentas sejam, você não precisa delas quando estiver usando o Remix porque nem precisamos de JavaScript do lado do cliente para que tudo funcione... pense no aplicativo que você está criando... e vamos fingir que você pode jogar fora todo o código que tem a ver com o gerenciamento de estado do aplicativo... é assim que funciona quando você trabalha com o Remix. Se funcionar sem JavaScript no navegador, isso significa que você não precisa de nada no navegador que exija gerenciamento de estado.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Crédios
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Escrito originalmente por &lt;a href="https://twitter.com/jimniels" rel="noopener noreferrer"&gt;Jim Nielsen&lt;/a&gt;, em &lt;a href="https://remix.run/blog/remix-data-flow" rel="noopener noreferrer"&gt;Data Flow in Remix&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>remix</category>
      <category>webdev</category>
      <category>braziliandevs</category>
    </item>
  </channel>
</rss>
