<?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: Bruno Silva</title>
    <description>The latest articles on Forem by Bruno Silva (@obrusilva).</description>
    <link>https://forem.com/obrusilva</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%2F609236%2F55d12a2c-1901-46e5-bb7a-59599496a564.jpg</url>
      <title>Forem: Bruno Silva</title>
      <link>https://forem.com/obrusilva</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/obrusilva"/>
    <language>en</language>
    <item>
      <title>Migrando do AutoMapper para Mapster em .NET: Segurança, Performance e Custo Zero</title>
      <dc:creator>Bruno Silva</dc:creator>
      <pubDate>Thu, 26 Mar 2026 13:12:26 +0000</pubDate>
      <link>https://forem.com/obrusilva/migrando-do-automapper-para-mapster-em-net-seguranca-performance-e-custo-zero-56eo</link>
      <guid>https://forem.com/obrusilva/migrando-do-automapper-para-mapster-em-net-seguranca-performance-e-custo-zero-56eo</guid>
      <description>&lt;p&gt;Fala, pessoal! 👋&lt;br&gt;&lt;br&gt;
Hoje o papo é sobre uma mudança que muitos times estão enfrentando: a substituição do tradicional AutoMapper, por outro pacote, aqui e eu optei pelo &lt;strong&gt;Mapster&lt;/strong&gt; e vou contar como foi o processo de migração, por que decidimos sair do AutoMapper e como o Mapster pode ser um aliado para manter sua aplicação rápida, segura e, o melhor, sem custos adicionais. Bora conferir? 😉&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;O Cenário: Por que mudar?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;O AutoMapper sempre foi o padrão de mercado, mas o cenário mudou drasticamente por dois motivos principais:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vulnerabilidade Crítica&lt;/strong&gt;: A versão 13 (gratuita) do AutoMapper apresenta vulnerabilidades conhecidas que podem comprometer a segurança da aplicação.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mudança de Licenciamento&lt;/strong&gt;: As novas versões corrigidas e estáveis do AutoMapper passaram a adotar um modelo pago, o que pode inviabilizar a migração para muitos projetos e empresas.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Diante disso, precisávamos de uma alternativa que fosse &lt;strong&gt;open-source (MIT)&lt;/strong&gt;, mantivesse a produtividade do time e, se possível, trouxesse ganhos de performance. Foi aí que o &lt;strong&gt;Mapster&lt;/strong&gt; entrou em cena. 🚀&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Por que Mapster?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;O Mapster não é apenas um "substituto", ele é uma evolução em vários aspectos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  ✅ &lt;strong&gt;Performance Extrema&lt;/strong&gt;: Ele gera código otimizado em tempo de execução (JIT) ou compilação, sendo significativamente mais rápido que o AutoMapper.&lt;/li&gt;
&lt;li&gt;  ✅ &lt;strong&gt;Memória Otimizada&lt;/strong&gt;: Consome menos recursos, o que é vital para microsserviços e APIs de alta escala.&lt;/li&gt;
&lt;li&gt;  ✅ &lt;strong&gt;Sintaxe Familiar&lt;/strong&gt;: Se você já usa AutoMapper, vai se sentir em casa. A curva de aprendizado é baixíssima.&lt;/li&gt;
&lt;li&gt;  ✅ &lt;strong&gt;Sem Herança Obrigatória&lt;/strong&gt;: Você não precisa herdar de classes como &lt;code&gt;Profile&lt;/code&gt; para tudo, embora possa organizar seus mapeamentos de forma similar.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Mão na Massa: Implementação&lt;/strong&gt;
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;1. Instalação dos Pacotes&lt;/strong&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Mapster
dotnet add package Mapster.DependencyInjection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Configurando o Mapeamento (O novo "Profile")&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;No Mapster, utilizamos a interface &lt;code&gt;IRegister&lt;/code&gt;. Veja como fica um mapeamento genérico de um DTO de usuário:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsuarioMapping&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRegister&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypeAdapterConfig&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewConfig&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Usuario&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UsuarioDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&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="n"&gt;dest&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NomeCompleto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;dest&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataCadastro&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreatedAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ignore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SenhaHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Segurança em primeiro lugar!&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AfterMapping&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Lógica customizada pós-mapeamento&lt;/span&gt;
                &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UltimoAcesso&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&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;h3&gt;
  
  
  &lt;strong&gt;3. Registro na Injeção de Dependência&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;No seu &lt;code&gt;Program.cs&lt;/code&gt;, a configuração é muito limpa. Ele faz o scan automático de todos os seus mapeamentos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Registra o Mapster&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TypeAdapterConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GlobalSettings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Assembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetExecutingAssembly&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMapper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ServiceMapper&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Diferenças Práticas: De AutoMapper para Mapster&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Para facilitar a sua vida na hora de converter os mapeamentos, aqui está um "de-para" rápido das operações que mais usamos no dia a dia:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Configuração Inicial&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;No AutoMapper, herdamos de &lt;code&gt;Profile&lt;/code&gt;. No Mapster, implementamos &lt;code&gt;IRegister&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;AutoMapper&lt;/strong&gt;: &lt;code&gt;public class MyProfile : Profile&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Mapster&lt;/strong&gt;: &lt;code&gt;public class MyMapping : IRegister&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Mapeamento de Membros&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A sintaxe muda de &lt;code&gt;.ForMember&lt;/code&gt; para um simples &lt;code&gt;.Map&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AutoMapper&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ForMember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Nome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Mapster&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&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="n"&gt;dest&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Nome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. Ignorar Campos&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Mais direto e sem a necessidade de abrir uma expressão de opção.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AutoMapper&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ForMember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Senha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ignore&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Mapster&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ignore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Senha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;4. Lógica Pós-Mapeamento&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;O &lt;code&gt;AfterMap&lt;/code&gt; vira &lt;code&gt;AfterMapping&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AutoMapper&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AfterMap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Mapster&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AfterMapping&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&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;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;5. Mapeamento de Mão Dupla&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Se você precisa que o mapeamento funcione nos dois sentidos (Src -&amp;gt; Dest e Dest -&amp;gt; Src).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;AutoMapper&lt;/strong&gt;: &lt;code&gt;.ReverseMap()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Mapster&lt;/strong&gt;: &lt;code&gt;.TwoWays()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Uso no Dia a Dia (Services/Controllers)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;O uso continua sendo via injeção de dependência, o que significa que você quase não altera seu código de negócio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PedidoService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IMapper&lt;/span&gt; &lt;span class="n"&gt;_mapper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PedidoService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMapper&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_mapper&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PedidoDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ProcessarPedido&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pedido&lt;/span&gt; &lt;span class="n"&gt;pedido&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// A chamada é idêntica!&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dto&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_mapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PedidoDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;pedido&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Resultados Obtidos&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Após a migração, notamos benefícios imediatos:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Segurança&lt;/strong&gt;: Vulnerabilidades da versão gratuita do AutoMapper eliminadas.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Compliance&lt;/strong&gt;: Sem preocupações com custos inesperados de licenciamento.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Velocidade&lt;/strong&gt;: Redução perceptível no tempo de processamento de mapeamentos complexos em listas grandes.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Mudar de biblioteca nem sempre é uma escolha fácil, mas quando envolve &lt;strong&gt;segurança e sustentabilidade financeira&lt;/strong&gt; do projeto, é uma decisão necessária. O Mapster provou ser uma ferramenta superior em performance e extremamente amigável para quem está vindo do ecossistema AutoMapper.&lt;/p&gt;

