<?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: William Santos</title>
    <description>The latest articles on Forem by William Santos (@wsantosdev).</description>
    <link>https://forem.com/wsantosdev</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%2F262977%2F7e6708b2-3f54-42e8-8ea2-af9028345f15.jpg</url>
      <title>Forem: William Santos</title>
      <link>https://forem.com/wsantosdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/wsantosdev"/>
    <language>en</language>
    <item>
      <title>Performance: Structs e Cópias (Defensivas)</title>
      <dc:creator>William Santos</dc:creator>
      <pubDate>Tue, 27 Jan 2026 12:28:02 +0000</pubDate>
      <link>https://forem.com/wsantosdev/performance-structs-e-copias-defensivas-458</link>
      <guid>https://forem.com/wsantosdev/performance-structs-e-copias-defensivas-458</guid>
      <description>&lt;p&gt;Olá!&lt;/p&gt;

&lt;p&gt;Este é mais um post da seção &lt;strong&gt;Performance&lt;/strong&gt; e vamos falar sobre uma armadilha invisível no uso de structs em C#: as cópias defensivas.&lt;/p&gt;

&lt;p&gt;Vamos lá!&lt;/p&gt;

&lt;h2&gt;
  
  
  Structs: Por quê?
&lt;/h2&gt;

&lt;p&gt;Vamos recapitular: structs são uma escolha excelente para em cenários onde alocação de memória é uma questão crítica. Por existirem apenas na &lt;em&gt;stack&lt;/em&gt;, tendo portanto uma existência efêmera e não sujeita à ação do &lt;em&gt;Garbage Collector&lt;/em&gt;, seu uso afeta positivamente o desempenho de uma aplicação, efeito que pode ser constatado no caminho crítico da mesma via profile, ou via benchmarks em ambiente de desenvolvimento, quando avaliado diante de classes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mas nem tudo são flores!
&lt;/h2&gt;

&lt;p&gt;Ainda que sejam uma alternativa ao uso de classes e tenham por principal característica a evitação da alocação de memória no &lt;em&gt;heap&lt;/em&gt;, existe uma armadilha oculta: as cópias (defensivas).&lt;/p&gt;

&lt;h3&gt;
  
  
  Cópias: o fundamental
&lt;/h3&gt;

&lt;p&gt;Quando uma struct é passada como parâmetro para um método, dada sua natureza de &lt;em&gt;value type&lt;/em&gt;, a passagem acontece por valor, ou seja, uma cópia da struct em questão é gerada dentro do método, o que impede naturalmente que a struct original seja modificada, já que a cópia não é um ponteiro pra ela e encerra seu ciclo de vida ao final do método no qual foi recebida.&lt;/p&gt;

&lt;h3&gt;
  
  
  O Modificador &lt;em&gt;in&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Para permitir a passagem de structs como parâmetros por referência, surgiu o modificador &lt;code&gt;in&lt;/code&gt;, o que impede que uma cópia da struct seja criada mas que, ao mesmo tempo, cria um risco: mudança implícita de estado.&lt;/p&gt;

&lt;p&gt;Por ser passada por referência, qualquer alteração no estado interno da struct perdurará para além do método invocado, tornando a instância da struct vulnerável à mudanças indesejadas, ainda que o modificador &lt;code&gt;in&lt;/code&gt; crie um contexto &lt;code&gt;readonly&lt;/code&gt; que não permita a alteração explícita do estado da struct.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cópias defensivas, uma (aparente) solução
&lt;/h3&gt;

&lt;p&gt;Cópias defensivas são uma forma do compilador (&lt;em&gt;Roslyn&lt;/em&gt;) evitar que sua struct sofra mudanças indesejadas quando passadas como parâmetros &lt;code&gt;in&lt;/code&gt;, o que aumenta a segurança da aplicação contra efeitos colaterais. Mas, como bem sabemos, segurança sempre custará performance e é exatamente o que acontece neste caso.&lt;/p&gt;

&lt;h3&gt;
  
  
  Um pequeno exemplo
&lt;/h3&gt;

&lt;p&gt;Aqui temos uma struct chamada &lt;em&gt;Square&lt;/em&gt; que representa um quadrado. Um quadrado tem quatro lados iguais, portanto apenas o tamanho de um lado precisa ser conhecido. Vejamos:&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;struct&lt;/span&gt; &lt;span class="nc"&gt;Square&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;Side&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;init&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="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;Area&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Side&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Side&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repare que esta struct é imutável, ou seja, suas propriedades não podem ser modificadas durante um dado processamento. O problema é que o compilador não sabe se há alguma mudança de estado acionada quando a propriedade &lt;code&gt;Area&lt;/code&gt; é invocada e, por isso, cria uma cópia defensiva sempre que encontra uma invocação a este método.&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="n"&gt;Span&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Square&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;squares&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;stackalloc&lt;/span&gt; &lt;span class="n"&gt;Square&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1_000&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&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;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;squares&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lenght&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;squares&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&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;Square&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Side&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&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;panel&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;Panel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;panel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;squares&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//Renderiza os quadrados via propriedade `Area`.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adivinhe o que acontece aqui? Se você respondeu que &lt;em&gt;mil cópias defensivas&lt;/em&gt; serão criadas a partir do array de &lt;em&gt;Point&lt;/em&gt;, acertou na mosca!&lt;/p&gt;

&lt;p&gt;Mas, William, mil cópias?!&lt;/p&gt;

&lt;p&gt;Aqui está o pulo do gato: cada vez que uma propriedade é utilizada, o compilador cria uma cópia da sua struct toda pra garantir que aquela propriedade não afetará seu estado interno. Percebe o impacto potencial que estas operações geram sobre a performance?&lt;/p&gt;

&lt;h2&gt;
  
  
  Readonly ao resgate!
&lt;/h2&gt;

&lt;p&gt;Felizmente existe uma forma de lidar com esse problema sem prejuízo da performance, o modificador &lt;code&gt;readonly&lt;/code&gt;. Este modificador indica ao compilador que sua struct é imutável, o que garante que não haverá mudança de estado quando a struct for manipulada por um dado método.&lt;/p&gt;

&lt;p&gt;O modificador &lt;code&gt;readonly&lt;/code&gt; pode ser aplicado em duas dimensões: a struct inteira ou em certas propriedades. Vejamos:&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;readonly&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Square&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;Side&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;init&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="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;Area&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Side&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Side&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com o modificador aplicado à struct toda, o compilador entender que não há a possibilidade de mudança de estado quando a instância tiver suas propriedades invocadas. Isso faz com que nenhuma cópia defensiva seja criada.&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;struct&lt;/span&gt; &lt;span class="nc"&gt;Square&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;readonly&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;Side&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;init&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="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;Area&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Side&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Side&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com o modificador aplicado apenas a &lt;code&gt;Side&lt;/code&gt; o compilador entenderá que a propriedade &lt;code&gt;Area&lt;/code&gt; oferece o risco de mudança no estado interno, o que implicará uma cópia defensiva. Entretanto, o mesmo não acontecerá com a propriedade &lt;code&gt;Side&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance: medindo o impacto
&lt;/h2&gt;

&lt;p&gt;Abaixo temos uma demonstração do prejuízo à performance por força das cópias defensivas. Vejamos:&lt;/p&gt;

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

&lt;p&gt;Aqui fica claro que, com structs grandes no caminho crítico da aplicação, sem o devido cuidado, as cópias defensivas oferecem um prejuízo perceptível, dado que o mesmo é mensurável em benchmark com dados fictícios.&lt;/p&gt;

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

&lt;p&gt;Conhecer as características internas do compilador é muito útil para pensar sobre como desenhar suas structs e utilizá-las. Este é um caso onde seria simplesmente impossível considerar que suas structs são a causa do problema sem esse conhecimento.&lt;/p&gt;

&lt;p&gt;Como sempre, o código do benchmark está disponível no &lt;a href="https://github.com/wsantosdev/performance-defensive-copies" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; para te permitir testar e modificar à vontade.&lt;/p&gt;

&lt;p&gt;Gostou? Me deixe saber pelo indicadores. Caso tenha alguma dúvida, deixe um comentário ou me procure nas redes sociais. Será um prazer te responder.&lt;/p&gt;

&lt;p&gt;Muito obrigado por ler até aqui, e até a próxima!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>performance</category>
      <category>csharp</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Coopera Sharp - Chamadas em Sequência</title>
      <dc:creator>William Santos</dc:creator>
      <pubDate>Tue, 30 Sep 2025 18:11:45 +0000</pubDate>
      <link>https://forem.com/wsantosdev/coopera-sharp-chamadas-em-sequencia-2hh</link>
      <guid>https://forem.com/wsantosdev/coopera-sharp-chamadas-em-sequencia-2hh</guid>
      <description>&lt;p&gt;Olá!&lt;/p&gt;

&lt;p&gt;Este é mais um post da seção CooperaSharp e, desta vez, vamos falar um pouco sobre paralelismo e desempenho.&lt;/p&gt;

&lt;p&gt;Vamos lá!&lt;/p&gt;

&lt;h2&gt;
  
  
  O Problema
&lt;/h2&gt;

&lt;p&gt;O &lt;a href="https://github.com/yanjustino/cooperasharp/discussions/6" rel="noopener noreferrer"&gt;desafio&lt;/a&gt; propõe a melhoria de um conjunto de chamadas a uma API para a obtenção de certos dados.&lt;/p&gt;

&lt;p&gt;O código do problema está replicado abaixo:&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;IntegradorService&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;IApiClienteA&lt;/span&gt; &lt;span class="n"&gt;_clienteA&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;IApiClienteB&lt;/span&gt; &lt;span class="n"&gt;_clienteB&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;IApiClienteC&lt;/span&gt; &lt;span class="n"&gt;_clienteC&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;IntegradorService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;IApiClienteA&lt;/span&gt; &lt;span class="n"&gt;clienteA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;IApiClienteB&lt;/span&gt; &lt;span class="n"&gt;clienteB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;IApiClienteC&lt;/span&gt; &lt;span class="n"&gt;clienteC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_clienteA&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clienteA&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_clienteB&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clienteB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_clienteC&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clienteC&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;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Dado&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ObterDados&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;dados&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;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Dado&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dadosA&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;_clienteA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuscarDados&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// chamada pode demorar&lt;/span&gt;
        &lt;span class="n"&gt;dados&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dadosA&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;dadosB&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;_clienteB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuscarDados&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// chamada pode falhar&lt;/span&gt;
        &lt;span class="n"&gt;dados&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dadosB&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;dadosC&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;_clienteC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuscarDados&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// chamada pode ser lenta&lt;/span&gt;
        &lt;span class="n"&gt;dados&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dadosC&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;dados&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repare no seguinte: o código acima faz uma chamada para uma dada API, obtém seu resultado, adiciona-o a uma lista e faz a chamada seguinte.&lt;/p&gt;

&lt;p&gt;Ou seja, o tempo total da execução do método &lt;code&gt;ObterDados&lt;/code&gt; vai ser igual à soma do tempo de execução de cada chamada externa. Portanto, se cada chamada levar 1s para ser executada, teremos 3s de tempo total de execução.&lt;/p&gt;

&lt;p&gt;Ruim. Certo?&lt;/p&gt;

&lt;p&gt;Há mais um detalhe: o código acima é tremendamente otimista! Se alguma exceção ocorrer no meio do caminho ela não apenas não é tratada como é lançada para o método chamador, o que comprometeria todo o processo de obtenção dos dados.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Solução: Paralelismo!
&lt;/h2&gt;

&lt;p&gt;Algo que podemos fazer em caso semelhante é paralelizar as chamadas às API. Para fazer isso existem duas opções, que exploraremos abaixo:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Task.WhenAll&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A forma mais simples de fazer diversas chamadas e aguardar sua execução é criando novas threads, uma para cada chamada, unindo-as ao final de suas respectivas execuções para extrair o resultado desejado.&lt;/p&gt;

&lt;p&gt;Para isso, vamos transformar as chamadas em &lt;code&gt;Task&lt;/code&gt; e realizar todas as chamadas simultaneamente, desta forma:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;requests&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;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WeatherForecast&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;serverErrorClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetWeatherForecasts&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                                                          &lt;span class="n"&gt;serverSlowClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetWeatherForecasts&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                                                          &lt;span class="n"&gt;serverTimeoutClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetWeatherForecasts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;try&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;results&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;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requests&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;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SelectMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&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="k"&gt;new&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;data&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;AggregateException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//Handle inner exceptions on ex&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InnerExceptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repare que acima criamos um array de tasks para abrigar as chamadas e que, para obter seus resultados invocamos o método &lt;code&gt;Task.WhenAll(...)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Mas o que este método faz?&lt;/p&gt;

&lt;p&gt;A ideia é que, a partir de uma lista de tarefas, aqui representada pelo nosso array, as mesmas sejam chamadas em paralelo, limitando o tempo de espera por aquela que mais demorar. Esse método também é interessante porque, caso haja exceções lançadas pelas tasks as mesmas serão agregadas em um tipo chamado &lt;code&gt;AggregateException&lt;/code&gt;, integrando sua propriedade &lt;code&gt;InnerExceptions&lt;/code&gt;. Desta forma é possível registrá-las em log ou manipulá-las, decidindo como o fluxo seguirá.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Parallel.ForEachAsync&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Existe uma outra forma de obter nossos dados fazendo chamadas em paralelo para as API, o &lt;code&gt;Parallel.ForEachAsync&lt;/code&gt;. O código seria o seguinte, nos baseando no código acima:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;requests&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;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WeatherForecast&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;serverErrorClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetWeatherForecasts&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                                                          &lt;span class="n"&gt;serverSlowClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetWeatherForecasts&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                                                          &lt;span class="n"&gt;serverTimeoutClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetWeatherForecasts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ConcurrentBag&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WeatherForecast&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;results&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;cts&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;CancellationTokenSource&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;Parallel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ForEachAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requests&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;ParallelOptions&lt;/span&gt; 
                                &lt;span class="p"&gt;{&lt;/span&gt; 
                                    &lt;span class="n"&gt;MaxDegreeOfParallelism&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lenght&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cts&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;},&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;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ct&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;ct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ThrowIfCancellationRequested&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;result&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;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                                    &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                                &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;WithAggregateException&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;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SelectMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&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="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Elapsed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stopwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ElapsedMilliseconds&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;data&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="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//Handle ex&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status500InternalServerError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repare que aqui temos um capacidade adicional em relação ao &lt;code&gt;Task.WhenAll&lt;/code&gt;, que é a possibilidade de estabelecer o número máximo de &lt;em&gt;cores&lt;/em&gt; que serão usados, em vez de threads do threadpool, via &lt;code&gt;ParallelOptions { MaxDegreeOfParallelism = requests.Lengh }&lt;/code&gt;. O tamanho da nossa lista de requisições não foi informado à toa, ele indica exatamente o número de &lt;em&gt;cores&lt;/em&gt; -- repare, &lt;em&gt;cores&lt;/em&gt;, não threads! -- que esperamos abrir, solicitadas para tornar as chamadas possíveis.&lt;/p&gt;

&lt;p&gt;Ao final de cada requisição adicionaremos o resultado a uma &lt;code&gt;ConcurrentBag&lt;/code&gt;, uma coleção &lt;em&gt;threadsafe&lt;/em&gt; que armazenará todos os resultados.&lt;/p&gt;

&lt;p&gt;Perceba que o comportamento é um tanto diferente to &lt;code&gt;Task.WhenAll&lt;/code&gt;. Enquanto este aguarda a execução de todas as tasks para retornar o resultado de seu processamento, o &lt;code&gt;Parallel.ForEachAsync&lt;/code&gt; executa a função recebida como parâmetro tão logo uma tarefa seja executada. Ainda assim, a instrução seguinte ao &lt;code&gt;Parallel.ForEachAsync&lt;/code&gt; será executada apenas quando todas as tarefas forem concluídas.&lt;/p&gt;

&lt;p&gt;Uma outra diferença está na opção &lt;code&gt;WithAggregateException&lt;/code&gt;. Enquanto para o &lt;code&gt;Task.WhenAll&lt;/code&gt; a lança uma &lt;code&gt;AggregateException&lt;/code&gt; por padrão, o &lt;code&gt;Parallel.ForEachAsync&lt;/code&gt; precisa ser informado. Não informar vai fazer com que a primeira exceção ocorrida no processamento paralelo seja lançada e o fluxo seja interrompido.&lt;/p&gt;

&lt;h3&gt;
  
  
  Qual opção usar?
&lt;/h3&gt;

&lt;p&gt;A recomendação é que o &lt;code&gt;Task.WhenAll&lt;/code&gt; seja utilizado como primeira escolha sempre que possível. É um mecanismo suficiente para a maioria dos casos e  uma solução pouco verbosa.&lt;br&gt;
Já o &lt;code&gt;Parallel.ForEachAsync&lt;/code&gt; é uma escolha para quando houver precupação com o controle do uso de recursos ou houver cores disponíveis para executar as tarefas em paralelo, o que é mais performático que alternar entre threads em um mesmo core, sendo uma solução mais completa e um tanto mais performática, ainda que ao custo de maior verbosidade.&lt;/p&gt;

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

&lt;p&gt;Processamento paralelo é um recurso muito interessante para casos como o do desafio, onde é importante processar diversas tarefas no menor tempo possível.&lt;/p&gt;

&lt;p&gt;É uma bela alternativa para utlizar melhor certos recursos computacionais, como o &lt;em&gt;threadpool&lt;/em&gt; e os &lt;em&gt;cores&lt;/em&gt; do processador, de modo a aumentar o desempenho de sua aplicação.&lt;/p&gt;

&lt;p&gt;Gostou? Me deixe saber pelos indicadores ou por minhas redes sociais.&lt;/p&gt;

&lt;p&gt;Restou alguma dúvida? Fique à vontade para deixá-la nos comentários.&lt;/p&gt;

&lt;p&gt;Muito obrigado por ler até aqui, e até a próxima!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>cooperasharp</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Coopera Sharp - Dependency Explosion</title>
      <dc:creator>William Santos</dc:creator>
      <pubDate>Mon, 08 Sep 2025 15:21:45 +0000</pubDate>
      <link>https://forem.com/wsantosdev/cooperasharp-dependency-explosion-3b6d</link>
      <guid>https://forem.com/wsantosdev/cooperasharp-dependency-explosion-3b6d</guid>
      <description>&lt;p&gt;Olá!&lt;/p&gt;

&lt;p&gt;Este é mais um post da seção CooperaSharp e, desta vez, vamos examinar o desafio sobre explosão de dependências.&lt;/p&gt;

&lt;p&gt;Vamos lá!&lt;/p&gt;

&lt;h1&gt;
  
  
  O Problema
&lt;/h1&gt;

&lt;p&gt;Como é possível ver &lt;a href="https://github.com/yanjustino/cooperasharp/discussions/5" rel="noopener noreferrer"&gt;neste post&lt;/a&gt; temos um serviço com diversas dependências injetadas, o que cria o que chamamos de explosão de dependências no construtor (constructor dependency explosion em inglês).&lt;/p&gt;

&lt;p&gt;Este serviço executa uma série de passos para tornar possível a criação de um pedido.&lt;/p&gt;

&lt;p&gt;Esta abordagem é ruim porque, além de já conter inúmeras dependências, ainda pode adicionar mais caso o processo de torne maior, o que levaria a uma explosão ainda maior, tornando o construtor quase um container de dependências.&lt;/p&gt;