&lt;p&gt;Se você está rodando versões antigas do AutoMapper, recomendo fortemente avaliar essa migração. É o tipo de melhoria técnica que "se paga" no primeiro deploy.&lt;/p&gt;

&lt;p&gt;Espero que esse guia ajude vocês nessa jornada! Dúvidas? Deixa nos comentários! 👇&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;#backend #mapping #mapster #dotnet #braziliandevs #performance #security #opensource&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>braziliandevs</category>
      <category>architecture</category>
      <category>performance</category>
    </item>
    <item>
      <title>Cache Multi-Nível com FusionCache em Aplicações .NET 8</title>
      <dc:creator>Bruno Silva</dc:creator>
      <pubDate>Fri, 26 Sep 2025 00:49:56 +0000</pubDate>
      <link>https://forem.com/obrusilva/cache-multi-nivel-com-fusioncache-em-aplicacoes-net-8-4hdo</link>
      <guid>https://forem.com/obrusilva/cache-multi-nivel-com-fusioncache-em-aplicacoes-net-8-4hdo</guid>
      <description>&lt;p&gt;Fala, pessoal! 👋&lt;br&gt;
Hoje falar um papo sobre cache multi-nível e como implementar isso de forma prática usando o FusionCache.&lt;/p&gt;

&lt;p&gt;A ideia é mostrar como deixar sua aplicação mais rápida, resiliente e preparada para lidar com cenários de alta demanda. Bora lá? 😉&lt;/p&gt;

&lt;p&gt;Antes de ir para o código, precisamos entender os conceitos.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;O que é Cache Multi-Nível?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Cache multi-nível é uma arquitetura que combina diferentes tipos de cache para otimizar performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L1 (Level 1)&lt;/strong&gt;: Cache em memória local - ultra-rápido (~0.1ms)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L2 (Level 2)&lt;/strong&gt;: Cache distribuído (Redis/Valkey) - rápido e compartilhado (~1-5ms)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Por exemplo, em APIs podemos usar cache L1 para dados acessados frequentemente e cache L2 para compartilhar dados entre múltiplas instâncias da aplicação.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Por que FusionCache?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;O &lt;strong&gt;FusionCache&lt;/strong&gt; é uma biblioteca .NET que oferece:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Cache Multi-Nível&lt;/strong&gt; (L1 + L2 automático)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Backplane&lt;/strong&gt; (sincronização entre instâncias)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Fail-Safe&lt;/strong&gt; (funciona mesmo se Redis cair)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Cache Stampede Protection&lt;/strong&gt; (evita sobrecarga)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Timeouts Configuráveis&lt;/strong&gt; (performance otimizada)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Cenário de Exemplo&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Vamos imaginar uma API que precisa trabalhar com cache, mas diferenciando entre dois tipos de dados: críticos e simples.&lt;/p&gt;