&lt;p&gt;Aqui o trecho do código ruim:&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;IClienteRepository&lt;/span&gt; &lt;span class="n"&gt;_clienteRepository&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;IPedidoRepository&lt;/span&gt; &lt;span class="n"&gt;_pedidoRepository&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;IEstoqueService&lt;/span&gt; &lt;span class="n"&gt;_estoqueService&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;IPagamentoService&lt;/span&gt; &lt;span class="n"&gt;_pagamentoService&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;IEmailService&lt;/span&gt; &lt;span class="n"&gt;_emailService&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;INotificacaoService&lt;/span&gt; &lt;span class="n"&gt;_notificacaoService&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;ILogger&lt;/span&gt; &lt;span class="n"&gt;_logger&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;IFreteService&lt;/span&gt; &lt;span class="n"&gt;_freteService&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;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IAuditoriaService&lt;/span&gt; &lt;span class="n"&gt;_auditoriaService&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;IClienteRepository&lt;/span&gt; &lt;span class="n"&gt;clienteRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;IPedidoRepository&lt;/span&gt; &lt;span class="n"&gt;pedidoRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;IEstoqueService&lt;/span&gt; &lt;span class="n"&gt;estoqueService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;IPagamentoService&lt;/span&gt; &lt;span class="n"&gt;pagamentoService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;IEmailService&lt;/span&gt; &lt;span class="n"&gt;emailService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;INotificacaoService&lt;/span&gt; &lt;span class="n"&gt;notificacaoService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ILogger&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;IFreteService&lt;/span&gt; &lt;span class="n"&gt;freteService&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="n"&gt;IAuditoriaService&lt;/span&gt; &lt;span class="n"&gt;auditoriaService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_clienteRepository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clienteRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_pedidoRepository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pedidoRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_estoqueService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;estoqueService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_pagamentoService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pagamentoService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_emailService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_notificacaoService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;notificacaoService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_freteService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;freteService&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="n"&gt;_auditoriaService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auditoriaService&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;void&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;PedidoDto&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cliente&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_clienteRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ObterPorId&lt;/span&gt;&lt;span class="p"&gt;(&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;ClienteId&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;cliente&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="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cliente não encontrado"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pedido&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;Pedido&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&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;_estoqueService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReservarItens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pedido&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Itens&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;valorFrete&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_freteService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CalcularFrete&lt;/span&gt;&lt;span class="p"&gt;(&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;EnderecoEntrega&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;pedido&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AdicionarFrete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valorFrete&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;pagamentoOk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_pagamentoService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Processar&lt;/span&gt;&lt;span class="p"&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;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;pagamentoOk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_notificacaoService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Enviar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Falha no pagamento"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;_pedidoRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Salvar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pedido&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;_emailService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EnviarConfirmacao&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cliente&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_auditoriaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Registrar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pedido criado"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pedido&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="nf"&gt;ToString&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;h1&gt;
  
  
  Uma Solução Ingênua
&lt;/h1&gt;

&lt;p&gt;Uma das soluções que pode vir à mente neste momento é a de trabalhar com eventos e deixar cada handler se prontificar a resolver uma parte do processo, fazendo com que o PedidoService cuide apenas da persistência do pedido.&lt;/p&gt;

&lt;p&gt;Seria uma solução interessante se não fosse por um detalhe: repare neste trecho:&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="n"&gt;_estoqueService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReservarItens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pedido&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Itens&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;valorFrete&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_freteService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CalcularFrete&lt;/span&gt;&lt;span class="p"&gt;(&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;EnderecoEntrega&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;pedido&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AdicionarFrete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valorFrete&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O que acontece se houver uma exceção no serviço de frete? Pois é! O o estoque permanece com os itens reservados ainda que o processo no geral falhe. Isso leva a uma inconsistência no estado da aplicação, podendo impedir que outro pedido seja feito por falta de estoque.&lt;/p&gt;

&lt;p&gt;Portanto, precisamos pensar numa forma de tornar possível que ações compensatórias como a liberação dos itens seja possível. E aí entra a solução que encontrei para o desafio.&lt;/p&gt;

&lt;h1&gt;
  
  
  Minha Solução (Elegante?)
&lt;/h1&gt;

&lt;p&gt;Se pensarmos bem, este processo todo é uma transação. Se um passo dela falhar, todas devem falhar, executando um rollback e revertendo a mudança de estado que eventualmente tenha provocado. Então entra em cena um mecanismo de transação em cadeia, inspirado no pattern Chain of Responsibility sobre o qual falo &lt;a href="https://dev.to/wsantosdev/chain-of-responsibility-e-aspnet-core-14hn"&gt;neste post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vamos conhecer o código!&lt;/p&gt;

&lt;p&gt;Em primeiro lugar, precisamos ser capazes de descrever os passos da nossa transação e, para isso, foram criados os TransactionSteps, como podemos ver abaixo:&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;interface&lt;/span&gt; &lt;span class="nc"&gt;ITransactionStep&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TInput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TInput&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;notnull&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="n"&gt;TInput&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Rollback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;error&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;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TransactionStepBase&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TInput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;ITransactionStep&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TInput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;?&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ITransactionStep&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TInput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TInput&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;notnull&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="n"&gt;TInput&lt;/span&gt; &lt;span class="n"&gt;input&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;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ExecuteInternal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="n"&gt;input&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;result&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="n"&gt;input&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;result&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&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;result&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;Rollback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ExecuteInternal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="n"&gt;TInput&lt;/span&gt; &lt;span class="n"&gt;input&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;abstract&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Rollback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui temos uma interface que vai demarcar a anatomia de um passo transacional e, também, uma classe abstrata que deverá ser implementada por cada passo.&lt;/p&gt;

&lt;p&gt;Repare que cada passo da transação, tal qual na cadeia de responsabilidades, recebe uma referência para o passo seguinte. A diferença está no método &lt;code&gt;Rollback&lt;/code&gt;, que será invocado caso hava um resultado negativo do passo implementação.&lt;/p&gt;

&lt;p&gt;Há, também, uma verificação do próximo passo a fim de saber se alcançamos o fim da cadeia.&lt;/p&gt;

&lt;p&gt;Agora vamos ver a implementação de um dos passos, o de verificação da existência de um cliente:&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;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomerValidationStep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CustomerDataAccess&lt;/span&gt; &lt;span class="n"&gt;customerDataAccess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                           &lt;span class="n"&gt;OrderStockReservationStep&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TransactionStepBase&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderSubmissionBag&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ExecuteInternal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="n"&gt;OrderSubmissionBag&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&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;customer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customerDataAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerId&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;customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&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;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Error&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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Customer with id &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; not found"&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="n"&gt;ex&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;Rollback&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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&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;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Rollback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;error&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;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repare que aqui temos três possíveis desfechos para a execução deste passo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;O cliente existe e é incluído em uma &lt;code&gt;OrderSubmissionBag&lt;/code&gt; que contém uma referência para a requisição recebida pelo servidor, uma para o cliente e outra para o pedido. Neste desfecho o passo seguinte é executado, o &lt;code&gt;OrderStockReservationStep&lt;/code&gt;, responsável por reservar os itens do pedido no estoque.&lt;/li&gt;
&lt;li&gt;O cliente não existe e é retornado um erro indicando que o Id informado não foi encontrado.&lt;/li&gt;
&lt;li&gt;Há uma exceção em algum ponto do fluxo e o método &lt;code&gt;Rollback&lt;/code&gt; é acionado. Neste caso, por tratar-se de uma consulta, não há a necessidade de reverter o estado da aplicação, portanto apenas um erro  será retornado, interrompendo a execução da cadeia transacional.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Para iniciar a execução destes passos, é necessário um transactor, que funciona apenas como um iniciador do processo.&lt;br&gt;
Veja o código abaixo:&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;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderSubmissionTransactor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CustomerValidationStep&lt;/span&gt; &lt;span class="n"&gt;step&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="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderSubmissionBag&lt;/span&gt; &lt;span class="n"&gt;bag&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;step&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="n"&gt;bag&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com isso, como pode ser visto no &lt;a href="https://github.com/wsantosdev/CooperaSharp-DependencyExplosion" rel="noopener noreferrer"&gt;repositório da solução&lt;/a&gt;, todos os passos da transação podem ser executados em sequência e novos podem ser encadeados, injetando apenas a dependência do Transactor no Controller, como segue:&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="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="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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SubmitOrderController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderSubmissionTransactor&lt;/span&gt; &lt;span class="n"&gt;transactor&lt;/span&gt;&lt;span class="p"&gt;)&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;SubmitOrder&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;OrderSubmissionRequest&lt;/span&gt; &lt;span class="n"&gt;request&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;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transactor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Process&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;OrderSubmissionBag&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Customer&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;Order&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; 
                &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"It was not possible to submit your order. Try again later."&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;h1&gt;
  
  
  Conclusão
&lt;/h1&gt;

&lt;p&gt;Evitar a explosão de dependências é interessante para manter o código legível e, também, evitar o que alguns chamam de God Classes ou Megazords, classes que respondem por muitas atividades e cuja legibilidade geralmente é baixa, assim como é sua testabilidade - imagine criar e injetar 10 instâncias reais para testar esta classe, ou pior, utilizar mocks para isso!&lt;/p&gt;

&lt;p&gt;Com a solução que proponho, cada passo do processo está autocontido, portanto coeso, não tem conhecimento dos demais, exceto pelo passo imediatamente seguinte, eliminamos a explosão de dependências e, também, mantemos a consistência do estado da aplicação.&lt;/p&gt;

&lt;p&gt;Aparentemente uma vitória!&lt;/p&gt;

&lt;p&gt;Gostou? Me deixe saber pelos indicadores ou por minhas redes sociais.&lt;/p&gt;

&lt;p&gt;Muito obrigado por ler até aqui, e até o próximo post!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>cooperasharp</category>
      <category>programming</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Coopera Sharp - Otimização de Query</title>
      <dc:creator>William Santos</dc:creator>
      <pubDate>Mon, 25 Aug 2025 12:05:05 +0000</pubDate>
      <link>https://forem.com/wsantosdev/coopera-sharp-otimizacao-de-query-2hf0</link>
      <guid>https://forem.com/wsantosdev/coopera-sharp-otimizacao-de-query-2hf0</guid>
      <description>&lt;p&gt;Olá!&lt;/p&gt;

&lt;p&gt;Este é um post da seção Coopera Sharp, uma iniciativa do colega &lt;a href="https://www.linkedin.com/in/yanjustino" rel="noopener noreferrer"&gt;Yan Justino&lt;/a&gt; que propõe desafios no LinkedIn baseados em códigos com más práticas.&lt;/p&gt;

&lt;p&gt;Normalmente respondo diretamente pelos comentários mas, desta vez, achei interessante trazer para o blog já que temos uma questão importante de desempenho envolvida.&lt;/p&gt;

&lt;p&gt;Vamos lá!&lt;/p&gt;

&lt;h2&gt;
  
  
  O Desafio
&lt;/h2&gt;

&lt;p&gt;O &lt;a href="https://lnkd.in/dpGv-m5T" rel="noopener noreferrer"&gt;desafio&lt;/a&gt; é otimizar uma query muito ineficiente, que vai ao banco de dados por diversas vezes, uma para cada registro necessário à composição de uma view. Veja o código abaixo:&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;AppDbContext&lt;/span&gt; &lt;span class="n"&gt;_context&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;AppDbContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&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="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PedidoComClienteDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ObterPedidos&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;pedidos&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pedidos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&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;resultado&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;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PedidoComClienteDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&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;pedido&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pedidos&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;cliente&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clientes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&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;pedido&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClienteId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;resultado&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;PedidoComClienteDto&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;PedidoId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pedido&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;Data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pedido&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataCriacao&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;NomeCliente&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cliente&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="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resultado&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;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PedidoComClienteDto&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;PedidoId&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;NomeCliente&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repare que, nesta abordagem, para cada pedido existente na base, será feita uma nova consulta para seu respectivo cliente. Ou seja, quanto mais a tabela de pedidos cresce, mais consultas são realizadas, mais rede é consumida, e mais demorada é a resposta. E pior, existe uma alocação tremenda de memória porque a transformação de &lt;code&gt;IEnumuerable&amp;lt;PedidoComClienteDto&amp;gt;&lt;/code&gt; em &lt;code&gt;List&amp;lt;PedidoComClienteDto&amp;gt;&lt;/code&gt; duplica a quantidade de objetos alocados, pois os que pertencem ao &lt;code&gt;IEnumerable&lt;/code&gt; tem cópias criadas para a &lt;code&gt;List&lt;/code&gt; por conta da natureza &lt;em&gt;lazy&lt;/em&gt; de &lt;code&gt;IEnumerable&lt;/code&gt; versus a &lt;em&gt;eager&lt;/em&gt; da &lt;code&gt;List&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Um verdadeiro horror!&lt;/p&gt;

&lt;h2&gt;
  
  
  A Solução
&lt;/h2&gt;

&lt;p&gt;A solução mais elegante que encontrei, cujos números você poderá ver no benchmark que estará no final deste bloco, faz algo muito interessante. Em vez de ir ao banco uma vez para cada cliente demandado por um pedido, apenas uma conexão é necessária para trazer todo o conteúdo de pedidos e clientes para a memória e, então, criar as views.&lt;/p&gt;

&lt;p&gt;Aqui segue o código:&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="n"&gt;IList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CustomerIdentifiedOrderViewAsStruct&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Solution&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dbContext&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;AppDbContext&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;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;
                          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsValueEnumerable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;CustomerIdentifiedOrderViewAsStruct&lt;/span&gt;
                          &lt;span class="p"&gt;{&lt;/span&gt;
                              &lt;span class="n"&gt;OrderId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;order&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;CreationDate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreationDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                              &lt;span class="n"&gt;CustomerName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;!.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;
                          &lt;span class="p"&gt;})&lt;/span&gt;
                          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&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;result&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;StructLayout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LayoutKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sequential&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Pack&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;CustomerIdentifiedOrderViewAsStruct&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;OrderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;CreationDate&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;CustomerName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Antes de mais nada, perceba como este código é muito mais simples e direto ao ponto. Existe apenas uma consulta, relacionando os pedidos com seus respectivos clientes, e todo o resto do trabalho é feito em memória.&lt;/p&gt;

&lt;p&gt;Além de realizarmos menos idas ao banco, repare no método &lt;code&gt;AsNoTracking()&lt;/code&gt;. Ele é um método do EF Core para trabalhar com consultas cujos dados devam servir apenas para leitura, ou seja, não serão modificados e persistidos. Isso melhora muito a velocidade e a alocação em memória porque dispensa o EF Core de criar os rastreadores para os objetos criados na query.&lt;/p&gt;

&lt;p&gt;Se você leu meu post sobre o &lt;a href="https://dev.to/wsantosdev/desempenho-zlinq-3ihn"&gt;ZLinq&lt;/a&gt; deve ter percebi que ele está em uso nessa solução por conta do método &lt;code&gt;AsValueEnumerable()&lt;/code&gt;. A ideia é reduzir dramaticamente a quantidade de alocações para economizar memória e reduzir, com isso, a pressão sobre o &lt;em&gt;Garbage Collector&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Além disso, a própria view foi transformada em um record struct em vez de uma classe para reduzir as alocações. Repare no atributo &lt;code&gt;StructLayout&lt;/code&gt;, ele serve para indicar ao compilador como os bytes da struct devem ser arranjados para melhor uso da stack. Escrevi &lt;a href="https://dev.to/wsantosdev/desempenho-economizando-memoria-em-structs-com-struclayout-345p"&gt;este&lt;/a&gt; post a respeito, vale a leitura!&lt;/p&gt;

&lt;p&gt;Apesar desta solução ser ótima do ponto de vista de desempenho, há algumas considerações que gostaria de fazer do ponto de vista de design.&lt;br&gt;
Em primeiro lugar o ideal é evitar o uso de um único DbContext para toda a aplicação. Isso porque haverá uma pressão enorme para que consultas como a do desafio sejam feitas. Neste cenário em especial, o ideal seria que cada DbContext estivesse encapsulado em um módulo (Pedido ou Cliente) e que o módulo de Pedido pudesse pedir uma coleção de Clientes com base em uma coleção de ID. Isso manteria o código coeso do ponto de vista de cada módulo e, ao mesmo tempo, manteria os módulos isolados, evitando um acoplamento que levaria o código à famosa Grande Bola de Lama (&lt;em&gt;Big Ball of Mud&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;O ideal, do meu ponto de vista, seria o serviço de pedidos trabalhar com eventos de domínio que, a partir de uma projeção, geraria essa view, para que ela pudesse ser obtida com uma única consulta a partir de quem precisa desse dado. &lt;/p&gt;

&lt;p&gt;Mas, como é um exemplo meramente ilustrativo, não há o menor problema em estar como está!&lt;/p&gt;

&lt;p&gt;E, aqui, o benchmark dos dois codigos. Ambos utilizando a mesma base, com 100.000 pedidos e 100.000 clientes.&lt;/p&gt;

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

&lt;p&gt;Diferença assombrosa. Não? Pois é!&lt;/p&gt;

&lt;h2&gt;
  
  
  Show me the Code
&lt;/h2&gt;

&lt;p&gt;Como sempre o código desta demonstração está disponível no &lt;a href="https://github.com/wsantosdev/CooperaSharp-QueryInLoop" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Ela utiliza SQLite e, portanto, não exige o uso de Docker nem mesmo de uma instância de qualquer banco. É baixar, rodar, e modificar como quiser.&lt;/p&gt;

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

&lt;p&gt;Apesar de ser um desafio de desempenho, é preciso sempre levar questões de design em consideração para que a aplicação seja modular e, com isso, mais fácil de evoluir. Ao mesmo tempo, conhecer mais profundamente ferramentas como o EF Core e o ZLinq te ajudam a considerar desempenho quando trabalhando com consultas.&lt;/p&gt;

&lt;p&gt;Gostou? Me deixe saber pelos indicadores. Alguma dúvida ou comentário? Diga aqui na caixa de comentários ou me procure nas redes sociais, onde costumo responder mais rapidamente.&lt;/p&gt;

&lt;p&gt;Muito obrigado por ler até aqui e até a próxima.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>performance</category>
      <category>dotnet</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Crítica - MediatR: Pra Quê?</title>
      <dc:creator>William Santos</dc:creator>
      <pubDate>Mon, 23 Jun 2025 17:05:59 +0000</pubDate>
      <link>https://forem.com/wsantosdev/critica-mediatr-pra-que-5bdf</link>
      <guid>https://forem.com/wsantosdev/critica-mediatr-pra-que-5bdf</guid>
      <description>&lt;p&gt;Olá!&lt;/p&gt;

&lt;p&gt;Este é um post da seção &lt;strong&gt;Crítica&lt;/strong&gt;, e nele pretendo explorar o uso da biblioteca MediatR em aplicações reais e provocar uma reflexão a respeito.&lt;/p&gt;

&lt;p&gt;Vamos lá!&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é o MediatR?
&lt;/h2&gt;

&lt;p&gt;Antes de adentrarmos a provocação proposta, vamos entender sobre o que estamos falando.&lt;/p&gt;

&lt;p&gt;O MediatR se propõe a ser uma implementação do padrão &lt;a href="https://refactoring.guru/design-patterns/mediator" rel="noopener noreferrer"&gt;Mediator&lt;/a&gt; (em inglês) que, basicamente, é um meio de propagar mensagens sem que seu produtor precise conhecer seus consumidores.&lt;/p&gt;

&lt;p&gt;Além disso, a biblioteca possui uma implementação que sugere a mesma como um roteador de comandos e consultas, o que faz com que muitos imaginem, erroneamente, ser uma implementação de CQRS, o que explico &lt;a href="https://dev.to/wsantosdev/design-cqrs-desfazendo-mal-entendidos-5bpo"&gt;neste post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;E é aqui que começamos a entender o problema.&lt;/p&gt;

&lt;h2&gt;
  
  
  O Problema
&lt;/h2&gt;

&lt;p&gt;Quando o MediatR é utilizado para propagar comandos, a intenção é impedir que um Controller, por exemplo, tenha um "Constructor Dependency Explosion", ou seja, que tenha uma série de dependências injetadas via construtor e passe a depender, apenas, de uma instancia de &lt;code&gt;IMediator&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Isso traz algumas questões:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;O problema original é a necessidade de utilizar várias dependências para a execução de um único comando ou consulta, e não é o uso do &lt;code&gt;MediatR&lt;/code&gt; que vai resolver, ele vai apenas mascarar pois seus handlers tendem a continuar com a explosão de dependências em seus construtores, ainda que em número menor ao do Controller.&lt;/li&gt;
&lt;li&gt;Ao utlizar o MediatR adiciona-se uma camada de indireção por meio de sua interface &lt;code&gt;IMediator&lt;/code&gt;, o que obriga o programador a buscar a implementação do handler correspondente àquela operação para entender o que se passa.&lt;/li&gt;
&lt;li&gt;Normalmente o uso do MediatR esconde uma falha de design imensa que é a criação de uma quantidade enorme de handlers com uma quantidade enorme de código, disfarçando o que o Controller escancararia: código excessivo.&lt;/li&gt;
&lt;li&gt;Toda vez que um Controller que utiliza &lt;code&gt;IMediator&lt;/code&gt; é construído, também é uma instância do Mediator, uma vez que sua configuração padrão o cria como uma instância transiente.&lt;/li&gt;
&lt;li&gt;Um componente fundamental de sua aplicação, seu roteador de operações, passa a ser uma dependência externa, o que te torna vulnerável a eventuais falhas, as famosas &lt;em&gt;breaking changes&lt;/em&gt; ou até mesmo, como é possível ver &lt;a href="https://github.com/jbogard/MediatR/discussions/1105" rel="noopener noreferrer"&gt;aqui&lt;/a&gt;, uma mudança de licenciamento. Ou seja, não é mais um simples apoio, passa a ser um custo difícil de reverter.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ou seja, em parte significativa dos casos, a maioria na minha experiência, o MediatR apenas mascara uma deficiência do design da aplicação, tornando-a mais complexa e mais difícil de ler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Que Vantagem Maria Leva?
&lt;/h2&gt;

&lt;p&gt;Diante das questões apresentados acima, proponho a seguinte pergunta: qual o ganho real que o uso do MediatR te traz?&lt;/p&gt;

&lt;p&gt;É esconder a bagunça? Então ele só piora.&lt;br&gt;
É separar responsabilidades? Então você não precisa dele.&lt;br&gt;
É isolar dependências? Então você não precisa dele.&lt;/p&gt;

&lt;p&gt;Entendo que faça sentido buscar responder a essa pergunta, indo além das questões aqui colocadas. Ou seja, considerando qualquer outro uso que você entenda fazer sentido para a biblioteca. &lt;/p&gt;

&lt;p&gt;Aliás, te incentivo a trazer nos comentários do post, ou nas minhas redes, quais ganhos você percebe no uso da biblioteca que compense seus custos. Seria excelente ter pontos de vista diversos pra que eu mesmo considere, ou reconsidere.&lt;/p&gt;

&lt;h2&gt;
  
  
  MediatR é de Fácil Replicação
&lt;/h2&gt;

&lt;p&gt;Por último, depois de responder a pergunta cima, ofereço uma alternativa super simples: construir um mediador você mesmo. No fim do dia, tudo de que você precisa é de uma interface que te permita percorrer uma lista de handlers e escolher o adequado para executar sua operação.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/angelobelchior/reinventando-a-roda-criando-seu-proprio-mediatr-parte-1-3f1o"&gt;Nesta série de posts&lt;/a&gt;, meu colega e referência na comunidade .NET, Angelo Belchior, demonstra como é simples. Ele não pretende criar uma réplica utilizável em produção, apenas te guiar para uma implementação própria.&lt;/p&gt;

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

&lt;p&gt;Evito ao máximo o uso do MediatR como um roteador de comandos e consultas, prefiro injetar meus handlers diretamente no Controller correspondente à operação que pretendo realizar. &lt;br&gt;
Acho a biblioteca ruim? Nem de longe, tenho até um tutorial sobre ela &lt;a href="https://dev.to/wsantosdev/playground-mediatr-22je"&gt;aqui no Blog&lt;/a&gt;. &lt;br&gt;
Mas entendo não haver ganhos significativos diante das questões apresentadas a menos que a aplicação se beneficie (como se beneficia a do exemplo do tutorial) e, por isso, ofereço uma solução alternativa no post sobre mal-entendidos em CQRS já referenciado (mas que você pode acessar por &lt;a href="https://dev.to/wsantosdev/design-cqrs-desfazendo-mal-entendidos-5bpo"&gt;aqui&lt;/a&gt; também).&lt;/p&gt;

&lt;p&gt;Gostou deste formato de post? Deixe-me saber pelos indicadores. Tem alguma dúvida ou comentário? Por favor, descreva abaixo.&lt;/p&gt;

&lt;p&gt;Muito obrigado por ler até aqui e até o próximo post!&lt;/p&gt;

</description>
      <category>designpatterns</category>
      <category>csharp</category>
      <category>programming</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Desempenho - Aprimorando Span com ZLinq</title>
      <dc:creator>William Santos</dc:creator>
      <pubDate>Wed, 21 May 2025 11:43:51 +0000</pubDate>
      <link>https://forem.com/wsantosdev/desempenho-zlinq-3ihn</link>
      <guid>https://forem.com/wsantosdev/desempenho-zlinq-3ihn</guid>
      <description>&lt;p&gt;Olá!&lt;/p&gt;

&lt;p&gt;Este é mais um post da seção &lt;strong&gt;Desempenho&lt;/strong&gt; e, desta vez, vamos tratar de uma ferramenta muito útil que nos auxilia a lidar com &lt;code&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt;: O &lt;a href="https://www.nuget.org/packages/Zlinq" rel="noopener noreferrer"&gt;ZLinq&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vamos lá!&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivação
&lt;/h2&gt;

&lt;p&gt;Como sabemos, não é possível escrever expressões Linq sobre spans e, por isso, é comum termos de iterar em coleções acessadas por esse tipo para realizamos operações.&lt;/p&gt;

&lt;p&gt;O ZLinq ambiciona mudar esse comportamento!&lt;/p&gt;

&lt;p&gt;Por meio de um método que tranforma seu &lt;code&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; em um &lt;code&gt;ValueEnumerable&lt;/code&gt; e permite que, em sua superfície, sejam executadas expressões como aquelas do Linq -- digo como aquelas do Linq e não as deste porque as expressões são todas implementadas pela própria biblioteca, que alega ter 99% das implementações do Linq (como os métodos &lt;code&gt;Select&lt;/code&gt;, &lt;code&gt;Where&lt;/code&gt; e &lt;code&gt;Aggregate&lt;/code&gt; por exemplo).&lt;/p&gt;

&lt;p&gt;Aqui temos um exemplo do benchmarking realizado para fins de teste desta biblioteca:&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;MemoryDiagnoser&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;ZLinqBenchmark&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;Linq&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;DummyAccount&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;dummies&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;DummyAccount&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1_000&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&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;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;dummies&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="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
            &lt;span class="n"&gt;dummies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&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;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewGuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;i&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="k"&gt;return&lt;/span&gt;
        &lt;span class="n"&gt;dummies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Balance&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
               &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AccountId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
               &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&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="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;ZLinq&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Span&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DummyAccount&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dummies&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;stackalloc&lt;/span&gt; &lt;span class="n"&gt;DummyAccount&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1_000&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&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;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;dummies&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="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
            &lt;span class="n"&gt;dummies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&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;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewGuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;100m&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;dummies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsValueEnumerable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
               &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Balance&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
               &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AccountId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
               &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como podemos observar, não há grandes diferenças entre os usos do Linq e Zlinq, na verdade há apenas duas: o método &lt;code&gt;Linq&lt;/code&gt; utiliza um array, enquanto o &lt;code&gt;ZLinq&lt;/code&gt; utiliza um &lt;code&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; e; o método &lt;code&gt;ZLinq&lt;/code&gt; lança mão do &lt;code&gt;ValueEnumerable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Essas simples diferenças produziram o seguinte resultado no benchmarking:&lt;/p&gt;

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

&lt;p&gt;Repare nas alocações: é uma economia de 32k por execução. Agora imagine isso no caminho crítico de uma aplicação, como oferece um desempenho superior por conta da necessidade reduzida de acionamentos do &lt;em&gt;Garbage Collector&lt;/em&gt;. Impressionante!&lt;/p&gt;

&lt;p&gt;Algo interessante aqui: em cenários onde os dois testes utilizem IEnumerable com tipos complexos, e o ZLinq não utilize um &lt;code&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt;, alocações passam a ocorrer, como aquelas que ocorrem com o Linq.&lt;/p&gt;

&lt;p&gt;Veja:&lt;/p&gt;

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

&lt;p&gt;Além da alocação próxima, o tempo de execução foi um pouco maior. Ter esse conhecimento é interessante para evitar o uso indiscriminado da biblioteca. O ideal é utilizá-la quando há um cenário propício, no caso quando se usa &lt;code&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;ZLinq é uma ferramenta que entrega um desempenho ótimo ao lidarmos com &lt;code&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; tendo a necessidade de utilizar expressões Linq. &lt;br&gt;
A biblioteca ainda está em começo de vida, conta com pouco mais de 20k downloads, mas é  mantida pelo mesmo criador do &lt;a href="https://github.com/MessagePack-CSharp/MessagePack-CSharp" rel="noopener noreferrer"&gt;MessagePack-CSharp&lt;/a&gt; sobre o qual já falamos &lt;a href="https://dev.to/wsantosdev/messagepack-2-comunicando-apis-asp-net-core-3ni4"&gt;aqui&lt;/a&gt;. &lt;br&gt;
Vale a pena esperar por mais funcionalidades e melhorias com o tempo.&lt;/p&gt;

&lt;p&gt;O código do benckmark está, como sempre, disponível no &lt;a href="https://github.com/wsantosdev/performance-zlinq" rel="noopener noreferrer"&gt;Github&lt;/a&gt;. Divirta-se e me diga o que achou.&lt;/p&gt;

&lt;p&gt;Gostou do post? Me deixe saber pelos indicadores. Tem dúvidas ou sugestões? Deixe um comentário ou me procure em minhas redes.&lt;/p&gt;

&lt;p&gt;Muito obrigado por ler até aqui, e até o próximo post.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>performance</category>
      <category>programming</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Dica Rápida: Ganhando desempenho com o modificador Sealed</title>
      <dc:creator>William Santos</dc:creator>
      <pubDate>Mon, 14 Apr 2025 12:07:21 +0000</pubDate>
      <link>https://forem.com/wsantosdev/dica-rapida-ganhando-desempenho-com-o-modificador-sealed-2n4l</link>
      <guid>https://forem.com/wsantosdev/dica-rapida-ganhando-desempenho-com-o-modificador-sealed-2n4l</guid>
      <description>&lt;p&gt;Olá!&lt;/p&gt;

&lt;p&gt;Esta é mais uma &lt;strong&gt;Dica Rápida&lt;/strong&gt; e aqui vou demonstrar como uma simples palavra-chave da linguagem pode fazer o desempenho de sua aplicação aumentar.&lt;/p&gt;

&lt;p&gt;Parece inacreditável? Pois acredite, não é!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conhecendo a palavra-chave sealed
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;sealed&lt;/code&gt; é uma palavra-chave que indica que uma dada classe não pode ser herdada, ou seja, que ela está fechada para extensão.&lt;/p&gt;

&lt;p&gt;O que essa palavra-chave esconde é uma informação muito útil ao compilador que, grosso modo é, ao visitar um método de uma classe &lt;code&gt;sealed&lt;/code&gt; é líquido e certo que seu tipo não conterá qualquer derivação e, por isso, o compilador &lt;em&gt;just in time&lt;/em&gt; (JIT) sabe que não precisa identificar se o tipo é herdado ou não. Um outro ponto é que, por conta desta identificação inequívoca, um método/membro invocado de uma classe &lt;code&gt;sealed&lt;/code&gt; pode ser colocado em linha com o invocador, deixando a chamada mais rápida -- como quando se usa o atributo &lt;code&gt;[MethodImpl(MethodImplOptions.AggressiveInlining)]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Veja só um exemplo de benchmarking bastante simples, baseado no código abaixo:&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;Sealed&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;BaseType&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;virtual&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;GetInt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1&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;class&lt;/span&gt; &lt;span class="nc"&gt;NonSealedType&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BaseType&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;override&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;GetInt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SealedType&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BaseType&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;override&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;GetInt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;NonSealedType&lt;/span&gt; &lt;span class="n"&gt;nonSealed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&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;SealedType&lt;/span&gt; &lt;span class="n"&gt;@sealed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Baseline&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="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;GetIntNonSealed&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;nonSealed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetInt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;GetIntSealed&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;@sealed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetInt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E o resultado foi o seguinte:&lt;/p&gt;

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

&lt;p&gt;Repare na diferença: quase 15x a favor da classe &lt;code&gt;sealed&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Por conta desta vantagem o time do .NET vem modificando e criando classes internas e privadas do runtime do .NET para &lt;code&gt;sealed&lt;/code&gt;, de modo a melhorar seu desempenho -- aqui um &lt;a href="https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/#peanut-butter" rel="noopener noreferrer"&gt;post&lt;/a&gt; a respeito do assunto (em inglês).&lt;/p&gt;

&lt;p&gt;Como regra do polegar, recomendaria o seguinte:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Se a classe é privada/interna e não é herdada, então &lt;code&gt;sealed&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Se a classe é pública e não é desejável que ela seja herdada, então &lt;code&gt;sealed&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;De outro modo não faz sentido aplicar a palavra-chave.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gostou? Me deixe saber pelos indicadores. Caso deseje, deixe um comentário ou me procure nas redes sociais.&lt;/p&gt;

&lt;p&gt;Até a próxima!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>performance</category>
      <category>dotnet</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Design: Event Sourcing (5 anos depois)</title>
      <dc:creator>William Santos</dc:creator>
      <pubDate>Mon, 31 Mar 2025 12:20:38 +0000</pubDate>
      <link>https://forem.com/wsantosdev/design-event-sourcing-5-anos-depois-3fba</link>
      <guid>https://forem.com/wsantosdev/design-event-sourcing-5-anos-depois-3fba</guid>
      <description>&lt;p&gt;Olá!&lt;/p&gt;

&lt;p&gt;Este é mais um post da seção Design e nele trago uma atualização da série sobre &lt;a href="https://dev.to/wsantosdev/event-sourcing-parte-1-adicionando-suporte-a-eventos-ao-seu-modelo-me6"&gt;Event Sourcing&lt;/a&gt;, de 2020. Como o blog está comemorando 5 anos, entendi fazer sentido ilustrar o padrão com uma aplicação funcional.&lt;/p&gt;

&lt;p&gt;Este post foi inspirado pelo livro de &lt;a href="[Martin%20Dilger%20|%20LinkedIn](https://www.linkedin.com/in/martindilger/)"&gt;Martin Dilger&lt;/a&gt; chamado &lt;a href="https://leanpub.com/eventmodeling-and-eventsourcing" rel="noopener noreferrer"&gt;Understanding EventSourcing&lt;/a&gt; (em inglês). Sua abordagem é bastante interessante e, além disso, inclui uma explicação completa sobre modelagem de domínio orientada a eventos, a &lt;a href="https://eventmodeling.org/" rel="noopener noreferrer"&gt;Event Modeling&lt;/a&gt;, concebida por &lt;a href="https://www.linkedin.com/in/eventmodeling/" rel="noopener noreferrer"&gt;Adam Dymitruk&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;No repositório deste post estão diagramas de Event Modeling, que vou explorar no próximo post. Portanto não se preocupe caso sua leitura não seja óbvia logo de início.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de mais nada, muito obrigado!
&lt;/h2&gt;

&lt;p&gt;Antes de começarmos a explorar a aplicação, quero agradecer a você que esteve acompanhando este blog desde o início, 5 anos atrás. O apoio da comunidade é um excelente combustível para continuar a escrever (mesmo que com um hiato no meio do caminho) e cada curtida, comentário e compartilhamento ajuda muito. Então, muito obrigado!&lt;/p&gt;

&lt;p&gt;Sigamos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Event Sourcing
&lt;/h2&gt;

&lt;p&gt;Os módulos da aplicação de demonstração empregam este pattern para registrar o histórico de suas mudanças de estado enquanto, ao mesmo tempo, nos permite criar projeções (visões) nos mais diferentes moldes para uso analítico pelo negócio.&lt;/p&gt;

&lt;p&gt;Vamos ver o que o livro nos diz a esse respeito (em tradução livre): &lt;em&gt;"É sobre armazenar informações e construir diferentes visões a partir dos dados na EventStore. (...) Eventos são simples fatos e de forma alguma técnicos. Sempre que algo acontece no sistema nós armazenamos como um Evento. Então, em termos simples, Eventos são registros do passado, portanto os escrevemos no passado. Em vez de 'cliente enviando um pedido' dizemos 'Um pedido foi enviado', ou encurtando, 'PedidoEnviado'. Um pedido foi enviado e não há como mudar esse fato. Mesmo excluindo o pedido de sua base de dados não mudará o fato de que um pedido foi enviado antes".&lt;/em&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Snapshots
&lt;/h3&gt;

&lt;p&gt;Como dito no &lt;a href="https://dev.to/wsantosdev/event-sourcing-parte-3-snapshots-1mjm"&gt;post original&lt;/a&gt; sobre o tema, snapshots são uma forma útil de reduzir a carga sobre nosso Event Store, criando um ponto de restauração e demandando que apenas os eventos que lhes sejam posteriores sejam carregados, fazendo com que a obtenção de nosso modelo de escrita, nossa entidade, tenha um desempenho melhor.&lt;br&gt;
O livro sugere o mesmo em outras palavras (como sempre, em tradução livre): &lt;em&gt;"Snapshots podem ser usados quando você deseja limitar o número de eventos que você precisa preocessar em uma stream. Visto por esse ângulo, snapshots servem a um propósito semelhante ao cache: nós adicionamos ao cache o estado projetado do sistema em vez de calculá-lo em tempo de execução."&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Projeções
&lt;/h3&gt;

&lt;p&gt;Como dito acima, Event Sourcing nos permite criar projeções à medida em que os eventos ocorrem. Podemos criá-las a partir de qualquer requisito de negócio que envolva os dados dos eventos, e no mesmo momento em que estes são persistidos. &lt;br&gt;
Ou seja, podemos criar entradas de relatórios em tempo real, escolhendo uma estratégia que pode variar de acordo com os requisitos do negócio. Ao receber os eventos do sistema, o responsável pela criação das projeções pode, por exemplo, iterar em uma lista de Projetores para em cada um, gerar um projeção analítica diferente. Quando houver a necessidade de mais projeções, um novo projetor pode ser adicionado.&lt;/p&gt;

&lt;p&gt;Em nossa aplicação há o módulo Accounts, que atende à conta de um dado cliente. Esta conta pode sofrer créditos e débitos ao longo de seu ciclo de vida e, graças ao emprego de Event Sourcing, podemos extrair dados que respondam a perguntas como "quantos depósitos foram feitos pelos clientes no mês de março versus o mês de fevereiro?", ou "qual o valor total em depósitos dos clientes ao longo dos últimos doze meses?".&lt;br&gt;
Responder a essas perguntas é importante para o negócio, dado que decisões estratégicas podem ser tomadas a partir delas.&lt;/p&gt;
&lt;h2&gt;
  
  
  CQRS
&lt;/h2&gt;

&lt;p&gt;Como dito no &lt;a href="https://dev.to/wsantosdev/event-sourcing-parte-5-cqrs-2m6o"&gt;post original&lt;/a&gt; sobre o assunto, CQRS nos fornece uma forma de persistir projeções que atendam às necessidades da nossa aplicação. No caso da conta do cliente mencionado acima, podemos criar um modelo tão simples quanto o identificador do cliente e seu saldo (como fazemos na aplicação de demonstração) para que seja exibido, por exemplo, na tela inicial de um app ou numa página de um sistema web. Vale lembrar que é possível criar visões diferentes a partir de um mesmo evento, ou seja, seria possível também criar uma entrada de extrato com o que foi depositado/retirado pelo cliente, facilitando assim a exibição dos dados.&lt;/p&gt;
&lt;h3&gt;
  
  
  Consistência
&lt;/h3&gt;

&lt;p&gt;Algo muito importante ao se falar sobre CQRS é que muito se pensa que CQRS demanda, necessariamente, consistência eventual, e isso é um erro. Vamos recorrer ao livro mais uma vez para ilustrar esse caso (mais uma vez em tradução livre): &lt;em&gt;"Para muitos projetos a solução mais simples para atualizar todas as partes do sistema de uma vez é usar uma mesma base de dados para os modelos de escrita e leitura, o que não é proibido e nem uma má prática. Se você estiver utilizando um banco PostgreSQL para armazenar seus eventos assim como suas projeções é normalmente suficiente atualizá-los dentro de uma mesma transação. Essa abordagem garante consistência e simplifica muito o sistema, tornando fácil entender a respeito. Não subestime as vantagens da simplicidade. Eu me desviaria dela apenas se você tiver razões muito boas."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Em nossa aplicação de demonstração utilizei as duas formas de consistência, eventual para o módulo Account, e imediata, na mesma transação de armazenamento dos eventos, para os demais. Ao ler o código vai ficar clara a diferença entre ambas as abordagens, ainda que tenha simplificado muito a abordagem da consistência eventual para rodar no mesmo processo que a Web API.&lt;/p&gt;

&lt;p&gt;Bom, falamos algumas vezes sobre a aplicação de demonstração. Certo? Hora de conhecê-la!&lt;/p&gt;
&lt;h2&gt;
  
  
  Conhecendo a aplicação
&lt;/h2&gt;

&lt;p&gt;Esta aplicação é um &lt;a href="https://dev.to/wsantosdev/design-monolitos-modulares-parte-1-3fli"&gt;monolito modular&lt;/a&gt; composto pelos seguintes módulos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Account (Como já mencionado, atende à conta do cliente e suas movimentações)&lt;/li&gt;
&lt;li&gt;Orders (Ordens de compra e venda de ações)&lt;/li&gt;
&lt;li&gt;Exchange (Um simulador do comportamento da bolsa de valores, que executa as ordens)&lt;/li&gt;
&lt;li&gt;Positions (A visualização das ações que o usuário possui)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esses módulos são utilizados por uma Web API que expõe suas funcionalidades e compartilham a EventStore para registrar os eventos, assim como a Snapshot Store para os snapshots.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9lfj8gs00mkergjuo90.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9lfj8gs00mkergjuo90.png" alt="Solution view" width="574" height="800"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Introdução - Os fluxos
&lt;/h4&gt;

&lt;p&gt;Existem dois fluxos na aplicação, o de compra e de venda de ações. &lt;/p&gt;

&lt;p&gt;O fluxo de compra começa com um crédito em conta (&lt;code&gt;Deposit&lt;/code&gt; na WebApi), passa pela colocação de uma ordem (&lt;code&gt;PlaceOrder&lt;/code&gt;) que tem sua execução realizada pelo simulador de execução da bolsa de valores (&lt;code&gt;Execute&lt;/code&gt;) e termina com a possibilidade da consulta às ações compradas, que formam a posição do cliente (&lt;code&gt;Positions&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Já o fluxo de venda segue o caminho inverso. A partir das ações compradas coloca-se uma ordem de venda (&lt;code&gt;PlaceOrder&lt;/code&gt;), que também é executada pelo simulador da bolsa de valores (&lt;code&gt;Execute&lt;/code&gt;) e que gera um depósito na conta do cliente, o que lhe permite um saque (&lt;code&gt;Withdrawal&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;A Web API vem com um Swagger que te permite executar todos os comandos e consultar seus efeitos colaterais.&lt;/p&gt;
&lt;h4&gt;
  
  
  Parte 1 - Eventos
&lt;/h4&gt;

&lt;p&gt;Para aplicarmos o Event Sourcing criamos uma classe abstrata com o necessário para que todo e qualquer agregado consiga fazê-lo. Esta classe chama-se &lt;code&gt;EventBasedEntity&lt;/code&gt;. Veja o código abaixo:&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;namespace&lt;/span&gt; &lt;span class="nn"&gt;WSantosDev.EventSourcing.Commons.Modeling&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;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EventBasedEntity&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;set&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="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEvent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;UncommittedEvents&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&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;new&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;RaiseEvent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEvent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;TEvent&lt;/span&gt; &lt;span class="n"&gt;@event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TEvent&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEvent&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
            &lt;span class="n"&gt;UncommittedEvents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;@event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;ProcessEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;@event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;FeedEvents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEvent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&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;@event&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;@event&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="nf"&gt;ProcessEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;@event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ProcessEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEvent&lt;/span&gt; &lt;span class="n"&gt;@event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para facilitar a compreensão do código acima, vamos vê-lo em ação em nosso agregado Account e, em seguida, explicamos o conjunto.&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="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IError&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Credit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Money&lt;/span&gt; &lt;span class="n"&gt;amount&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;amount&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Zero&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;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IError&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvalidAmount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;RaiseEvent&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;AmountCredited&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&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;true&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;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ProcessEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEvent&lt;/span&gt; &lt;span class="n"&gt;@event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;@event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;AccountOpened&lt;/span&gt; &lt;span class="n"&gt;accountOpened&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;Apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountOpened&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;AmountCredited&lt;/span&gt; &lt;span class="n"&gt;amountCredited&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;Apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amountCredited&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;AmountDebited&lt;/span&gt; &lt;span class="n"&gt;amountDebited&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;Apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amountDebited&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;break&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;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AmountCredited&lt;/span&gt; &lt;span class="n"&gt;@event&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;_entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Credit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;@event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Repare que o código de &lt;code&gt;Account&lt;/code&gt; complementa o de &lt;code&gt;EventBasedEntity&lt;/code&gt;. Agora vamos às explicações.&lt;/p&gt;

&lt;p&gt;Quando um método do nosso agregado é acionado, seus argumentos são checadas (no caso se o valor depositado é maior que zero) e, em caso positivo, um evento é lançado. Neste caso lançamos o evento &lt;code&gt;AmountCredited&lt;/code&gt;, via &lt;code&gt;RaiseEvent&lt;/code&gt;, que vai atualizar o estado do módulo.&lt;/p&gt;

&lt;p&gt;Você pode estar se perguntando: mas não é mais simples apenas atribuir o valor do depósito a uma propriedade &lt;code&gt;Balance&lt;/code&gt; e seguir a vida? E a resposta é: não!&lt;/p&gt;

&lt;p&gt;Veja, quando lançamos um evento, alimentamos a lista &lt;code&gt;UncommitedEvents&lt;/code&gt; que representa o conjunto de eventos que ainda não foi persistido em nossa Event Store. E, como veremos à frente, essa lista é o que dá sentido ao Event Sourcing, da mesma forma que a versão do nosso agregado.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parte 2 - EventStore
&lt;/h3&gt;

&lt;p&gt;Algo interessante a dizer sobre a Event Store é que ela é agnóstica a mecanismo de persistência. Você poderia utilizar um arquivo caso quisesse -- isso não é uma recomendação!&lt;/p&gt;

&lt;p&gt;Em nossa demo utilizamos SQLite dado que bancos relacionais são a realidade da maioria de nós, mas poderia ser qualquer outro mecanismo de persistência que nos permita escrever uma versão serializada de nossos eventos, como vemos no código abaixo:&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EventDbContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DbContextOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EventDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;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="nf"&gt;DbContext&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;private&lt;/span&gt; &lt;span class="n"&gt;DbSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Events&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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;void&lt;/span&gt; &lt;span class="nf"&gt;AppendToStream&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;streamId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEvent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;events&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;eventsToAppend&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;EventSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&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;streamId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventsToAppend&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;internal&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;Event&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="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;StreamId&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;Created&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;EventType&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;Data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; utlizamos EF Core por simplicidade. Seu uso não é uma necessidade, assim como não é necessário qualquer outro ORM. O importante é ter acesso ao mecanismo de persistência e fazer operações de escrita de cada evento.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Repare que no código acima, o método &lt;code&gt;AppendToStream&lt;/code&gt; espera um &lt;code&gt;streamId&lt;/code&gt; e um tipo enumerado de &lt;code&gt;IEvent&lt;/code&gt;, que é a lista &lt;code&gt;UncommitedEvents&lt;/code&gt; que temos em nosso agregado.&lt;/p&gt;

&lt;p&gt;Repare também que os eventos são serializados para preencher a entidade &lt;code&gt;Event&lt;/code&gt; que será o registro de fato armazenado na base de dados.&lt;/p&gt;

&lt;p&gt;Vamos ver como funciona essa serialização:&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;internal&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EventSerializer&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;static&lt;/span&gt; &lt;span class="n"&gt;Event&lt;/span&gt; &lt;span class="n"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEvent&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;streamId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TEvent&lt;/span&gt; &lt;span class="n"&gt;@event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TEvent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEvent&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;eventId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="n"&gt;streamId&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;UtcNow&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="s"&gt;"yyyy-MM-dd HH:mm:ss"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                   &lt;span class="n"&gt;@event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetType&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;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;@event&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Então você poderia me perguntar: mas por que serializar? Por que não persistir o agregado e seguir a vida? Repare que recebemos &lt;code&gt;TEvent&lt;/code&gt; como parâmetro, isso para podermos permitir que qualquer tipo de evento seja persistido na base sem que tenhamos de nos preocupar com sua forma (nos preocupamos apenas com seu tipo para permitir a desserialização no futuro). Deste modo é muito mais simples persistir e é exatamente essa simplicidade que nos dá a flexibidade para escolher qualquer meio para persistir nossos eventos.&lt;/p&gt;

&lt;p&gt;Com este código conseguimos persistir os eventos que constituem o agregado com sucesso, precisando apenas recuperá-los em ordem (daí a importância do &lt;code&gt;Version&lt;/code&gt; em &lt;code&gt;EventBasedEntity&lt;/code&gt;) para restaurá-lo e poder operar com ele.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parte 3 - Snapshot
&lt;/h3&gt;

&lt;p&gt;Como dito acima, às vezes temos um ciclo de vida muito longo para um agregado. Uma conta, por exemplo, pode existir e ser movimentada por anos, o que, em grande volume de usuários, poderia comprometer o desempenho do sistema.&lt;/p&gt;

&lt;p&gt;Assim sendo, podemos criar snapshots a partir do código abaixo, que nos permite definir um tipo que será persistido e poderá ser recuperado no futuro para facilitar a restauração do agregado.&lt;/p&gt;

&lt;p&gt;Veja como funciona o snapshot pelo código de &lt;code&gt;Account&lt;/code&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EventBasedEntity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ISnapshotable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AccountSnapshot&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;ShouldTakeSnapshot&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;_entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="m"&gt;3&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="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;AccountSnapshot&lt;/span&gt; &lt;span class="nf"&gt;TakeSnapshot&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;new&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AccountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[..&lt;/span&gt; &lt;span class="n"&gt;_entries&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A interface &lt;code&gt;ISnapshotable&amp;lt;T&amp;gt;&lt;/code&gt; nos fornece os dois métodos acima para decidir se devemos ou não criar um snapshot e para gerar o tipo de snapshot que será persistido.&lt;/p&gt;

&lt;p&gt;Aqui usamos o tipo &lt;code&gt;AccountSnapshot&lt;/code&gt; que é uma representação do estado atual do agregado, neste caso seu id, sua versão e o conjunto de entradas que possui neste momento. Da mesma forma que um evento, este snapshot será serializado e persistido, com a diferença de que novos snapshots vão sobrescrever o original em vez de ser adicionados para viver junto com ele.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parte 4 - Projeções
&lt;/h3&gt;

&lt;p&gt;Aqui temos a criação de nosso modelo de leitura (ou &lt;em&gt;view&lt;/em&gt;) que será consultado quando necessário pela nossa aplicação.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; no caso específico de &lt;code&gt;Account&lt;/code&gt; optamos por simular um modelo de consistência eventual, e digo simular porque, em vez de utilizarmos um mecanismo de mensageria (como o RabbitMQ) ou event stream (como o Kafka), para receber os eventos de domínio e convertê-los em projeções, utilizamos um bus em memória por simplicidade.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No caso de Account, sempre que um evento do Event Sourcing é lançado, o comando que operou sobre o agregado irá lançar um evento de domínio correspondente, que será interceptado por um manipulador de eventos de domínio que o converterá em uma projeção.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; No caso dos demais módulos optamos por um modelo de consistência imediata. Em vez de lançarmos um evento de domínio correlato ao comando executado, persistimos a projeção na mesma transação em que persistimos os eventos do Event Sourcing (o que só é possível porque ambas as persistências estão na mesma base). Vale a pena examinar as diferenças entre ambas as abordagens.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Vamos examinar um desses manipuladores de eventos de domínio para entender como a projeção é criada.&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;AccountOpenedHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AccountViewDbContext&lt;/span&gt; &lt;span class="n"&gt;viewDbContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IMessageHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AccountOpened&lt;/span&gt;&lt;span class="p"&gt;&amp;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;HandleAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AccountOpened&lt;/span&gt; &lt;span class="n"&gt;message&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;view&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AccountView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AccountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InitialDeposit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;viewDbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&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;viewDbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Veja que é um manipulador bem simples, que reage a abertura de uma conta. Ele apenas cria uma projeção da conta, com seu id e o valor de seu depósito inicial, e o persiste em uma base de &lt;em&gt;views&lt;/em&gt; (que, em nosso caso, é a mesma base onde persistimos os eventos, mais uma vez por simplicidade).&lt;/p&gt;

&lt;p&gt;Pronto! A partir de agora podemos consultar essa projeção para sabermos qual é o saldo de uma conta no momento da consulta.&lt;/p&gt;

&lt;p&gt;Repare em algo interessante: essa &lt;em&gt;view&lt;/em&gt; é uma versão customizada de nosso agregado e sequer é fiel a ele. Não existem entradas, apenas o id da conta e seu saldo. Isso porque é apenas esse dado de que precisamos na aplicação. Se precisássemos de um histórico de transações, um extrato, poderíamos criar uma segunda &lt;em&gt;view&lt;/em&gt; que contivesse o número da conta, a data da movimentação, seu tipo (abertura de conta, crédito ou débito) e o valor.&lt;br&gt;
Esse é o poder do CQRS: é possível criar qualquer visualização desejada a partir do estado da aplicação, inclusive para, como dito acima, a geração de relatórios que a aplicação não consuma e que sirva apenas a fins analíticos.&lt;/p&gt;

&lt;p&gt;Excelente! Não?&lt;/p&gt;
&lt;h3&gt;
  
  
  Parte 5 - Juntando as peças
&lt;/h3&gt;

&lt;p&gt;Agora que entendemos as peças fundamentais do Event Sourcing/CQRS, podemos examinar nossa Web API que é quem fornecerá nossas funcionalidades ao mundo.&lt;/p&gt;

&lt;p&gt;Aqui encontraremos comandos e consultas que nos permitirão, a partir da nossa necessidade, obter dados das projeções ou restaurar o estado de nosso agregado para realizar operações.&lt;/p&gt;

&lt;p&gt;Vamos ver um exemplo do &lt;em&gt;controller&lt;/em&gt; de crédito em conta.&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;CreditController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Credit&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&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="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;"Credit"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ProducesResponseType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status200OK&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ProducesResponseType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status404NotFound&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ProducesResponseType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status500InternalServerError&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;Credit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreditRequest&lt;/span&gt; &lt;span class="n"&gt;request&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;credited&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;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteAsync&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;CreditParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Constants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultAccountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&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;credited&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credited&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorValue&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;InvalidAmountError&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;BadRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Invalid amount. The amount should be greater than zero."&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;StatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status500InternalServerError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Unspecified error."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Aqui temos o seguinte: recebemos uma instância de um comando de crédito que encapsula toda a lógica necessária a essa operação. Ele restaura o agregado, invoca seu método de crédito, persiste e lança o evento de domínio correspondente. No &lt;em&gt;controller&lt;/em&gt; temos apenas a execução do comando com seus respectivos parâmetros (o id da conta e o valor do depósito).&lt;/p&gt;

&lt;p&gt;Se o comando for bem sucedido, retornamos um &lt;code&gt;Ok (200)&lt;/code&gt;, do contrário, se a falha ocorreu por conta da invariante do agregado (depósito com valor maior que zero), retornamos um &lt;code&gt;Bad Request (400)&lt;/code&gt; e, caso a falha seja de outra natureza, retornamos um &lt;code&gt;InternalServerError (500)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Simples. Não?&lt;/p&gt;

&lt;h4&gt;
  
  
  Vertical Slices
&lt;/h4&gt;

&lt;p&gt;Repare que nosso &lt;em&gt;controller&lt;/em&gt; se chama &lt;code&gt;CreditController&lt;/code&gt;, ou seja, ele se responsabiliza apenas pelo depósito de valores na conta de um cliente. Essa abordagem, que é orientada a tarefas e não a recursos, se chama &lt;em&gt;vertical slices&lt;/em&gt; (fatias verticais em tradução livre), e é uma forma de segregar as ações possíveis no software de acordo com seu propósito. &lt;br&gt;
Ou seja, para verificarmos o saldo em conta de um cliente, teremos um &lt;code&gt;BalanceController&lt;/code&gt;, para realizar um débito na conta, um &lt;code&gt;DebitController&lt;/code&gt; e por aí vai. Isso em vez de ter um único &lt;em&gt;controller&lt;/em&gt; que reúna todas as operações possíveis em Account, o que pode se tornar difícil de manter e evidencia a simplicidade do próprio design da aplicação (se um controller está muito grande, é possível que o design não esteja adequado à solução).&lt;/p&gt;

&lt;p&gt;Aqui temos uma breve apresentação deste padrão segundo o livro (sempre em tradução livre): &lt;em&gt;"Até onde sei o termo 'Vertical Slice Architecture' foi cunhado por Jimmy Bogard em uma série de artigos por volta de 2019. Seu principal mote é 'minimizar o acoplamento entre as fatias e maximizar o acoplamento dentro de uma fatia'"&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Pode soar um pouco estranho ler o trecho "maximizar o acoplamento dentro da fatia", mas foi exatamente o que fizemos em nosso caso. Temos um controller apenas para a fatia de crédito, assim como um manipulador de comando e um evento de domínio. Todos esses elementos bastante acoplados entre si pois são todos dependências da funcionlidade de crédito.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; assim como tratado no post &lt;a href="https://dev.to/wsantosdev/design-cqrs-desfazendo-mal-entendidos-5bpo"&gt;CQRS - Desfazendo mal-entendidos&lt;/a&gt;, não há a menor necessidade de utilizarmos o famoso MediatR. Implementamos a injeção do manipulador de comando diretamente no controller pois nossa intenção é realizar uma única operação, o que acaba por tirar a razão de existir do MediatR que é notificar N manipuladores de forma desacoplada. Vale a pena evidenciar a ausência de ligação entre uma coisa e outra.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Como pudemos ver, Event Sourcing traz uma vantagem e um poder enorme para uma aplicação, nos permitindo conhecer por completo o histórico de mudanças de seu estado e, ao mesmo tempo, provendo uma forma de gerar visões distintas para propósitos específicos. É evidente que Event Sourcing não é uma necessidade para toda e qualquer aplicação mas, quando faz sentido, é um instrumento poderossímo e aliado do negócio -- que é a razão pela qual a aplicação existe.&lt;/p&gt;

&lt;p&gt;Espero que tenha gostado desta releitura 5 anos depois e que se divirta com o &lt;a href="https://github.com/wsantosdev/event-sourcing-cqrs" rel="noopener noreferrer"&gt;código da aplicação&lt;/a&gt; de demonstração para conhecê-la em detalhes.&lt;/p&gt;

&lt;p&gt;Se gostou, me deixe saber pelos indicadores do post e, caso queira, deixe um comentário, principalmente se tiver alguma dúvida.&lt;/p&gt;

&lt;p&gt;Muito obrigado pela leitura e até o próximo post!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>designpatterns</category>
      <category>systemdesign</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Playground: Jan Ken Po</title>
      <dc:creator>William Santos</dc:creator>
      <pubDate>Sun, 08 Sep 2024 16:03:32 +0000</pubDate>
      <link>https://forem.com/wsantosdev/playground-jan-ken-po-2l53</link>
      <guid>https://forem.com/wsantosdev/playground-jan-ken-po-2l53</guid>
      <description>&lt;p&gt;Olá!&lt;/p&gt;

&lt;p&gt;Este é mais um post da seção &lt;strong&gt;Playground&lt;/strong&gt; e, neste, não trago nada de mais além de um pouco de saudosismo.&lt;/p&gt;

&lt;p&gt;Pois é! O famoso jogo de Pedra, Papel e Tesoura foi o primeiro programa que escrevi além da sala de aula e, enquanto jogava Alex Kidd in the Miracle World DX, acabei me lembrando disso e escrevendo uma versão bem simples em C# apenas por diversão - diversão esta que compartilho agora por aqui.&lt;/p&gt;

&lt;p&gt;Como de costume o código está no &lt;a href="https://github.com/wsantosdev/jan-ken-po" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Espero que goste. Apesar de um post mais simples, espero seu feedback. Se tiver gostado, me deixe saber pelos indicadores. Fique à vontade, também, para me procurar em minhas redes sociais.&lt;/p&gt;

&lt;p&gt;Até mais!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>cli</category>
      <category>development</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Além do Código: Linguagem Ubíqua e Idioma do Código</title>
      <dc:creator>William Santos</dc:creator>
      <pubDate>Mon, 19 Aug 2024 11:02:47 +0000</pubDate>
      <link>https://forem.com/wsantosdev/alem-do-codigo-linguagem-ubiqua-e-idioma-do-codigo-197o</link>
      <guid>https://forem.com/wsantosdev/alem-do-codigo-linguagem-ubiqua-e-idioma-do-codigo-197o</guid>
      <description>&lt;p&gt;Olá! Este é um post da seção &lt;strong&gt;Além do Código&lt;/strong&gt;, onde pretendo discutir algumas questões que influenciam o desenvolvimento de software mas não necessariamente tratam de implementações.&lt;/p&gt;

&lt;p&gt;Neste post falarei um pouco sobre a relação entre Linguagem Ubíqua e o idioma utilizado no código. Esta é uma questão bastante frequente e, muito embora pareça simples, não é exatamente assim.&lt;/p&gt;

&lt;p&gt;Vamos lá!&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é a Linguagem Ubíqua
&lt;/h2&gt;

&lt;p&gt;A Linguagem Ubíqua é um dos aspectos centrais em Domain-Driven Design (DDD) e diz respeito à forma como é feita a comunicação entre pessoas de negócio (especialistas de domínio) e engenharia dentro do que o DDD vai chamar de Contextos Delimitados, conceito que ficará para futuros posts. &lt;/p&gt;

&lt;p&gt;Aqui temos uma definição muito boa de Martin Fowler a respeito, em tradução livre:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;É a prática de construir uma linguagem comum e rigorosa entre desenvolvedores e usuários. &lt;br&gt;
Deve se basear no modelo de domínio e, por isso, precisa ser rigorosa pois software não lida bem com ambiguidade.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Repare na palavra "rigorosa" acima. Ela indica que a linguagem deve ser a mais precisa possível a ponto de não admitir mais de um termo para se referir a um mesmo elemento do Modelo de Domínio (este também assunto para futuros posts), pois, desta forma, o risco de ruído na comunicação é drasticamente reduzido.&lt;/p&gt;

&lt;h2&gt;
  
  
  A questão do idioma
&lt;/h2&gt;

&lt;p&gt;Quero discutir essa questão à luz do DDD, ou seja, como um praticante lidaria com ela. Ao mesmo tempo, levarei em consideração o fato de estarmos no Brasil e como isso influencia a decisão.&lt;/p&gt;

&lt;h3&gt;
  
  
  Código em Inglês
&lt;/h3&gt;

&lt;p&gt;Muita gente acredita que codificar em inglês é o padrão e assim deve ser porque as linguagens de programação utilizam a lingua inglesa para oferecer suas instruções.&lt;/p&gt;

&lt;p&gt;Quero desafiar esta noção com as seguintes perguntas:&lt;br&gt;
1- Sendo a Linguagem Ubíqua baseada em elementos do negócio, faz sentido traduzi-los para o inglês no código se não for o idioma falado pelos especialistas de domínio? Seu time é capaz de garantir que não confundirá termos nos dois idiomas ao se comunicar com os especialistas de domínio de modo a levar esta confusão ao código? &lt;/p&gt;

&lt;p&gt;2- Seu time tem fluencia não apenas em inglês como, também, nos termos de negócio quando nesse idioma? O código poderia ser lido por um anglófono (falante de inglês) de modo a ser compreendido sem a necessidade de recorrer a membros do time para "traduzir a tradução"?&lt;/p&gt;

&lt;p&gt;Aqui é preciso lembrar que apenas 1% da população brasileira tem fluência em inglês e que, com certeza, nem todos nesse 1% atuam desenvolvendo software. Ou seja, as chances de haver erros importantes no código que possam criar confusão são grandes o bastante para gerar preocupação.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Exemplos simples&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;No mercado de capitais é muito comum falarmos em "ações". Ações são uma fração da propriedade de uma empresa e, em inglês, diz-se "stocks". Entretanto, em códigos que já tive a oportunidade de atuar, vi este termo traduzido literalmente como "actions".&lt;br&gt;
Ao mesmo tempo, em contabilidade, há o termo "lançamento contábil", que é o ato de registrar uma movimentação financeira, que, em inglês, é chamado de "journal entry", e já o vi sendo traduzido como "launch", que é lançamento no sentido de projeção, como no lançamento de foguetes.&lt;/p&gt;

&lt;p&gt;Partindo desses exemplos, deixo a pergunta: como um falante de inglês interpretaria esses termos? Faria sentido pra ele? E mais, se não é objetivo promover a interação de um anglófono, qual o sentido em traduzir os termos em primeiro lugar?&lt;/p&gt;

&lt;h3&gt;
  
  
  Código Híbrido
&lt;/h3&gt;

&lt;p&gt;Esta é uma modalidade de código onde elementos técnicos são tratados em inglês, mas elementos do domínio são tratados, no nosso caso, em portugês.&lt;/p&gt;

&lt;p&gt;Um exemplo simples seria uma classe chamada &lt;code&gt;BoletoRepository&lt;/code&gt;, responsável pela persistência e recuperação de boletos junto a um banco de dados, cujo método de persistência seja &lt;code&gt;Save&lt;/code&gt; e o de recuperação se chame &lt;code&gt;GetById&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Aqui já temos um sinal claro de como essa abordagem pode ser problemática. Uma pergunta que eu faria é: o ID de um boleto é algo conhecido pelo negócio? Em caso positivo, é chamado de ID? Ou "identificador"? Se for chamado de identificador, há uma tradução que se torna difícil saber se este é um aspecto técnico (um ID na base de dados por exemplo) ou um aspecto de negócio. Ou seja, perde-se a rigidez pretendida pela Linguagem Ubíqua e pode-se gerar confusão, inclusive, caso um novo membro chegue à equipe, já que ele provavelmente entenderia, ao ler o código, tratar-se de uma questão técnica e não de negócio até que isso fosse esclarecido.&lt;br&gt;
E aqui estamos falando apenas de um ID. Agora considere esse mesmo tipo de confusão distribuída pelo código por meio de diversos termos. Faz sentido assumir esse risco?&lt;/p&gt;

&lt;h3&gt;
  
  
  Código em Português
&lt;/h3&gt;

&lt;p&gt;Este, talvez, seja o mais controverso entre os modelos possíveis. Isso porque existe, da mesma forma que uma predileção pelo inglês, uma forte rejeição ao português.&lt;/p&gt;

&lt;p&gt;Vamos pegar o exemplo acima, de código híbrido, e fazer um teste:&lt;br&gt;
Para o caso do repositório de boletos teríamos uma classe chamada &lt;code&gt;RepositorioDeBoletos&lt;/code&gt;, com os métodos &lt;code&gt;Salvar&lt;/code&gt; e &lt;code&gt;ObterViaIdentificador&lt;/code&gt;. E então fica a pergunta: qual o real problema com este código? Ele é claro o bastante para indicar a ação sendo realizada bem como seu propósito e, mais importante, sendo português o idioma do negócio, o custo de tradução entre idiomas é zero. Ao mesmo tempo, por estar no idioma do negócio, um novo membro do time entenderia que o &lt;code&gt;Identificador&lt;/code&gt; é um conceito de negócio e não um componente técnico. Parece bom. Não?&lt;/p&gt;

&lt;p&gt;Isso significa que estou recomendando o uso de português? Não. Lembre-se de que, acima, afirmei que parece uma questão simples mas não é tanto, e agora vou explicar o porquê.&lt;/p&gt;

&lt;h3&gt;
  
  
  Determinismo Organizacional
&lt;/h3&gt;

&lt;p&gt;Esse termo serve para dizer o seguinte: essa é uma questão cultural na organização. E aqui deixo três perguntas para provocar uma reflexão: &lt;br&gt;
1- Sua organização tem/terá especialistas de domínio anglófonos, ou falantes de outros idiomas, demandando que toda a comunicação seja feita em inglês como língua franca? &lt;br&gt;
2- Os membros do time estão aptos a codificar em inglês sem perder significados, evitando cair no problema da "tradução da tradução"?&lt;br&gt;
3- Assumindo que sua organização contrate desenvolvedores de outros países, mas que mantenha o idioma do negócio em português, o que é mais barato? Qualificar os envolvidos para se comunicar em inglês? Ou trazer os anglófonos para o português?&lt;/p&gt;

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

&lt;p&gt;Perceba que não há uma resposta simples e definitiva para esta pergunta. Cada organização precisa tratar do assunto com cautela, lembrando: caso optem por utilizar DDD como ferramenta para descrever o domínio, pois o custo de mudança é alto e as consequências podem ser negativas a depender da abordagem escolhida – é uma decisão mais importante do que parece!&lt;/p&gt;

&lt;p&gt;Gostou deste post? Me deixe saber pelos indicadores. Fique à vontade para comentar ou me procurar em minhas redes sociais para prosseguir com o assunto.&lt;/p&gt;

&lt;p&gt;Muito obrigado pela leitura, e até o próximo post.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>ddd</category>
      <category>architecture</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Design: Imutabilidade</title>
      <dc:creator>William Santos</dc:creator>
      <pubDate>Mon, 20 May 2024 13:00:37 +0000</pubDate>
      <link>https://forem.com/wsantosdev/design-imutabilidade-3356</link>
      <guid>https://forem.com/wsantosdev/design-imutabilidade-3356</guid>
      <description>&lt;p&gt;Olá!&lt;/p&gt;

&lt;p&gt;Este é mais um post da seção Design e, nele, falaremos sobre um conceito fundamental da programação funcional cada vez mais presente no universo orientado a objetos: imutabilidade.&lt;/p&gt;

&lt;p&gt;A ideia é trazer as motivações, benefícios, e mostrar como é possível implementá-la usando C#.&lt;/p&gt;

&lt;p&gt;Vamos lá!&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é imutabilidade
&lt;/h2&gt;

&lt;p&gt;Imutabilidade é a característica de uma dada instância de uma estrutura de dados de ter seu estado preservado, e imune a mudanças, uma vez criado. Isso significa que, quando um novo valor se fizer necessário a uma de suas propriedade, uma nova cópia desta instância.&lt;/p&gt;

&lt;p&gt;Essa abordagem traz algumas vantagens, e algumas preocupações, algumas não muito óbvias, e vamos explorá-las a seguir.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-threading e Paralelismo
&lt;/h3&gt;

&lt;p&gt;A primeira grande vantagem trazida pela imutabilidade a preservação do estado de um objeto quando acessado por múltiplas threads (&lt;em&gt;thread safety&lt;/em&gt;) ou em paralelo (concorrência). Em cenários onde um objeto é mutável é necessário criar mecanismo de sincronização (a forma mais comum é via &lt;code&gt;lock&lt;/code&gt;) o que acaba trazendo complexidade à aplicação dado que o programador precisa se preocupar em checar se todo lugar que acessa esse objeto com a finalidade de mudar seu estado está sendo sincronizado adequadamente.&lt;/p&gt;

&lt;p&gt;Uma vez que um dado objeto é imutável, todas as threads poderão acessá-lo para fins de leitura e, caso seja necessário promover uma mudança de estado, um novo objeto é criado e passa a ser compartilhável, sem a necessidade de se preocupar com locks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Legibilidade, Debugging e Testes
&lt;/h3&gt;

&lt;p&gt;Outra vantagem muito interessante, embora não tão óbvia, é o aumento da legibilidade do código, e da facilitação de testes e debugging. &lt;br&gt;
Isso porque, como o objeto não sofre mudanças, basta procurar os pontos onde novos objetos são criados para verificar se há erro em sua inicialização, o que aumenta a eficiência já que é algo mais fácil de encontrar do que pontos de mudança, principalmente se essas mudanças não indicam o tipo do objeto que está sendo modificado (quando &lt;em&gt;var&lt;/em&gt; é utilizado, por exemplo).&lt;/p&gt;
&lt;h3&gt;
  
  
  Desempenho
&lt;/h3&gt;

&lt;p&gt;Dado que mudanças não são possíveis em um objeto, e a criação de uma nova instância é exigida, há um potencial uso aumentado de memória, sobretudo no caminho crítico da aplicação se houver um uso intensivo de recursos. &lt;br&gt;
Nestes casos um design que considere esse uso intensivo e a necessidade de evitar mudanças é fundamental.&lt;br&gt;
Linguagens funcionais são otimizadas para este cenário, com recursos como compartilhamento do endereço de memória das propriedades das instâncias, assim uma instância nova não precisa copiar dados, apenas referências, algo muito mais eficiente.&lt;/p&gt;
&lt;h2&gt;
  
  
  Imutabilidade e &lt;code&gt;C#&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Em suas últimas versões o time do .NET tem investido muito em recursos para tornar o C# compatível com imutabilidade, e vamos conferir os principais.&lt;/p&gt;
&lt;h3&gt;
  
  
  Readonly Structs
&lt;/h3&gt;

&lt;p&gt;Este foi um dos primeiros recursos da linguagem para tratar imutabilidade. Quando declaradas com a palavra chave &lt;code&gt;readonly&lt;/code&gt; a instância passa a ter mudanças impedidas. Desta forma, qualquer tentativa de mudança, inclusive por métodos da própria instância, não funcionará.&lt;/p&gt;

&lt;p&gt;Vamos ver um exemplo em código:&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;readonly&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Struct&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;_value&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;Struct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="k"&gt;value&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;_value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&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;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="k"&gt;value&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;_value&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="k"&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;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&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;@struct&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;Struct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;@struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Value: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;@struct&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;//3&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; a forma pela qual o compilador garante a imutabilidade de &lt;em&gt;readonly structs&lt;/em&gt; são as chamadas cópias defensivas (&lt;em&gt;defensive copies&lt;/em&gt;). Em termos simples, toda vez que a instância precisa ser acessada, uma cópia dela em seu estado original é criada. Desta forma a instância original tem seu estado protegido, ainda que ao custo de maior consumo da &lt;em&gt;stack&lt;/em&gt;. Há formas de resolver este problema, basicamente passando a instância por referência usando a palavra-chave &lt;code&gt;in&lt;/code&gt; na assinatura do método que irá recebê-la, ou por meio do uso de &lt;code&gt;ref readonly&lt;/code&gt; ao utilizar uma instância que esteja em um escopo maior.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Records
&lt;/h3&gt;

&lt;p&gt;Outro tipo que traz imutabilidade por design é o &lt;code&gt;record&lt;/code&gt;. Uma vez inicializado seus valores não podem mais ser alterados, o que torna possível ter classes imutáveis (uma vez que records são reference types).&lt;/p&gt;

&lt;p&gt;Um detalhe que precisa ser levado em consideração, no entanto, é que records podem se tornar mutáveis caso suas propriedades sejam declaradas com &lt;em&gt;setters&lt;/em&gt;. Ou seja, para tirar vantagem da imutabilidade as propriedades devem ser declaradas com &lt;em&gt;initializers&lt;/em&gt; ou de forma posicional.&lt;/p&gt;

&lt;p&gt;Vejamos exemplos:&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;record&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;init&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="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;Age&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;init&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;É o equivalente a:&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;record&lt;/span&gt; &lt;span class="nc"&gt;Person&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;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Desta forma há a garantia de que nenhuma mudança possa ser realizada.&lt;/p&gt;

&lt;h3&gt;
  
  
  Campos Read-only
&lt;/h3&gt;

&lt;p&gt;Este é um recurso bastante conhecido que, basicamente, informa que um dado campo de um objeto pode ser apenas lido e que, obrigatoriamente, será inicializado (ou por atribuição no momento da declaração, ou via construtor).&lt;/p&gt;

&lt;p&gt;Vejamos como funciona:&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;Person&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;readonly&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Person Name"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Que equivale a:&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;Person&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;readonly&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Person&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;name&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;_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Desta forma, uma vez inicializado, name não pode ter seu valor modificado.&lt;/p&gt;

&lt;h3&gt;
  
  
  Criando Novas Instâncias
&lt;/h3&gt;

&lt;p&gt;Para facilitar a criação de novas instâncias, atribuindo apenas os valores novos, é a palavra chave &lt;code&gt;with&lt;/code&gt;. Ela faz com que o compilador atribua à nova instância todos as propriedades que não forem declaradas em seu escopo.&lt;/p&gt;

&lt;p&gt;Vejamos um exemplo:&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;record&lt;/span&gt; &lt;span class="nc"&gt;Person&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;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;Age&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;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&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;john&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"John"&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;robert&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;john&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Robert"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"John's age: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;john&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Age&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Robert's age: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;robert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Age&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;//John's age: 30, Robert's age: 30&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repare que, no exemplo acima, &lt;code&gt;john&lt;/code&gt; permanece inalterado e &lt;code&gt;robert&lt;/code&gt; tem apenas seu nome atribuído, tendo como sua idade a mesma de &lt;code&gt;john&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Considerações Finais
&lt;/h2&gt;

&lt;p&gt;Imutabilidade é um recurso excelente para conter bugs por concorrência, a preocupação com gestão de estado, simplicidade, previsibilidade e legibilidade do código.&lt;/p&gt;

&lt;p&gt;Essas vantagens tem um valor enorme e, ao mesmo tempo, os recursos oferecidos no C# permitem explorá-las com facilidade.&lt;/p&gt;

&lt;p&gt;Recomendo muito considerar como ferramenta de design utilizando ao mesmo tempo como métrica de qualidade, porque se um dado objeto está sofrendo muitas mudanças é provável que sua forma de uso esteja inadequada.&lt;/p&gt;

&lt;p&gt;Gostou? Deixe-me saber pelos indicadores. Tem dúvidas ou sugestões? Deixe um comentário ou me procure em minhas redes sociais.&lt;/p&gt;

&lt;p&gt;Até a próxima!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>functional</category>
      <category>softwaredevelopment</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Design - Toda regra tem uma exceção?</title>
      <dc:creator>William Santos</dc:creator>
      <pubDate>Mon, 29 Apr 2024 13:10:46 +0000</pubDate>
      <link>https://forem.com/wsantosdev/design-toda-regra-tem-uma-excecao-2llk</link>
      <guid>https://forem.com/wsantosdev/design-toda-regra-tem-uma-excecao-2llk</guid>
      <description>&lt;p&gt;Olá! Este é mais um post da seção &lt;strong&gt;Design&lt;/strong&gt; e é, também, uma versão revisada do post &lt;a href=""&gt;Desempenho - Toda regra tem uma exceção?&lt;/a&gt;. No post sobre desempenho exploro a piora do desempenho ao empregar exceções em nosso código de forma indiscriminada, como um fator de validação de regras de negócio.&lt;/p&gt;

&lt;p&gt;Neste pretendo abordar o tema exceções de forma mais ampla, explorando diferentes cenários onde exceções fazem ou não sentido.&lt;/p&gt;

&lt;p&gt;Vamos lá!&lt;/p&gt;

&lt;h2&gt;
  
  
  Exceções: usar ou não?
&lt;/h2&gt;

&lt;p&gt;Quando lemos a documentação da Microsoft sobre &lt;a href="https://learn.microsoft.com/en-us/dotnet/standard/exceptions/" rel="noopener noreferrer"&gt;lançamento e tratamento de exceções&lt;/a&gt; (em inglês), encontramos os seguintes trechos (em tradução livre): &lt;em&gt;"Uma exceção é qualquer condição de erro, ou comportamento inesperado, encontrada em um programa em execução. Exceções podem ser lançadas por conta de uma falha em seu código ou um código que você invoca (como em uma biblioteca compartilhada). &lt;br&gt;
(...) &lt;br&gt;
Sua aplicação pode se recuperar de algumas dessas condições mas de outras não. Embora você possa se recuperar da maioria das exceções de aplicação, você não pode da maioria das exceções do ambiente de execução. (...) &lt;br&gt;
Exceções oferecem vantagens sobre outros métodos de notificação de erros, como códigos de retorno. Falhas não passam desapercebidas porque se uma exceção é lançada e você não a trata o ambiente de execução encerra sua aplicação. Valores inválidos não seguem propagando pelo sistema como resultado de um código de erro não identificado."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fica claro que, a princípio, a Microsoft recomenda o uso de exceções. Certo?&lt;/p&gt;

&lt;p&gt;Bom, mais ou menos.&lt;/p&gt;

&lt;p&gt;Quando vemos a documentação sobre &lt;a href="https://learn.microsoft.com/en-us/dotnet/standard/exceptions/best-practices-for-exceptions" rel="noopener noreferrer"&gt;melhores práticas para exceções&lt;/a&gt; (em inglês), encontramos o seguinte trecho, também em tradução livre: &lt;em&gt;"Uma classe pode prover métodos e propriedades que permitam evitar uma chamada que poderia resultar em uma exceção. Por exemplo, a classe FileStream fornece métodos que ajudam a determinar quando o final de um arquivo foi alcançado. Esses métodos podem ser usados para evitar uma exceção que seria lançada caso você tentasse ler além do fim do arquivo. &lt;br&gt;
(...)&lt;br&gt;
Outra forma de evitar exceções é retornando null (ou default) para a maioria dos casos de erro em vez de lançar uma exceção. Uma causa comum de erro pode ser considerada um fluxo normal. Retornando null nestes casos você reduz o impacto sobre o desempenho da aplicação."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Perceba aqui a preocupação com desempenho. É preferível buscar evitar exceções usando &lt;code&gt;null&lt;/code&gt;, tendo como rede de proteção o possível lançamento de uma &lt;code&gt;NullReferenceException&lt;/code&gt;, do que lançar uma exceção de aplicação.&lt;/p&gt;

&lt;p&gt;Ou seja, exceções devem ser evitadas e, se usadas, que seja com moderação.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parêntese:&lt;/strong&gt; no post sobre desempenho mostro a diferença entre o retorno de um erro tipado e o lançamento de uma exceção. Fiz o mesmo experimento daquele post mas, agora, com o .NET 9 Preview 3. O resultado é o seguinte:&lt;/p&gt;

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

&lt;p&gt;Ok. Mas agora você pode estar se perguntando: não devo usar exceções então?&lt;/p&gt;

&lt;p&gt;Não é bem assim. Vejamos a seguir.&lt;/p&gt;
&lt;h3&gt;
  
  
  Cada caso é um caso (mas nem tanto)
&lt;/h3&gt;

&lt;p&gt;Sendo bem direto, entendo existir um único caso onde usar exceções é um imperativo: ao desenvolver uma biblioteca. Isso por um motivo muito simples: exceções são a forma padrão de notificação de erros no .NET e, se cada biblioteca tiver seu próprio meio de notificação a aplicação pode ficar muito confusa e propensa a erros.&lt;/p&gt;

&lt;p&gt;Entendo este caso como uma regra. Ou seja, não faz sentido empregar qualquer outro meio de notificação que não seja exceções. Um bom meio de observar isso é como o .NET funciona na interoperabilidade com componentes COM que, por natureza, retornam códigos de resultado (HRESULT). O ambiente de execução do .NET intercepta este código e, caso represente uma falha, lança uma exceção correspondente ao código de erro ou um COMException caso o código não lhe seja conhecido.&lt;/p&gt;

&lt;p&gt;Sobre o &lt;code&gt;null&lt;/code&gt;, não recomendo seu uso de forma alguma, e a Microsoft, mais recentemente, também não. &lt;br&gt;
Sim, entendo que o doc tem um trecho dedicado a essa abordagem (e suspeito que esteja desatualizado) mas devemos nos lembrar sempre do &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references" rel="noopener noreferrer"&gt;Nullable Reference Types&lt;/a&gt; (em inglês) que é um mecanismo para evitarmos situações que lancem a famigerada &lt;code&gt;NullReferenceException&lt;/code&gt;. Ou seja, não é uma boa ideia trabalhar com &lt;code&gt;null&lt;/code&gt; como retorno. Aliás, há um termo, cunhado pelo próprio criador do &lt;code&gt;null&lt;/code&gt;, Tony Hoare, que é o &lt;a href="https://www.youtube.com/watch?v=ybrQvs4x0Ps" rel="noopener noreferrer"&gt;Erro de um Bilhão de Dólares&lt;/a&gt; (em inglês). Se o próprio criador do &lt;code&gt;null&lt;/code&gt; o percebe como uma má ideia, faz sentido crer que seja mesmo!&lt;/p&gt;

&lt;p&gt;Neste caso, precisamos de outra abordagem, que vamos tratar a seguir.&lt;/p&gt;
&lt;h3&gt;
  
  
  Impedindo Erros
&lt;/h3&gt;

&lt;p&gt;Antes de mais nada precisamos entender que exceções são o meio mais seguro de notificar erros (não só no .NET mas imagino que em qualquer outro &lt;em&gt;runtime&lt;/em&gt;). &lt;br&gt;
Portanto, se você não se sente confortável para abrir mão delas, inclusive prefere sacrificar desepenho por essa segurança, é uma escolha legítima. &lt;br&gt;
A intenção deste post é sugerir uma abordagem que, a partir das melhores práticas sugeridas pela própria Microsoft, permita não apenas ganho de desempenho como, também, um código mais semântico.&lt;/p&gt;

&lt;p&gt;Vejamos como faze-lo.&lt;/p&gt;
&lt;h4&gt;
  
  
  Retornos Tipados
&lt;/h4&gt;

&lt;p&gt;A Microsoft recomenda não utilizar códigos de erro pois uma falha em sua checagem pode resultar em um estado inválido da aplicação, o que é um risco tão real quanto sério. É aqui que a segurança oferecida pelas exceções se mostra útil e, também, onde a abordagem aqui sugerida demonstra seu valor.&lt;/p&gt;

&lt;p&gt;A fim de evitar o retorno por código de erros é ideal que se utilize outros tipos de retorno, que demandem necessariamente uma checagem, para que o fluxo da aplicação possa seguir sem criar um estado inválido. &lt;br&gt;
Dois exemplos são &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; e &lt;code&gt;Result&amp;lt;TResult, TError&amp;gt;&lt;/code&gt; apresentados no post sobre &lt;a href="https://dev.to/wsantosdev/design-monadas-3ndb"&gt;Mônadas&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Option&amp;lt;T&amp;gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; é recomendado para métodos que precisam retornar um valor, como uma consulta ao banco de dados.&lt;br&gt;
Considere o seguinte exemplo:&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;HttpGet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;ById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&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;Option&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userDataAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetById&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui, caso &lt;code&gt;user&lt;/code&gt; não seja encontrado, retornamos um código 404 no método &lt;code&gt;ById&lt;/code&gt;.&lt;br&gt;
Mas vamos olhar mais de perto esse objeto de acesso a dados que é invocado:&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="n"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&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="k"&gt;try&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SingleOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u&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;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToOption&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="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//Logs, métricas e outros tratamentos&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repare que, por estarmos na borda da aplicação, podemos tratar uma exceção imediatamente se necessário e simplesmente retornar um &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; com ausência de valor (&lt;code&gt;None&lt;/code&gt;). &lt;br&gt;
Essa simples medida já nos poupa a penalização de desempenho  com a escalada da exceção pela pilha de chamadas (&lt;em&gt;call stack&lt;/em&gt;). Ao mesmo tempo, na execução da consulta, é possível evitar o valor nulo invocando o método &lt;code&gt;.ToOption()&lt;/code&gt; que o transformaria em um &lt;code&gt;Option&amp;lt;User&amp;gt;.None&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Talvez você esteja se perguntando se o uso de &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; não seria análogo ao de &lt;code&gt;null&lt;/code&gt; e a resposta é: sim e não. Sim porque é necessário um check para verificar se o valor existe para seguir com o fluxo caso os métodos próprios de &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; não sejam utilizados. E não porque o tipo de retorno deixa explícito que sempre haverá uma instância não nula como retorno, ainda que significando ausência de valor e, caso o desenvolvedor se esqueça de fazer a checagem, uma exceção de &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; a &lt;code&gt;NoneValueException&lt;/code&gt; vai se encarregar de servir de rede de proteção para que o fluxo seja interrompido.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; ao utilizar bibliotecas de terceiros, ou do framework que dependa de recursos do sistema, busque sempre utilizar um bloco &lt;code&gt;try-catch&lt;/code&gt; (principalmente se não houver documentação). Tal como dito na nota acima, é a forma padrão de notificação do .NET e, portanto, é esperado que algum método venha a lançar alguma.&lt;/p&gt;

&lt;p&gt;Para tratar eventuais nulos nesse tipo de componente, basta invocar o método desejado e adicionar &lt;code&gt;.ToOption()&lt;/code&gt; para que o mesmo seja convertido para um &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Result&amp;lt;TReturn, TError&amp;gt;
&lt;/h4&gt;

&lt;p&gt;Aqui temos outro exemplo de como evitar exceções, desta vez em operações que podem retornar erros tipados que emulam as exceções de aplicação.&lt;/p&gt;

&lt;p&gt;Considere o seguinte exemplo:&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;HttpPost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;AddAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AddAddressRequest&lt;/span&gt; &lt;span class="n"&gt;request&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;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Street&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZipCode&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;result&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;Created&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newAddress&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;result&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;InvalidStreetError&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Invalid Street"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;InvalidZipCodeError&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Invalid ZipCode"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Unspecified Error"&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui temos uma tentativa de criação de endereço, que está sujeita a certas validações. Vamos ver no detalhe como isso acontece.&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;Address&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;static&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IAddressError&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Create&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;street&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;zipCode&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;street&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;AddressErrors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvalidStreetError&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zipCode&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;AddressErrors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvalidZipCodeError&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;address&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;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IAddressError&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;struct&lt;/span&gt; &lt;span class="nc"&gt;InvalidStreetError&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IAddressError&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;struct&lt;/span&gt; &lt;span class="nc"&gt;InvalidZipCodeError&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IAddressError&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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddressErrors&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;InvalidStreetError&lt;/span&gt; &lt;span class="n"&gt;InvalidStreetError&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidStreetError&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;InvalidZipCodeError&lt;/span&gt; &lt;span class="n"&gt;InvalidZipCodeError&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidZipCodeError&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui vemos o método &lt;code&gt;Create&lt;/code&gt; validando os dados de entrada para a criação de endereços. Caso haja algum erro é retornado um tipo equivalente e, do contrário, é retornado o novo endereço.&lt;/p&gt;

&lt;p&gt;Desta forma, com um simples teste de resultado e um &lt;em&gt;pattern matching&lt;/em&gt;, que simula um bloco &lt;code&gt;try-catch&lt;/code&gt;, temos o que precisamos para decidir como seguir com o fluxo a partir de um sucesso ou erro.&lt;/p&gt;

&lt;h3&gt;
  
  
  Considerações Finais
&lt;/h3&gt;

&lt;p&gt;Neste post entendemos como exceções de aplicação (chamadas de &lt;em&gt;business exceptions&lt;/em&gt; no post original) não apenas prejudicam o desempenho como são desnecessárias e, por isso, devem ser evitadas a depender dos recursos disponíveis e pelo risco da applicação ser encerrada por força de uma exceção não tratada. Entendemos, também, como um antigo método de controle de fluxo, o retorno de &lt;code&gt;null&lt;/code&gt;, não é desejável.&lt;/p&gt;

&lt;p&gt;Para ter acesso aos tipos &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;Result&amp;lt;TResult, TError&amp;gt;&lt;/code&gt; entre outros, te convido a conhecer o &lt;a href="https://nuget.org/packages/moonad" rel="noopener noreferrer"&gt;Moonad&lt;/a&gt;, uma biblioteca que escrevi a partir das mônadas do F# para facilitar seu uso no C#.&lt;/p&gt;

&lt;p&gt;Gostou? Me deixe saber pelos indicadores. Fique à vontade para me procurar nas redes ou deixar comentários por aqui.&lt;/p&gt;

&lt;p&gt;Muito obrigado pela leitura e até a próxima!&lt;/p&gt;

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