&lt;p&gt;Mas por que separar assim? 🤔&lt;br&gt;
A ideia é ter mais controle sobre o tempo de vida de cada cache de acordo com a importância da informação e isso era uma necessidade real que eu tinha.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Crítico&lt;/strong&gt;: dados muito requisitados (coisa de 1 milhão de requisições por dia) e que mudam pouco. Aqui vale a pena usar L1 + L2, garantindo performance máxima e resiliência.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simples&lt;/strong&gt;: dados menos importantes, que não precisam ficar tanto tempo em memória. Nesse caso podemos buscar mais vezes no Redis/Valkey, usando principalmente o L2.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Implementação em .NET 8&lt;/strong&gt;
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;1. Instalação dos Pacotes NuGet&lt;/strong&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package ZiggyCreatures.FusionCache
dotnet add package ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson
dotnet add package ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Configuração no Program.cs&lt;/strong&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Configuração do Redis/Valkey&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IConnectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;connectionString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetConnectionString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Redis"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"localhost:6379"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;configurationOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConfigurationOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;configurationOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AbortOnConnectFail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;configurationOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectRetry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;configurationOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectTimeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;configurationOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SyncTimeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ConnectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configurationOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Cache distribuído (L2)&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDistributedCache&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IConnectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RedisCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RedisCacheOptions&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ConnectionMultiplexerFactory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;InstanceName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&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;// FusionCache Crítico (L1 + L2 + Backplane) - Para produtos&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFusionCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CacheCritico"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDefaultEntryOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;FusionCacheEntryOptions&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Duration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;           &lt;span class="c1"&gt;// L1: 10 minutos&lt;/span&gt;
        &lt;span class="n"&gt;DistributedCacheDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// L2: 30 minutos&lt;/span&gt;
        &lt;span class="n"&gt;FactorySoftTimeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMilliseconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;FactoryHardTimeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;IsFailSafeEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;FailSafeMaxDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;FailSafeThrottleDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;30&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="nf"&gt;WithSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FusionCacheNewtonsoftJsonSerializer&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDistributedCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDistributedCache&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithBackplane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RedisBackplane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RedisBackplaneOptions&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetConnectionString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Redis"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="c1"&gt;// FusionCache Simples (microcaching + L2) - Para relatórios&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFusionCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CacheSimples"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDefaultEntryOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;FusionCacheEntryOptions&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Duration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;          &lt;span class="c1"&gt;// L1: 30 segundos (microcaching)&lt;/span&gt;
        &lt;span class="n"&gt;DistributedCacheDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// L2: 60 minutos&lt;/span&gt;
        &lt;span class="n"&gt;FactorySoftTimeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMilliseconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;FactoryHardTimeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;IsFailSafeEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;FailSafeMaxDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;FailSafeThrottleDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;15&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="nf"&gt;WithSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FusionCacheNewtonsoftJsonSerializer&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDistributedCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IDistributedCache&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithBackplane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RedisBackplane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RedisBackplaneOptions&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetConnectionString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Redis"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Aqui estamos registrando o FusionCache e dizendo que ele vai usar tanto memória local quanto Redis como backplane. Simples, né? 😎&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;3. Interface de Cache Personalizada&lt;/strong&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ICacheService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;BuscarAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="n"&gt;CriarAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;expiracao&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;BuscarOuCriarAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;expiracao&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;RemoverAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ExisteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;RemoverPorPrefixoAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;prefixo&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;h3&gt;
  
  
  &lt;strong&gt;4. Implementação do Serviço de Cache&lt;/strong&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FusionCacheService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ICacheService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IFusionCache&lt;/span&gt; &lt;span class="n"&gt;_cacheCritico&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IFusionCache&lt;/span&gt; &lt;span class="n"&gt;_cacheSimples&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IConnectionMultiplexer&lt;/span&gt; &lt;span class="n"&gt;_connectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IDatabase&lt;/span&gt; &lt;span class="n"&gt;_database&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;FusionCacheService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;IFusionCacheProvider&lt;/span&gt; &lt;span class="n"&gt;fusionCacheProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;IConnectionMultiplexer&lt;/span&gt; &lt;span class="n"&gt;connectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_cacheCritico&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fusionCacheProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CacheCritico"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_cacheSimples&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fusionCacheProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CacheSimples"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_connectionMultiplexer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_database&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;GetDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;BuscarAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;EscolherCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chave&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;await&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetOrDefaultAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="n"&gt;CriarAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;expiracao&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;expiracaoMemoria&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;EscolherCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FusionCacheEntryOptions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expiracao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DistributedCacheDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expiracao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expiracaoMemoria&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expiracaoMemoria&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&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;await&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;BuscarOuCriarAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;expiracao&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;expiracaoMemoria&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;EscolherCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FusionCacheEntryOptions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expiracao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DistributedCacheDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expiracao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expiracaoMemoria&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expiracaoMemoria&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&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;await&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetOrSetAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;RemoverAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Remove de ambos os caches (ativa Backplane automaticamente)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cacheCritico&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RemoveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cacheSimples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RemoveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ExisteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;EscolherCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;valor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetOrDefaultAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;valor&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;RemoverPorPrefixoAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;prefixo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_connectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;IsConnected&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_connectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_connectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEndPoints&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"*&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;prefixo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Busca chaves com SCAN e remove via FusionCache (ativa Backplane)&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chaveRedis&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;KeysAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chaveLimpa&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ExtrairChaveFusionCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chaveRedis&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cacheCritico&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RemoveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chaveLimpa&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cacheSimples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RemoveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chaveLimpa&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="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Escolhe qual tipo de cache usar baseado na criticidade dos dados&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IFusionCache&lt;/span&gt; &lt;span class="nf"&gt;EscolherCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Dados críticos: usa cache completo (L1 + L2)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"produto:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"usuario:"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_cacheCritico&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Dados menos críticos: usa microcaching + L2&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_cacheSimples&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Remove prefixo de versão do FusionCache (v0:, v1:, etc.)&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ExtrairChaveFusionCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RedisKey&lt;/span&gt; &lt;span class="n"&gt;chaveRedis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chaveStr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chaveRedis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;indiceDoisPontos&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chaveStr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;':'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;indiceDoisPontos&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;possivelPrefixo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chaveStr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indiceDoisPontos&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Verifica se é prefixo de versão (v + números + :)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;possivelPrefixo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"v"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;possivelPrefixo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EndsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;numeroVersao&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;possivelPrefixo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;possivelPrefixo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numeroVersao&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;All&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDigit&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="n"&gt;chaveStr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;possivelPrefixo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&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;return&lt;/span&gt; &lt;span class="n"&gt;chaveStr&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;h3&gt;
  
  
  &lt;strong&gt;5. Configuração no appsettings.json&lt;/strong&gt;
&lt;/h3&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;"ConnectionStrings"&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;"Redis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"localhost:6379"&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;"Cache"&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;"Tipo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FusionCache"&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;h3&gt;
  
  
  &lt;strong&gt;6. Uso nos Controllers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Agora vamos ver como aplicar estratégias de cache diferentes nos nossos Controllers.&lt;/p&gt;

&lt;p&gt;👉 &lt;em&gt;&lt;strong&gt;Obs: no exemplo vou mostrar direto na Controller só pra facilitar o entendimento.&lt;br&gt;
Mas, seguindo boas práticas, o ideal é ter uma camada de Services responsável pelo acesso aos dados, deixando a Controller apenas como “ponte” entre requisição e regra de negócio.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;No primeiro endpoint temos cache de &lt;strong&gt;produtos críticos&lt;/strong&gt; com L1 + L2.&lt;/p&gt;

&lt;p&gt;No segundo endpoint foi implementado cache de &lt;strong&gt;relatórios&lt;/strong&gt; com microcaching.&lt;/p&gt;

&lt;p&gt;No terceiro endpoint mostramos &lt;strong&gt;remoção por prefixo&lt;/strong&gt; para invalidar categorias inteiras.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Código do Controller:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/[controller]"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProdutosController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ICacheService&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IProdutoRepository&lt;/span&gt; &lt;span class="n"&gt;_produtoRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ProdutosController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ICacheService&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IProdutoRepository&lt;/span&gt; &lt;span class="n"&gt;produtoRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_cache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_produtoRepository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;produtoRepository&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="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;BuscarProduto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Cache crítico - L1 (10min) + L2 (20min)&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;produto&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuscarOuCriarAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;$"produto:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_produtoRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuscarPorIdAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&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;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;produto&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="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"categoria/{categoriaId}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;BuscarProdutosPorCategoria&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;categoriaId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Cache simples - Microcaching (30s) + L2 (60min)&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;produtos&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuscarOuCriarAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;$"categoria:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;categoriaId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_produtoRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuscarPorCategoriaAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;categoriaId&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;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;produtos&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="nf"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;AtualizarProduto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;FromBody&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;Produto&lt;/span&gt; &lt;span class="n"&gt;produto&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Atualiza no banco&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_produtoRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AtualizarAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;produto&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Remove cache específico (ativa Backplane - sincroniza todas as instâncias)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RemoverAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"produto:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Remove caches relacionados por prefixo&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RemoverPorPrefixoAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"produtos:categoria:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;produto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CategoriaId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&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;Ok&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="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"relatorio/vendas"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;RelatorioVendas&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Relatório - Cache simples com TTL longo&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;relatorio&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuscarOuCriarAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"relatorio:vendas:diario"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GerarRelatorioVendas&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;30&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;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relatorio&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="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GerarRelatorioVendas&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Simula processamento pesado&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2000&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;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;TotalVendas&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;15000m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;TotalProdutos&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;150&lt;/span&gt; 
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Principais Benefícios Obtidos&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Performance Dramática&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cache Hit L1&lt;/strong&gt;: ~0.1ms (1000x mais rápido que banco)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Hit L2&lt;/strong&gt;: ~2ms (50x mais rápido que banco)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Miss&lt;/strong&gt;: Tempo normal do banco&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Sincronização Entre Instâncias&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Problema antes do Backplane:&lt;/span&gt;
&lt;span class="c1"&gt;// Instância A: Remove cache às 10:30&lt;/span&gt;
&lt;span class="c1"&gt;// Instância B: Cache válido até 10:35 (5 min desatualizado!)&lt;/span&gt;

&lt;span class="c1"&gt;// Solução com Backplane:&lt;/span&gt;
&lt;span class="c1"&gt;// Instância A: Remove cache às 10:30&lt;/span&gt;
&lt;span class="c1"&gt;// Backplane: Notifica TODAS as instâncias instantaneamente&lt;/span&gt;
&lt;span class="c1"&gt;// Instância B: Remove cache automaticamente às 10:30&lt;/span&gt;
&lt;span class="c1"&gt;// ✅ Todas as instâncias sincronizadas!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Resiliência Total&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Cenário: Redis fica offline&lt;/span&gt;
&lt;span class="c1"&gt;// ✅ L1 (memória) continua funcionando&lt;/span&gt;
&lt;span class="c1"&gt;// ✅ Fail-Safe serve dados em cache mesmo expirados&lt;/span&gt;
&lt;span class="c1"&gt;// ✅ Aplicação NÃO para, NÃO trava&lt;/span&gt;
&lt;span class="c1"&gt;// ✅ Quando Redis volta, reconecta automaticamente&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Configurações Avançadas&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Cache por Tipo de Dados&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IFusionCache&lt;/span&gt; &lt;span class="nf"&gt;EscolherCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Dados críticos: L1 + L2 completo&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"produto:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; 
        &lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"usuario:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; 
        &lt;span class="n"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"config:"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_cacheCritico&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Dados menos críticos: microcaching + L2&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_cacheSimples&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;h3&gt;
  
  
  &lt;strong&gt;TTL Diferenciado&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Cache crítico com TTL otimizado&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CriarAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"produto:123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;produto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;    &lt;span class="c1"&gt;// L2: 30 minutos&lt;/span&gt;
    &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;// L1:5 minutos&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Cache de relatório com TTL longo&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CriarAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"relatorio:vendas"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;relatorio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;    &lt;span class="c1"&gt;// L2: 4 horas&lt;/span&gt;
    &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// L1: 1 Minuto (microcaching)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Remoção Inteligente por Prefixo&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;AtualizarCategoria&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;categoriaId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Atualiza dados no banco&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AtualizarCategoria&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;categoriaId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Remove TODOS os caches relacionados (em TODAS as instâncias)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RemoverPorPrefixoAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"produtos:categoria:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;categoriaId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Próximas consultas já pegam dados atualizados!&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;Exemplo de Teste no Swagger&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Teste 1: Cache Hit&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/produtos/123
Response Time: 0.2ms ← Cache L1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Teste 2: Cache Miss → Cache Hit&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/produtos/456
Response Time: 150ms ← Busca no banco

GET /api/produtos/456 (segunda chamada)
Response Time: 0.2ms ← Cache L1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Teste 3: Sincronização Between Instâncias&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Instância A: POST /api/produtos/123 (atualiza produto)
Backplane: Sincroniza remoção automaticamente

Instância B: GET /api/produtos/123
Response: Dados atualizados! ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Resultados Obtidos&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Performance&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;95% redução&lt;/strong&gt; no tempo de resposta para dados em cache&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;80% redução&lt;/strong&gt; na carga do banco de dados&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sub-segundo&lt;/strong&gt; response time para a maioria das consultas&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Resiliência&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;100% uptime&lt;/strong&gt; mesmo com falhas do Redis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero downtime&lt;/strong&gt; durante manutenções do cache&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail-safe&lt;/strong&gt; mantém aplicação funcionando&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Escalabilidade&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sincronização automática&lt;/strong&gt; entre N instâncias&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load balancer friendly&lt;/strong&gt; (cache consistente)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero configuração&lt;/strong&gt; adicional para novas instâncias&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Configuração de Fallback&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Para desenvolvimento ou quando Redis não está disponível:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Fallback automático para cache apenas em memória&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Configuração com Redis (produção)&lt;/span&gt;
    &lt;span class="nf"&gt;ConfigurarFusionCacheComRedis&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Fallback para apenas memória (desenvolvimento)&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFusionCache&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDefaultEntryOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;FusionCacheEntryOptions&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Duration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;// Sem DistributedCache nem Backplane&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;O &lt;strong&gt;FusionCache&lt;/strong&gt; oferece uma solução para cache em aplicações .NET 8:&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Zero breaking changes&lt;/strong&gt; no código existente&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Performance extrema&lt;/strong&gt; com cache multi-nível&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Sincronização automática&lt;/strong&gt; entre instâncias&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Resiliência total&lt;/strong&gt; a falhas de infraestrutura&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Configuração flexível&lt;/strong&gt; por tipo de dados  &lt;/p&gt;

&lt;p&gt;Esta implementação é ideal para:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;APIs de alta performance&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aplicações distribuídas&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microserviços&lt;/strong&gt; que precisam de cache consistente&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Com essas configurações, você consegue ter cache que melhora drasticamente a performance e reduz a carga no banco de dados, enquanto mantém dados sempre sincronizados entre todas as instâncias!&lt;/p&gt;

&lt;p&gt;Simples né? Com apenas essas configurações você consegue ter uma solução de cache robusta, performática e resiliente.&lt;/p&gt;

&lt;p&gt;Espero que tenham gostado e até a próxima!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;#backend #cache #fusionCache #dotnet #braziliandevs #performance #redis #valkey&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>tutorial</category>
      <category>performance</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Implementando Resiliência em Aplicações .NET 8 com Polly</title>
      <dc:creator>Bruno Silva</dc:creator>
      <pubDate>Tue, 02 Sep 2025 14:15:08 +0000</pubDate>
      <link>https://forem.com/obrusilva/implementando-resiliencia-em-aplicacoes-net-8-com-polly-9m0</link>
      <guid>https://forem.com/obrusilva/implementando-resiliencia-em-aplicacoes-net-8-com-polly-9m0</guid>
      <description>&lt;p&gt;Hoje vamos falar sobre resiliência em aplicações e como implementar políticas de retry, circuit breaker e outras técnicas usando a biblioteca Polly no .NET 8.&lt;/p&gt;

&lt;h3&gt;
  
  
  O que é Resiliência em Software?
&lt;/h3&gt;

&lt;p&gt;Resiliência em software é a capacidade de uma aplicação continuar funcionando adequadamente mesmo quando enfrenta falhas temporárias ou problemas de infraestrutura. Em um mundo de microserviços e APIs distribuídas, falhas transitórias são comuns - timeouts de rede, serviços temporariamente indisponíveis, limitações de taxa (rate limiting), entre outros.&lt;/p&gt;

&lt;h3&gt;
  
  
  O que é o Polly?
&lt;/h3&gt;

&lt;p&gt;Polly é uma biblioteca .NET que permite implementar políticas de resiliência e tratamento de falhas transitórias de forma elegante e configurável. Com Polly, você pode adicionar comportamentos como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retry:&lt;/strong&gt; Tentar novamente após uma falha&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Circuit Breaker:&lt;/strong&gt; Evitar chamadas quando um serviço está falhando consistentemente&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout:&lt;/strong&gt; Definir tempo limite para operações&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallback:&lt;/strong&gt; Comportamento alternativo em caso de falha&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bulkhead Isolation:&lt;/strong&gt; Isolar recursos para evitar propagação de falhas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache:&lt;/strong&gt; Armazenar resultados para evitar chamadas desnecessárias&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Por que adotar o Polly?
&lt;/h3&gt;

&lt;p&gt;Antes de adotarmos o Polly, lidávamos com falhas transitórias em nosso código de forma manual e pouco estruturada.&lt;br&gt;
Um exemplo era o uso de goto para implementar tentativas de repetição (retry) em chamadas onde um token vencia e precisva pegar um novo. Apesar de funcionar em alguns cenários, essa abordagem tornava o código difícil de ler, de manter e de evoluir.&lt;/p&gt;

&lt;p&gt;O Polly surgiu como uma alternativa muito mais limpa, flexível e padronizada. Com ele conseguimos substituir blocos de código complexos (e até "gambiarras") por políticas declarativas, configuráveis e fáceis de entender.&lt;br&gt;
Isso trouxe vários benefícios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Legibilidade:&lt;/strong&gt; O código ficou mais claro e expressivo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Padronização:&lt;/strong&gt; Passamos a tratar falhas de forma consistente em diferentes partes da aplicação.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evolução:&lt;/strong&gt; É simples trocar ou combinar políticas (ex.: Retry + Circuit Breaker) sem precisar reescrever a lógica do zero.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manutenção:&lt;/strong&gt; Reduzimos a chance de erros escondidos em tratamentos manuais.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Instalação
&lt;/h3&gt;

&lt;p&gt;Para começar a usar o Polly em seu projeto .NET 8, instale o pacote NuGet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet add package Polly
dotnet add package Microsoft.Extensions.Http.Polly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Implementação Prática
&lt;/h3&gt;

&lt;p&gt;Vamos criar exemplos práticos de como usar o Polly em diferentes cenários.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Retry Simples
&lt;/h4&gt;

&lt;p&gt;Começando com um exemplo básico de retry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Polly;

public class ProdutoService
{
    private readonly HttpClient _httpClient;

    public async Task&amp;lt;Produto&amp;gt; ObterProdutoAsync(int id)
    {
        // Define política de retry: 3 tentativas
        var retryPolicy = Policy
            .Handle&amp;lt;HttpRequestException&amp;gt;()
            .RetryAsync(3, onRetry: (exception, retryCount) =&amp;gt;
            {
                Console.WriteLine($"Tentativa {retryCount} falhou: {exception.Message}");
            });

        // Executa com a política
        return await retryPolicy.ExecuteAsync(async () =&amp;gt;
        {
            var response = await _httpClient.GetAsync($"/api/produtos/{id}");
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadFromJsonAsync&amp;lt;Produto&amp;gt;();
        });
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Wait and Retry com Backoff Exponencial
&lt;/h4&gt;

&lt;p&gt;Para cenários mais complexos, podemos usar wait-and-retry com intervalos crescentes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class NotaFiscalService
{
    private readonly HttpClient _httpClient;
    private readonly ILogger&amp;lt;NotaFiscalService&amp;gt; _logger;

    public async Task&amp;lt;NotaFiscalResponse&amp;gt; EmitirNotaAsync(NotaFiscalRequest request)
    {
        var retryPolicy = Policy
            .HandleResult&amp;lt;HttpResponseMessage&amp;gt;(r =&amp;gt; !r.IsSuccessStatusCode)
            .WaitAndRetryAsync(
                retryCount: 4,
                sleepDurationProvider: retryAttempt =&amp;gt; TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                onRetry: (outcome, timespan, retryCount, context) =&amp;gt;
                {
                    var statusCode = outcome.Result?.StatusCode;
                    _logger.LogWarning(
                        "Tentativa {RetryCount} após {Timespan}s. StatusCode: {StatusCode}",
                        retryCount, timespan.TotalSeconds, statusCode);
                });

        var response = await retryPolicy.ExecuteAsync(async () =&amp;gt;
            await _httpClient.PostAsJsonAsync("/api/nfe/emitir", request)
        );

        return await response.Content.ReadFromJsonAsync&amp;lt;NotaFiscalResponse&amp;gt;();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Renovação Automática de Token com Retry
&lt;/h4&gt;

&lt;p&gt;Um cenário muito comum é renovar tokens JWT automaticamente quando expiram:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ApiClientService
{
    private readonly HttpClient _httpClient;
    private readonly ITokenService _tokenService;
    private string _currentToken;

    public async Task&amp;lt;T&amp;gt; ExecutarRequisicaoAsync&amp;lt;T&amp;gt;(string endpoint)
    {
        var retryPolicy = Policy&amp;lt;HttpResponseMessage&amp;gt;
            .HandleResult(r =&amp;gt; r.StatusCode == HttpStatusCode.Unauthorized)
            .RetryAsync(
                retryCount: 3,
                onRetryAsync: async (outcome, retryCount, context) =&amp;gt;
                {
                    Console.WriteLine($"Token expirado. Renovando... Tentativa {retryCount}/3");

                    // Renova o token
                    _currentToken = await _tokenService.RenovarTokenAsync();

                    // Atualiza o header de autorização
                    _httpClient.DefaultRequestHeaders.Authorization = 
                        new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _currentToken);
                });

        var response = await retryPolicy.ExecuteAsync(async () =&amp;gt;
        {
            var request = new HttpRequestMessage(HttpMethod.Get, endpoint);
            request.Headers.Authorization = 
                new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _currentToken);

            return await _httpClient.SendAsync(request);
        });

        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync&amp;lt;T&amp;gt;();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Boas Práticas
&lt;/h3&gt;

&lt;p&gt;Escolha as políticas adequadas: Nem toda operação precisa de retry. Operações não-idempotentes devem ser tratadas com cuidado.&lt;br&gt;
Configure timeouts apropriados: Evite que retries demorem muito tempo.&lt;br&gt;
Use Circuit Breaker para dependências externas: Proteja sua aplicação de serviços instáveis.&lt;br&gt;
Monitore e registre logs: Acompanhe quantos retries estão ocorrendo.&lt;/p&gt;

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

&lt;p&gt;O Polly é uma ferramenta poderosa para adicionar resiliência às suas aplicações .NET. Com apenas algumas linhas de código, você pode tornar sua aplicação muito mais robusta e capaz de lidar com falhas transitórias de forma elegante.&lt;br&gt;
A implementação de políticas de resiliência não é apenas uma boa prática - é essencial em arquiteturas distribuídas modernas. Com o Polly, você tem um kit de ferramenta completo para implementar padrões de resiliência de forma simples e manutenível.&lt;/p&gt;

&lt;p&gt;Espero que este artigo tenha sido útil para entender como implementar resiliência em suas aplicações .NET 8!&lt;/p&gt;

&lt;h1&gt;
  
  
  dotnet #polly #resilience #microservices #braziliandevs
&lt;/h1&gt;

</description>
    </item>
    <item>
      <title>Implementação de RateLimit em Aplicações .NET 8</title>
      <dc:creator>Bruno Silva</dc:creator>
      <pubDate>Fri, 16 Feb 2024 01:43:22 +0000</pubDate>
      <link>https://forem.com/obrusilva/implementacao-de-ratelimit-em-aplicacoes-net-8-3594</link>
      <guid>https://forem.com/obrusilva/implementacao-de-ratelimit-em-aplicacoes-net-8-3594</guid>
      <description>&lt;p&gt;Hoje vamos falar um pouco sobre Ratelimit e aproveitar para mostrar como fazer a implementação em uma aplicação Net 8.&lt;/p&gt;

&lt;p&gt;Antes de ir pra código precisamos entender.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;O que é RateLimit?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Este termo "rate limit" é referente a um mecanismo usado para controlar a taxa de solicitações de requisições que um usuário  pode realizar em um determinado período de tempo.&lt;/p&gt;

&lt;p&gt;Por exemplo, em APIs podemos impor limites de taxa para evitar que um único cliente ou aplicativo sobrecarregue o servidor com um grande número de solicitações em um curto período de tempo. E isso pode ser feito através do "Rate Limit".&lt;/p&gt;

&lt;p&gt;Em resumo, o rate limit é uma técnica utilizada para garantir que um serviço ou recurso não seja sobrecarregado, equilibrando a carga e garantindo um desempenho consistente e justo para todos os usuários, limitando assim o número de solicitações que podem ser feitas por segundo, minuto, hora etc.&lt;/p&gt;

&lt;p&gt;Os bloqueios podem ser feitos por usuário , ip de origem, endpoint  , enfim as possibilidades são inúmeras. &lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;em&gt;Implementação em .NET 8&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;A partir do .net 7 a Microsoft disponibilizou um middleware nativo para implementação de limite de requisições. &lt;/p&gt;

&lt;p&gt;Foram disponibilizadas 4 alternativas de limites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Fixed window - (Janela fixa de tempo, neste cenário limitamos a quantidade de requisições em um determinado tempo. Ex: 5 requisições a cada 10 segundos )&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sliding window - (Janela variável de tempo. É semelhante ao limitador de janela fixa, mas adiciona segmentos por janela. A janela desliza um segmento a cada intervalo de segmento. O intervalo de segmento é (tempo de janela)/(segmentos por janela))&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Token bucket - Simplificando é como se fosse um balde de tokens. Você tem uma quantidade de tokens em um balde e a cada requisição um token é retirado. A cada período os tokens também são adicionados e se em certo momento não tiver token disponível é porque atingiu a capacidade máxima. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Concurrency - Limitação por simultaneidade, ele limita a quantidade simultâneas de requisições.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Caso queira se aprofundar em mais detalhes segue o documentação da Microsoft &lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?view=aspnetcore-8.0#fixed"&gt;https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?view=aspnetcore-8.0#fixed&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Então para exemplificar vamos criar uma Api em .net 8 com alguns endpoints para simular.&lt;/p&gt;

&lt;p&gt;Vamos focar nas opções mais usadas que é a fixed window e concurrency e vamos deixar nossa Api com 3 configurações distintas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bora pro código?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Para habilitar o Ratelimit na sua Api vamos ter que adicionar o método AddRateLimiter no builder.Service da sua classe program.&lt;/p&gt;

&lt;p&gt;Neste método é possível configurar várias opções do ratelimit, a primeira configuração que vamos fazer é padronização do retorno do status code da requisição quando tivermos o limite atingindo. Para essa configuração basta associar o status 429 (too many request) a configurações opt.RejectionStatusCode.&lt;/p&gt;

&lt;p&gt;Vamos separar nosso RateLimit em 3 opções.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Global: Vamos ter uma limitação por usuário e endpoint onde a quantidade limite de requisições seja 120 requisições por minuto.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Politica Por IP: Criaremos uma politica onde a limitação será por usuário e endpoint e a quantidade de requisições não pode passar de 50 requisições por minuto.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Politica Por Concorrência: vamos criar uma limitação onde o endpoint só pode receber 1 requisição por vez.    &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Politica Global:&lt;br&gt;
A configuração GlobalLimiter será definida com o tipo Fixed Window  , no GetFixedWindowLimiter precisamos ajustar a partition key (essa partition key é como será agrupada as nossas requisições. No nosso exemplo vamos agrupar por usuário logado e endpoint). &lt;br&gt;
No retorno desse método vamos definir as configurações.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AutoReplenishment = true, //autoincremento &lt;/li&gt;
&lt;li&gt;PermitLimit = 120, // limite de requisição&lt;/li&gt;
&lt;li&gt;QueueLimit = 0,// limite de requisições em fila&lt;/li&gt;
&lt;li&gt;Window =TimeSpan.FromSeconds(60) // janela de tempo &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Politica Por Ip:&lt;br&gt;
Um pouco mais abaixo vamos adicionar uma politica nova como o código addPolicy, definindo o nome da politica e colocando o partitionKey por Ip e Endpoint. Vamos retornar as configurações de 50 requisições por minuto.&lt;/p&gt;

&lt;p&gt;Politica por Concorrência:&lt;br&gt;
A configuração por concorrência segue os mesmos passos com algumas modificações. A adição da politica é igual então vamos fazer o addPolicy com o nome concurrency, porem o RateLimitPartition precisar ser o método GetConcurrencyLimiter. No GetConcurrencyLimiter vamos definir a partitionKey apenas por endpoint e o PermitiLimit vamos definir apenas 1, para que a limite de requisição seja apenas 1. O QueueLimit será 0 para não enfileirar nenhum request.&lt;/p&gt;

&lt;p&gt;A ultima configuração que vamos fazer é a padronização da mensagem de retorno. Fazemos isso no OnRejected. Desta forma temos uma mensagem mais amigável de retorno.  &lt;/p&gt;

&lt;p&gt;Após as configurações feitas temos que ativar o rateLimite adicionando o codigo app.UseRateLimiter(). &lt;/p&gt;

&lt;p&gt;Lembrando que a adição desse trecho de código tem que ficar após o  app.UseRouting(). Se colocado antes o Rate limit pode não funcionar.&lt;/p&gt;

&lt;p&gt;A classe program vai ficar com as informações abaixo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
builder.Services.AddRateLimiter(opt =&amp;gt;
{
    opt.RejectionStatusCode = StatusCodes.Status429TooManyRequests; //força o retorno 429 de too many requests
    opt.GlobalLimiter = PartitionedRateLimiter.Create&amp;lt;HttpContext, string&amp;gt;(httpContext =&amp;gt;
        RateLimitPartition.GetFixedWindowLimiter(
            partitionKey: $"{httpContext.User.Identity?.Name}_{httpContext.Request.Path}",
            factory: partion =&amp;gt;
            {
                return new FixedWindowRateLimiterOptions
                {
                    AutoReplenishment = true, //autoincremento 
                    PermitLimit = 120, // limite de requisição
                    QueueLimit = 0,// limite de requisições em fila
                    Window = TimeSpan.FromSeconds(60) // janela de tempo
                };
            })
    );

    opt.AddPolicy("ip", httpContext =&amp;gt;
       RateLimitPartition.GetFixedWindowLimiter(
           partitionKey: $"{httpContext.Request.Path}_{httpContext.Connection.RemoteIpAddress}",
           factory: partition =&amp;gt;
           {
               return new FixedWindowRateLimiterOptions
               {
                   AutoReplenishment = true,
                   PermitLimit = 50,
                   QueueLimit = 0,
                   Window = TimeSpan.FromSeconds(60)
               };
           }));
    opt.AddPolicy("concurrency", httpContext =&amp;gt;
        RateLimitPartition.GetConcurrencyLimiter(
            partitionKey: httpContext.Request.Path,
            factory: partition =&amp;gt;
            {
                return new ConcurrencyLimiterOptions { 
                    PermitLimit = 1,
                    QueueLimit = 0,
                };
            }));


    opt.OnRejected = async (context, token) =&amp;gt;
    {
        context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
        if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAtfer))
            await context.HttpContext.Response.WriteAsync($"O limite de requisições foi atingifo tente novamente daqui {retryAtfer.TotalSeconds} segundos ");
        else if (context.Lease.TryGetMetadata(MetadataName.ReasonPhrase, out var reasonPhrase))
            await context.HttpContext.Response.WriteAsync($"O limite de requisições simultâneas foi atingido,tente novamente mais tarde");
        else
            await context.HttpContext.Response.WriteAsync("O limite de requisições foi atingido, tente novamente mais tarde");
    };
});

var app = builder.Build();

app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors(x =&amp;gt; x
    .AllowAnyOrigin()
    .AllowAnyMethod()
    .AllowAnyHeader()
);

app.UseRateLimiter();
app.UseEndpoints(endpoints =&amp;gt; { _ = endpoints.MapControllers(); });
app.UseResponseCompression();
app.Run();


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

&lt;/div&gt;



&lt;p&gt;Agora temos que configurar as nossas Controllers, lembrando que como temos uma politica global todas os endpoints já nasceram com a politica Global aplicadaa. Vamos ter que nos preocupar em configurar apenas as politicas de concorrência e por Ip&lt;/p&gt;

&lt;p&gt;No primeiro endpoint temos o exemplo da rateLimite global, onde não há nenhuma atributo vinculado.&lt;/p&gt;

&lt;p&gt;No Segundo endpoint foi adicionado o atributo [EnableRateLimiting("ip")], aqui o endpoint é forçado a usar a politica por IP.&lt;/p&gt;

&lt;p&gt;No terceiro Endpoint foi adicionado o atributo  [EnableRateLimiting("concurrency")], aqui o endpoint é forçado a usar a politica por concorrência.&lt;/p&gt;

&lt;p&gt;Código Controller&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  public class ExamplesController : ControllerBase
  {


      [HttpPost]
      [Route("rate-limiting-global")]
      public IActionResult ReteLimitingGlobal()
      {
          Thread.Sleep(10000);
          return Ok();
      }

      [HttpGet]
      [EnableRateLimiting("ip")]
      [Route("rate-limiting")]
      public IActionResult RateLimitingIP()
      {
          Thread.Sleep(10000);
          return Ok();
      }

      [HttpPost]
      [EnableRateLimiting("concurrency")]
      [Route("rate-limiting-post")]
      public IActionResult ReteLimitingPost()
      {
          Thread.Sleep(10000);
          return Ok();
      }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ex: de uma chamada com limite atingido no Swagger &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hkwrd62y23adisw2wfc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hkwrd62y23adisw2wfc.png" alt="Image description" width="562" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Simples né com apenas essas configurações você consegue ter N possibilidades de deixar sua api mais segura com controle de requisições. Isso ajuda a evitar ataques DDoS e ataques de força bruta.&lt;/p&gt;

&lt;p&gt;Espero que tenham gostado e até a próxima!&lt;/p&gt;

</description>
      <category>backend</category>
      <category>ratelimit</category>
      <category>dotnet</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Múltiplas documentações em uma única Api - Swagger</title>
      <dc:creator>Bruno Silva</dc:creator>
      <pubDate>Fri, 29 Apr 2022 00:54:20 +0000</pubDate>
      <link>https://forem.com/obrusilva/multiplas-documentacoes-em-uma-unica-api-swagger-lg7</link>
      <guid>https://forem.com/obrusilva/multiplas-documentacoes-em-uma-unica-api-swagger-lg7</guid>
      <description>&lt;p&gt;Bem vindos Devs!&lt;/p&gt;

&lt;p&gt;Pensei nesse post devido a necessidade que tive esses dias e por causa da dificuldade de achar algo claro sobre o assunto.&lt;/p&gt;

&lt;p&gt;A ideia é vir aqui de vez em quando pra mostrar alguma ideia ou algo que precisamos no dia a dia e que tenha poucos exemplos.&lt;/p&gt;

&lt;p&gt;Bora para o que interessa, hoje vamos falar de documentação de API!!&lt;/p&gt;

&lt;p&gt;A documentação de Api é algo muito importante nos dias de hoje e uma das ferramentas mais utilizada é o Swagger. &lt;/p&gt;

&lt;p&gt;Em muitos cenários precisamos ter uma documentação interna para os desenvolvedores e outra para cliente/fornecedores que precisam consumir as nossas Apis.&lt;/p&gt;

&lt;p&gt;Que tal eu mostrar uma forma simples e rápida de separar métodos e ter duas documentações diferentes de uma mesma api utilizando o Swagger? &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mas antes de começar, você sabe o que é Swagger?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;O Swagger é framework de código aberto que auxilia os desenvolvedores a documentar uma API automaticamente. Sua interface gráfica possui ferramentas que deixam a visualização mais intuitiva, mostrando os métodos, parâmetros, retornos e modelos de body. &lt;/p&gt;

&lt;p&gt;Além dessa documentação é possível também consumir os endpoints fazendo chamadas diretamente nas rotas. Desta forma você consegue testar de uma forma simplificada sem ter a necessidade de usar algum software especifico.&lt;/p&gt;

&lt;p&gt;Agora que sabemos o que é o Swagger, vou mostrar na prática como gerar duas documentações diferentes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Bora Codar&lt;/em&gt;&lt;/strong&gt;!!!&lt;br&gt;
Vamos criar uma aplicação em .net 6 e simular uma Api de produtos com 2 métodos um de buscar produto e outro de atualizar preço do produto. &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%2F4zuc5tqqaqt7bwd36fu7.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%2F4zuc5tqqaqt7bwd36fu7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Neste cenário queremos que as 2 rotas apareça para a documentação dos desenvolvedores, mas para os clientes apareçam  apenas a rota de buscar produto.&lt;/p&gt;

&lt;p&gt;Primeiro passo é ir na sua classe de controller e adicionar a ação &lt;br&gt;
&lt;strong&gt;[ApiExplorerSettings(GroupName = "CLIENTE")]&lt;/strong&gt;&lt;br&gt;
no método que você quer disponibilizar para o cliente &lt;/p&gt;

&lt;p&gt;Essa ação será responsável pela visibilidade dessa rota apenas para os cliente. Não é possível adicionar mais de uma ação, então iremos marcar essa api apenas para os clientes e vamos tratar para aparecer para os desenvolvedores de outra forma.&lt;/p&gt;

&lt;p&gt;Trecho do código da controller&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace WebApplication1.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class ProdutosController : ControllerBase
    {
        [ApiExplorerSettings(GroupName = "CLIENTE")]
        [HttpGet("buscar")]
        public IEnumerable&amp;lt;Produtos&amp;gt; BuscarProduto()
        //...

        [HttpPost("atualizar-preco")]
        public ActionResult BuscarPreco([FromBody] Produtos produto)
         //...

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

&lt;/div&gt;



&lt;p&gt;Depois de configurado a controller vamos para a configuração do Swagger&lt;/p&gt;

&lt;p&gt;Na classe Program onde é adicionado o serviço do Swagger devemos adicionar o seguinte código.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;builder.Services.AddSwaggerGen(option =&amp;gt; {
    option.SwaggerDoc("Cliente", new OpenApiInfo { Title = "Produtos API", Version = "v1" }); 
    option.SwaggerDoc("Desenvolvedor", new OpenApiInfo { Title = "Produtos API", Version = "v1" });
    option.DocInclusionPredicate((docName, apiDesc) =&amp;gt; {
        if (docName == "Desenvolvedor")
            return true;

        var groupName = apiDesc?.GroupName;
        return groupName?.Trim().ToUpper() == docName.Trim().ToUpper();
    });
});

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

&lt;/div&gt;



&lt;p&gt;As linhas do SwaggerDoc é a adição das duas documentações uma para o desenvolvedor e outra para o cliente.&lt;/p&gt;

&lt;p&gt;A linha DocInclusionPredicate é a onde fazemos a mágica (lembra que falei que iriamos tratar a visibilidade dos desenvolvedores de outra forma? então ela acontece aqui!).&lt;/p&gt;

&lt;p&gt;Dentro do DocInclusionPredicate efetuamos uma validação, onde caso o docName seja Desenvolvedor já retorne True. Isto fará com que todos os métodos sejam mapeados para a documentação desenvolvedor na parte gráfica do Swagger.&lt;/p&gt;

&lt;p&gt;Caso não seja a documentação do desenvolvedor ele irá validar se o docNome é igual ao GroupName definido lá na método da controller&lt;/p&gt;

&lt;p&gt;Para finalizar podemos definir urls diferentes para cada documentação. Isso é feito também na classe Program &lt;/p&gt;

&lt;p&gt;Antes do app.Run()&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.UseSwagger(c =&amp;gt; c.RouteTemplate = "/swagger-desenvolvedor/{documentName}/swagger.json");
app.UseSwaggerUI(c =&amp;gt;
{
    c.SwaggerEndpoint("../swagger-desenvolvedor/Desenvolvedor/swagger.json", "Produtos API");
    c.RoutePrefix = "swagger-desenvolvedor";
});

app.UseSwagger(c =&amp;gt; c.RouteTemplate = "/swagger-cliente/{documentName}/swagger.json");
app.UseSwaggerUI(c =&amp;gt;
{
    c.SwaggerEndpoint("../swagger-cliente/Cliente/swagger.json", "Produtos API");
    c.RoutePrefix = "swagger-cliente";
});

app.Run();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Na linha app.UseSwagger deve ser definido o caminho do json.&lt;br&gt;
Na linha app.SwaggerUI deve ser adicionado o mesmo caminho do json do swagger + o nome da aplicação + a rota da url.&lt;/p&gt;

&lt;p&gt;Defini as rotas como swagger-desenvolvedor e swagger-cliente&lt;/p&gt;

&lt;p&gt;Pronto sua api já está com a documentação segmentada, como podemos ver abaixo a rota atualizar-preço não aparece na documentação de clientes.&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%2Fptas3wcfccy9tafj0w0j.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%2Fptas3wcfccy9tafj0w0j.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A intenção do post é mostrar uma forma simplificada, existem algumas outras formas de customizar a UI do Swagger.&lt;/p&gt;

&lt;p&gt;Espero que tenham gostado. Até a próxima!&lt;/p&gt;

</description>
      <category>braziliandevs</category>
      <category>tutorial</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
