<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rodolpho Alves</title>
    <description>The latest articles on DEV Community by Rodolpho Alves (@ardc_overflow).</description>
    <link>https://dev.to/ardc_overflow</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%2F414829%2F10bcad0e-2fc9-44f6-919a-40a1309d637a.jpg</url>
      <title>DEV Community: Rodolpho Alves</title>
      <link>https://dev.to/ardc_overflow</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ardc_overflow"/>
    <language>en</language>
    <item>
      <title>A brief intro to SLF4J and the Java logging hell</title>
      <dc:creator>Rodolpho Alves</dc:creator>
      <pubDate>Sun, 04 Sep 2022 18:06:08 +0000</pubDate>
      <link>https://dev.to/ardc_overflow/a-brief-intro-to-slf4j-and-the-java-logging-hell-2gfj</link>
      <guid>https://dev.to/ardc_overflow/a-brief-intro-to-slf4j-and-the-java-logging-hell-2gfj</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;This will be breaking my chain of Articles written in Brazilian Portuguese, I've fallen into the habit of making my study notes in English to make them easier to share. I'll soon translate everything here and create a translated version!&lt;/p&gt;

&lt;h2&gt;
  
  
  A brief history of logging in Java
&lt;/h2&gt;

&lt;p&gt;At the dawn of Java &lt;em&gt;logging&lt;/em&gt; still wasn’t as important as it is today, and as most languages of at that time everything boiled down to &lt;strong&gt;writing lines to the console&lt;/strong&gt; and that was good enough! &lt;em&gt;Why do you need standards and fancy structures when memory is sparse, right?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So it was common to see folks using the rather annoying &lt;code&gt;System.out&lt;/code&gt; and &lt;code&gt;System.err&lt;/code&gt; and doing their own solutions to write logs to files and other places!&lt;/p&gt;

&lt;h3&gt;
  
  
  log4j is born
&lt;/h3&gt;

&lt;p&gt;This is where the Open Source community first steps in! Since Java lacked any logging standard whatsoever and it’s “native logger” (being generous here given it was &lt;code&gt;err&lt;/code&gt; and &lt;code&gt;out&lt;/code&gt;...)  was rather poor when it came to features the community itself rose to the challenge and &lt;strong&gt;log4j&lt;/strong&gt; was created as a mean to provide &lt;strong&gt;extensible, configurable and standardized logging for Java.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nowadays log4j is infamous due to the &lt;a href="https://www.ncsc.gov.uk/information/log4j-vulnerability-what-everyone-needs-to-know"&gt;security incident&lt;/a&gt; that wreaked havoc across the industry  back in December 2021, but at its time &lt;strong&gt;it was a good thing&lt;/strong&gt;. Not perfect, but good!&lt;/p&gt;

&lt;p&gt;Of course Sun, the Java owner back then, quickly got wind of a need for standards and native ways to log stuff so they decided to make that happen! &lt;/p&gt;

&lt;h3&gt;
  
  
  The ‘native’ library enters the fray
&lt;/h3&gt;

&lt;p&gt;With JDK 1.4, in 2002, &lt;code&gt;java.util.logging&lt;/code&gt; was released as a native offering for logging. But… it wasn’t well received. First it was hard to decouple and isolate, then &lt;em&gt;for some unknown reason&lt;/em&gt; they also decided it’d be nice to reinvent the wheel and use a bunch of random names for log levels instead of the standardized ones the industry had. Thanks to that you had pearls such as being able to &lt;code&gt;log.fine("this is fine")&lt;/code&gt; but also being able to &lt;code&gt;log.finer("this is even finer!")&lt;/code&gt; and not to mention the best of all &lt;code&gt;log.finest("really great logging levels, innit?")&lt;/code&gt;... as you may have notice the way they decided to standardize log levels was quite &lt;code&gt;log.severe("not good")&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;🤦🏻 Worth noting that up to this day the &lt;code&gt;java.util.logging&lt;/code&gt; library keeps this standard due to backwards compatibility. So even when doing Kotlin stuff in Android you can still log great &lt;code&gt;fine&lt;/code&gt; and &lt;code&gt;severe&lt;/code&gt; messages if you wish!&lt;/p&gt;

&lt;h3&gt;
  
  
  I need to switch loggers! Now what?
&lt;/h3&gt;

&lt;p&gt;Now that there were two ‘popular’ contenders a new problem began to surface: What if I want to move my project from the amazing &lt;code&gt;java.util.logging&lt;/code&gt; library into &lt;code&gt;log4j&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Since both libraries were coupled to their own implementation that meant you’d need to refactor your whole code at once &lt;em&gt;or&lt;/em&gt; roll with two logging libraries for a while. You could also create your own interfaces and implementations to hide the logging details from caller! And that’s what the community did!&lt;/p&gt;

&lt;p&gt;Apache &lt;code&gt;common-logging&lt;/code&gt; was born to provide means to abstract the implementation details of any logging library and provide a &lt;strong&gt;common, standard, API that any Application may use without caring who’s writing the logs&lt;/strong&gt; behind the curtains! It had its shortcomings and wasn’t that widely adopted, but it’s idea lived on to see a better implementation be born. Enter &lt;strong&gt;SLF4J.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  SLF4J to the Rescue
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Simple Logging Facade for Java (SLF4J for short)&lt;/strong&gt; is an out-of-the-box set of &lt;code&gt;Interface&lt;/code&gt; and Abstractions that aim to &lt;strong&gt;consolidate and standardize logging in the JVM environment, while allowing decoupling.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The decoupling is achieved because &lt;strong&gt;you need only to rely on SLF4J interfaces&lt;/strong&gt; during our implementation. This wizardry is achievable &lt;strong&gt;because logging libraries itself need to provide “Wrappers” or “Adapters”&lt;/strong&gt; &lt;strong&gt;that plug into SLF4J abstractions&lt;/strong&gt; to then deliver their logging functionalities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.slf4j.Logger&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.slf4j.LoggerFactory&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * get a SL4J logger and do something
 */&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getLoggerAndLog&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my logger"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// note that this is a SLF4J Logger Interface!&lt;/span&gt;
    &lt;span class="c1"&gt;// whichever library is 'wrapped' by SLF4J now handles the levels, formatting and everything!&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;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"this is a debug message"&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;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"this is some information"&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;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"this is a warning!"&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"this is an 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;In practical terms what happens here is that you need at least two dependencies to be added to your project for this to work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;'org.slf4j:slf4j-api'&lt;/code&gt; for the SLF4J interfaces&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;log4j-slf4j18-impl&lt;/code&gt;, &lt;code&gt;com.github.tony19:logback-android&lt;/code&gt;, &lt;code&gt;ch.qos.logback:logback-classic&lt;/code&gt; for the SLF4J ‘providers’&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The provides themselves use &lt;code&gt;Reflection&lt;/code&gt; and other technologies to “tie” into the &lt;code&gt;LoggerFactory&lt;/code&gt; class and routines provided by SLF4J!&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a “Facade”
&lt;/h3&gt;

&lt;p&gt;The “Facade” within SLF4J stands for the &lt;a href="https://refactoring.guru/design-patterns/facade"&gt;Facade Design Pattern&lt;/a&gt;. In a nutshell &lt;strong&gt;a Facade attempts to provide a simpler (and common) access method to complex systems beneath it.&lt;/strong&gt; This is good because it &lt;strong&gt;can allow for decoupling between clients of those subsystems and themselves while also reducing the need to completely understand those subsystems!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine you want to buy something online. As a developer you know that you’re only inputting information in a website (or a mobile app) but you also know that &lt;em&gt;there’s something beneath it&lt;/em&gt;. Beneath every ecommerce out there there’ll be APIs, Messaging and Databases! &lt;strong&gt;In a way you could say that the Frontend is a Facade for everything that happens underneath.&lt;/strong&gt; As an online shopper you don’t need to know about which API calls are happening and which Messages are being sent over RabbitMQ, you &lt;strong&gt;just care that interacting with the ‘buy’ routine does all the magic for you.&lt;/strong&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Sample - Logback on Android!
&lt;/h3&gt;

&lt;p&gt;First things first: Let’s start by adding the SLF4J dependency to our Application’s Gradle file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* snip */&lt;/span&gt;
&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/* Adding SLF4J's Interfaces */&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'org.slf4j:slf4j-api:1.7.36'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we’d need to add Logback’s library, but since there are some additional steps required to run Logback on Android we can use a forked version that does all the extra work for us already: &lt;a href="https://github.com/tony19/logback-android"&gt;Logback-android&lt;/a&gt;. Now, to get Logback working we’ll need to first add the dependency and add the logback &lt;code&gt;xml&lt;/code&gt; configuration to the application’s &lt;code&gt;assets&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* snip */&lt;/span&gt;
&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'org.slf4j:slf4j-api:1.7.36'&lt;/span&gt;
        &lt;span class="cm"&gt;/* Logback-android */&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'com.github.tony19:logback-android:2.0.0'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;appender&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"logcat"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ch.qos.logback.classic.android.LogcatAppender"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tagEncoder&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;pattern&amp;gt;&lt;/span&gt;%logger{12}&lt;span class="nt"&gt;&amp;lt;/pattern&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/tagEncoder&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;encoder&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;pattern&amp;gt;&lt;/span&gt;[%-20thread] %msg&lt;span class="nt"&gt;&amp;lt;/pattern&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/encoder&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/appender&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;root&lt;/span&gt; &lt;span class="na"&gt;level=&lt;/span&gt;&lt;span class="s"&gt;"DEBUG"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;appender-ref&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"logcat"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/root&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now all that’s left is to request a &lt;code&gt;Logger&lt;/code&gt; by invoking the &lt;code&gt;LoggerFactory.GetLogger&lt;/code&gt; method from SLF4J and you can Log away!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;

&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Initializing, now with 101% more HILT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;setContent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;AppCanvas&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&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;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;p&gt;Complete notes available on &lt;a href="https://ardc-overflow.notion.site/Java-Logging-Hell-SLF4J-to-the-Rescue-005e136d1dac449eabc9fdcbd8f2ec36"&gt;my Notion workspace&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post's cover is from &lt;a href="https://undraw.co/"&gt;https://undraw.co/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>android</category>
      <category>logging</category>
      <category>programming</category>
    </item>
    <item>
      <title>Mantendo a sanidade no mundo de Tecnologia</title>
      <dc:creator>Rodolpho Alves</dc:creator>
      <pubDate>Sat, 09 Apr 2022 21:57:45 +0000</pubDate>
      <link>https://dev.to/ardc_overflow/mantendo-a-sanidade-no-mundo-de-tecnologia-1h1b</link>
      <guid>https://dev.to/ardc_overflow/mantendo-a-sanidade-no-mundo-de-tecnologia-1h1b</guid>
      <description>&lt;p&gt;Salve pessoal! Esse será o primeiro post em que irei, brevemente, lançar um "podcast" sobre lá no YouTube! A ideia é prover um contexto por escrito para deixar registrado e disponível para aquele que, como eu, não são fãs de ficar assistindo vídeos ou podcasts direto 😎️.&lt;/p&gt;

&lt;p&gt;Antes de irmos para o post preciso deixar algo claro: &lt;em&gt;Tudo que está escrito aqui é baseado na minha própria experiência como desenvolvedor, arquiteto e gerente na área de desenvolvimento&lt;/em&gt;, eu não sou e nem pretendo ser um psicólogo ou expert nessa área!&lt;/p&gt;

&lt;h2&gt;
  
  
  Mantendo a sanidade no mundo de Tecnologia
&lt;/h2&gt;

&lt;p&gt;Não é novidade para ninguém que o universo de TI é uma loucura. São bugs explodindo, caminhão esperando nota fiscal pra sair, Injection na Sprint chegando à direita e esquerda e aquela constante demanda de descobrir (e conseguir usar) o que quer que seja a novidade daquele mês/trimestre/ano - não é a toa que &lt;strong&gt;burnouts sejam tão comuns&lt;/strong&gt; no nosso universo.&lt;/p&gt;

&lt;p&gt;Para complementar com a pressão do dia-a-dia também é comum em alguns casos &lt;strong&gt;sentirmos uma desconexão do trabalho do dia-a-dia e o impacto que chega a um usuário&lt;/strong&gt;. Em pessoas focadas no backend isso é praticamente de praxe - são sprints a fio desenvolvendo alguma grande feature que irá entregar dados para vários &lt;em&gt;clients&lt;/em&gt; ou vai permitir &lt;em&gt;que vários outros sistemas se conectem com o nosso&lt;/em&gt; e no final é normal os "louros" irem para o frontend ou o mobile. Mas mesmo assim, nem tudo são flores para o frontend também, pois...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ainda existe uma cultura de "proteger" desenvolvedores de feedback de clientes!&lt;/strong&gt; Muitas vezes isso é feito com boas intenções em mente como "O cliente sempre quer demais" ou "O usuário nem olhou o tutorial" - como se o desenvolvedor não tivesse a capacidade de filtrar e extrair informações mesmo de feedback negativos - e no fim isso &lt;strong&gt;faz com que fiquemos cada vez mais desconectados da realidade do usuário&lt;/strong&gt; e eventualmente &lt;strong&gt;fiquemos desmotivados e sem entender o porquê nosso trabalho é tão valioso!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Para a cereja no bolo da catástrofe temos também nós mesmos e nossa própria cobrança. Sempre olhamos para o colega, um YouTuber ou um post lá no StackOverflow e pensamos: &lt;em&gt;"Como esse cara/guria é foda. Eu não sei metade disso..."&lt;/em&gt; - e nessas horas esquecemos uma realidade muito importante de programar em pleno século XXI: &lt;strong&gt;quase toda informação está a um Google/DuckDuckGo de distância, portanto memorizar os métodos e classes de uma biblioteca é opcional!&lt;/strong&gt;. Na hora do pull request ou da review a gente esquece que aquele sênior pode ter passado por um processo enorme de ficar pesquisando e consolidando informações antes de chegar naquele código totalmente clean e 100% testável! (Não se espelhe em desenvolvedores que não testam o próprio código 😂️)&lt;/p&gt;

&lt;h3&gt;
  
  
  As ferramentas para manter a Sanidade!
&lt;/h3&gt;

&lt;p&gt;A introdução já foi um wall of text falando sobre os problemas e como chegamos neles, portanto é hora de falarmos um pouco de como podemos usar algumas coisas para combatermos tudo isso! Cada sub-seção aqui vai abordar uma das "ferramentas" na caixa da sanidade.&lt;/p&gt;

&lt;h4&gt;
  
  
  Entenda que ninguém é perfeito
&lt;/h4&gt;

&lt;p&gt;Primeiro passo que eu costumo fazer comigo mesmo é me lembrar que &lt;strong&gt;perfeição é impossível&lt;/strong&gt;. &lt;strong&gt;Todos nós temos pontos fortes e fracos&lt;/strong&gt;, através do nosso auto-conhecimento podemos entender e nos planejar com isso em mente!&lt;/p&gt;

&lt;p&gt;Por exemplo &lt;strong&gt;eu sei que sou fraquíssimo quando a questão é UI&lt;/strong&gt;. Mesmo com um UX Designer me cantando tudo que precisa ser feito para a interface ficar bonita &lt;em&gt;eu sei que eu sou travado ali&lt;/em&gt; e respeito essa limitação. Vou até onde eu posso, me esforço até onde dá sem me frustrar e garanto que tenho algum colega que pode me ajudar na hora de deixar uma interface de fato tinindo.&lt;/p&gt;

&lt;p&gt;Por outro lado &lt;strong&gt;eu sei que sou muito bom em arquitetura&lt;/strong&gt;. Com pouca informação consigo propor vários cenários com Pros e Contras e ajudar alguém a chegar numa decisão sobre como arquitetar sua solução. Pensando nisso &lt;em&gt;eu sei onde posso melhor ajudar as pessoas&lt;/em&gt; e sei que vou compensar, de uma maneira ou de outra, aquele ponto fraco.&lt;/p&gt;

&lt;p&gt;Parece clichê falar. Mas a chave aqui &lt;strong&gt;é se conhecer, entender seus limites e se respeitar - você nunca terá todas as respostas para todas as perguntas&lt;/strong&gt; e está tudo bem, você não precisa disso pois dificilmente estará completamente isolado, em uma ilha deserta e sem internet, resolvendo problemas de TI.&lt;/p&gt;

&lt;h4&gt;
  
  
  Saiba pesquisar por informação
&lt;/h4&gt;

&lt;p&gt;Sabendo (e aceitando) que não temos todas as respostas vem uma skill crítica em minha opinião: &lt;strong&gt;Saber buscar nas fontes de conhecimento coletivo os dados para a pergunta que você não sabe responder&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Existem várias fontes de informação que você tem à sua disposição! &lt;strong&gt;A pergunta que você quer responder provavelmente já foi&lt;/strong&gt; (pelo menos parcialmente) &lt;strong&gt;respondida por alguém na Internet!&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No mínimo você tem acesso a essas fontes - saber como utilizar elas a seu favor vai ser um baita diferencial para dar aquele "unblocker" sem precisa se frustar tanto!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DuckDuckGo e Google&lt;/strong&gt;: Saber pesquisar direto em qualquer &lt;em&gt;search engine&lt;/em&gt; significa ter o poder da Internet inteira em sua mão&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;StackOverflow&lt;/strong&gt;: Provavelmente vai precisar peneirar um pouco (e torcer para ter uma resposta atualizada), mas costuma ser útil para pelo menos ter ideias alternativas!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: Se você está utilizando uma biblioteca open source famosa, muito provavelmente, outras pessoas também estão! Então por qual motivo não poderíamos dar uma olhada em como os outros estão usando essas bibliotecas?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repositórios da sua Empresa&lt;/strong&gt;: Se é uma biblioteca interna ela está em algum repositório interno. Seja um GitHub, BitBucket, GitLab ou GitTea da vida: você também pode (e deve!) pesquisar no códigos dos outros nesses repositórios! Novamente, nem que seja para ter uma pequena nova ideia ou tentar algo levemente diferente!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;YouTube&lt;/strong&gt;: Existem muitos criadores de conteúdo para vários frameworks e linguagens de programação! É por aqui que normalmente descubro novas bibliotecas ou padrões para utilizar em meus projetos individuais.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/cEBkvm0-rg0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  Confie na sua equipe
&lt;/h4&gt;

&lt;p&gt;No dia-a-dia normalmente esquecemos que outra fonte de informação e apoio muito importante são nossos próprios colegas. &lt;strong&gt;Se mantivermos e cultivarmos um mindset de crescimento será muito mais prático consultar seus colegas para ter ajuda&lt;/strong&gt;. Inclusive ao fazer isso &lt;strong&gt;estamos ajudando a consolidar o conhecimento das pessoas&lt;/strong&gt; pois &lt;em&gt;ensinar e explicar&lt;/em&gt; faz parte do próprio processo de aprendizado!&lt;/p&gt;

&lt;p&gt;São aqui que podemos utilizar várias metodologias:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pair Programming&lt;/strong&gt;: Fazer uma sessão em que das pessoas colaboram para escrever código em um único lugar (um digitando e outro falando)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Programming Dojo&lt;/strong&gt;: Similar ao pair programming porém com mais pessoas - uma vai bater o código e as outras falam, simplesmente revezando quem está batendo o código&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Reviews&lt;/strong&gt;: A maneira assíncrona e normalmente mais prática para algumas organizações. Simplesmente chame seus colegas para fazer review do seu &lt;em&gt;pull request&lt;/em&gt; antes que fazer o merge. Mesmo que seu código esteja compilando e passando todos os testes existem nuances que apenas pessoas vão pegar!&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Celebre pequenas vitórias
&lt;/h4&gt;

&lt;p&gt;Para balancear com a enchurrada debugs lembre-se de &lt;strong&gt;celebrar e valorizar pequenas vitórias&lt;/strong&gt;! Coisas "bobas" como passar por um code review só com aprovações, aumentar a performance de uma rotina por milisegundos, aumentar a cobertura de teste de uma área do sistema mesmo que por 5 linhas ou tirar um code smell antes que ele gere um bug são coisas a se celebrar!&lt;/p&gt;

&lt;p&gt;Além disso lembre-se também de &lt;strong&gt;celebrar os pequenos avanços enquanto estiver estudando!&lt;/strong&gt; Normalmente levamos várias semanas e meses para entender completamente uma nova tecnologia, portanto não custa celebrar aquela primeira prova de conceito que fez o Kafka funcionar ou um cache no Redis ser consultado pela sua api!&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/o7w5r5PfBKo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  Tenha válvulas de escape
&lt;/h4&gt;

&lt;p&gt;Talvez o mais crítico de tudo aqui seja este ponto: &lt;strong&gt;Você apenas trabalha uma parte do seu dia&lt;/strong&gt; - o resto do seu dia é &lt;strong&gt;seu&lt;/strong&gt; e você deve aproveitar e respeitar isso.&lt;/p&gt;

&lt;p&gt;Sempre que possível faça seus hobbies nesse tempo livre. Se não tiver um hobby busque um! Seja ele qual for (auxiliar em open source, escrever num blog, pedalar, correr, malhar, artes marciais, jogar video-game) &lt;strong&gt;esse hobby será sua válvula de escape para as horas de stress que acontecem no trabalho&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Não tem segredo - ninguém é de ferro! Algo que eu costumo muito fazer é sair para uma corrida ou um pedal mais pesado quando estou frustrado ou precisando arejar os pensamentos depois de um dia ou uma semana puxada.&lt;/p&gt;

&lt;p&gt;E a melhor parte? &lt;strong&gt;Normalmente durante esses momentos que acabamos tendo as melhores ideias!&lt;/strong&gt; Quando livramos a mente de toda a pressão e passamos a fazer algo mais "relaxante" (mesmo que canse mais o corpo kkk) conseguimos ativar nossos processos criativos e muitas vezes vemos possíveis saídas para os problemas que estavam nos assombrando durante o horário de trabalho.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;É por isso que vemos essa quantidade de pessoas de TI acabando, por exemplo, indo para Maratonas, Triathlon, pedais de longa distância ou gravando vídeos aleatórios!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/IqjUe96vDM0?start=436"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  As Vantagens de estar bem
&lt;/h3&gt;

&lt;p&gt;Só para sintetizar e encerrar o post: &lt;strong&gt;estar bem é o segredo para o sucesso no longo prazo&lt;/strong&gt;. Ao estar "bem" você vai &lt;strong&gt;ter um processo criativo melhor&lt;/strong&gt; (e não se engane - programar requer criatividade mesmo que não seja uma arte!), &lt;strong&gt;você irá se frustar menos quando errar aqui ou ali&lt;/strong&gt;, &lt;strong&gt;ter uma jornada de aprendizado mais estável e sustentável&lt;/strong&gt; e &lt;strong&gt;conseguirá recarregar as baterias constantemente para conseguir participar daquela reunião mais chata da semana&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>lifestyle</category>
      <category>healthy</category>
      <category>sanity</category>
      <category>it</category>
    </item>
    <item>
      <title>Configurando um ambiente Windows para desenvolvimento</title>
      <dc:creator>Rodolpho Alves</dc:creator>
      <pubDate>Wed, 08 Sep 2021 01:01:57 +0000</pubDate>
      <link>https://dev.to/ardc_overflow/configurando-um-ambiente-windows-para-desenvolvimento-1cdk</link>
      <guid>https://dev.to/ardc_overflow/configurando-um-ambiente-windows-para-desenvolvimento-1cdk</guid>
      <description>&lt;h2&gt;
  
  
  💭 Introdução
&lt;/h2&gt;

&lt;p&gt;Que atire o a primeira pedra o desenvolvedor que usa primariamente Windows que nunca enrolou pra atualizar ou formatar uma máquina por causa do Setup estar redondo... &lt;em&gt;salvo por aquele bug chato ou serviço travando a máquina toda 🤷‍♂️&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;E isso é uma coisa que não tem como argumentar (&lt;em&gt;ainda&lt;/em&gt;) que o pessoal primário Linux tem de mão beijada!&lt;/p&gt;

&lt;p&gt;Mas e se eu te falasse que com um pouco de paciência, planejamento e &lt;del&gt;magia&lt;/del&gt; scripts de Powershell conseguimos chegar a algo próximo? &lt;/p&gt;

&lt;p&gt;&lt;a href="https://undraw.co/" rel="noopener noreferrer"&gt;Cover para o post via UnDraw&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 Planejando!
&lt;/h2&gt;

&lt;p&gt;Antes de tudo temos de pensar:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;O que eu realmente preciso, do zero, em uma instalação nova do Windows?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Se questione, antes de partimos para as próximas etapas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Qual &lt;strong&gt;meu foco&lt;/strong&gt; primário?&lt;/li&gt;
&lt;li&gt;Quais &lt;strong&gt;ferramentas&lt;/strong&gt; preciso sempre?&lt;/li&gt;
&lt;li&gt;Quais &lt;strong&gt;linguagens&lt;/strong&gt; preciso sempre?&lt;/li&gt;
&lt;li&gt;Quais &lt;strong&gt;atalhos&lt;/strong&gt; gosto de usar sempre?&lt;/li&gt;
&lt;li&gt;Quais &lt;strong&gt;"frescuras"&lt;/strong&gt; instalo sempre?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As minhas respostas, em ordem:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Backend&lt;/li&gt;
&lt;li&gt;Git, Powershell Core, Windows Terminal, SSH, Visual Studio Code, WSL, Docker&lt;/li&gt;
&lt;li&gt;C#, Typescript&lt;/li&gt;
&lt;li&gt;Meus atalhos de Git e Navegação terminal&lt;/li&gt;
&lt;li&gt;oh-my-posh, posh-git, fontes com ligatures&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Com base nessas respostas que vou pesar qual ferramentas de package irei utilizar e a prioridade!&lt;/p&gt;

&lt;p&gt;Por exemplo apesar de &lt;em&gt;sempre&lt;/em&gt; usar o Visual Studio em si sei que ele é mais demorado de instalar e, portanto, não ligo de precisar deixar ele rodando por mais tempo. Os outros ali da lista conseguem ir cobrindo a falta dele para as primeiras horas ou dia do novo setup!&lt;/p&gt;

&lt;h3&gt;
  
  
  1 - Escolhendo sua ferramentas para Packages
&lt;/h3&gt;

&lt;p&gt;Para quem é acostumado com os &lt;code&gt;Nix&lt;/code&gt; sabe que sempre tem um Package Manager da vida, o famoso &lt;code&gt;sudo apt-get install&lt;/code&gt; (e similares, &lt;em&gt;nem tudo é apt-get&lt;/em&gt;). Mas no Windows infelizmente estamos acostumados a sair caçando instaladores internet a fora, baixando, clicando em vários &lt;em&gt;Next&lt;/em&gt; e só então vemos a coisa ser instalada.&lt;/p&gt;

&lt;p&gt;Note que estou &lt;strong&gt;focando nos ambientes comuns&lt;/strong&gt;. Existem soluções para &lt;strong&gt;criar imagens inteiras do Windows&lt;/strong&gt; disponíveis para quem quer se dedicar a esse nível mas... quantas vezes você vai fazer isso fora de um ambiente empresarial?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Para o seu ambiente pessoal o que é mais fácil? Manter uma imagem de instalação pronta ou configurar alguns scripts?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Felizmente a própria comunidade partiu pro ataque e surgiu com alguns soluções para Package Managers no Windows! Alguns deles são:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://chocolatey.org/" rel="noopener noreferrer"&gt;Chocolatey&lt;/a&gt;: Requer instalação (Powershell)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://scoop.sh/" rel="noopener noreferrer"&gt;Scoop&lt;/a&gt;: Requer instalação (Powershell)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/microsoft/winget-cli" rel="noopener noreferrer"&gt;Winget&lt;/a&gt;: Já incluso nas builds Insiders, passará a ser nativo no futuro, pode ser instalado pela Store&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;O que eles têm em comum? &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Todos contam com a comunidade para manter sua lista de packages atualizada&lt;/li&gt;
&lt;li&gt;Todos são OpenSource&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Então qual o grande diferencial? A quantidade de packages que cada um tem. Em números o &lt;strong&gt;Chocolatey&lt;/strong&gt; toma a frente, seguido pelo &lt;strong&gt;Winget&lt;/strong&gt; e pelo &lt;strong&gt;Scoop&lt;/strong&gt;. Porém o &lt;strong&gt;Winget se tornará nativo ao Windows em breve&lt;/strong&gt;, enquanto que os outros precisaram de passos adicionais.&lt;/p&gt;

&lt;h3&gt;
  
  
  2 - Escolhendo um Package Manager com base nas suas necessidades
&lt;/h3&gt;

&lt;p&gt;Essa é uma escolha que você deverá fazer com base nas ferramentas que você utiliza no seu dia a dia. No meu caso, por utilizar várias ferramentas da Microsoft, vou de &lt;strong&gt;winget&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Como estou utilizando o &lt;code&gt;winget&lt;/code&gt; vou pesquisando e salvando, em um &lt;code&gt;.txt&lt;/code&gt; a lista de ferramentas que tenha disponíveis por ele e desejo instalar. Para isso executo o comando &lt;code&gt;winget search &amp;lt;nome_software&amp;gt;&lt;/code&gt;, trocando as o campo &lt;code&gt;&amp;lt;nome_software&amp;gt;&lt;/code&gt; pelo nome de o que gostaria de instalar, como o próprio SDK do dotnet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn6qq2ecs0g6fht2d5lno.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn6qq2ecs0g6fht2d5lno.gif" alt="Buscando packages no Winget"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A seguinte lista é a que estou montando para a próxima vez que formatar meu laptop pessoal, todos estes são &lt;code&gt;id&lt;/code&gt;s válidos do &lt;strong&gt;winget&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;7zip.7zip
Mozilla.FirefoxDeveloperEdition
Microsoft.VisualStudioCode-User-x64
Git.Git
Microsoft.PowerShell
Microsoft.WindowsTerminal
Microsoft.dotnet
ditto.ditto
ShareX.ShareX
code52.Carnac
Microsoft.PowerToys
VideoLAN.VLC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A lista é "pequena" mas lembre-se: Estou focando apenas no absolutamente essencial para que eu comece a trabalhar/estudar utilizando o ambiente. O Docker, Visual Studio e WSL terão de vir depois.&lt;/p&gt;
&lt;h2&gt;
  
  
  🛠 Preparando!
&lt;/h2&gt;
&lt;h3&gt;
  
  
  3 - Automatizando os básicos
&lt;/h3&gt;

&lt;p&gt;Como estamos falando do Windows devemos lembrar que &lt;strong&gt;o Powershell é uma grande ferramenta&lt;/strong&gt; e seu amigo nas horas em que precisa automatizar algo! Ele não é tão difundido quanto os &lt;code&gt;shell scripts&lt;/code&gt; mas ele é capaz de &lt;strong&gt;muita coisa&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Meu primeiro script de automação do setup vai ser um bem básico, seguindo o seguinte algoritmo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verifique se o &lt;code&gt;winget&lt;/code&gt; está disponível, se sim continue&lt;/li&gt;
&lt;li&gt;Pegue um arquivo &lt;code&gt;.txt&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Carregue o conteúdo, linha a linha&lt;/li&gt;
&lt;li&gt;Para cada linha, em ordem de leitura:
a. Execute &lt;code&gt;winget install -e --id $linha&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A implementação final é essa aqui, junto com o arquivo que mostrei acima com os IDs:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$SoftwareListFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".\software_list.txt"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]]&lt;/span&gt;&lt;span class="nv"&gt;$SoftwareList&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$SoftwareListFile&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="cm"&gt;&amp;lt;#
&lt;/span&gt;&lt;span class="cs"&gt;.SYNOPSIS&lt;/span&gt;&lt;span class="cm"&gt;
    Checks if a Command is currently available on Powershell
#&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Test-CommandExists&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CmdletBinding&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mandatory&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$testCommand&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Get-Command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$testCommand&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Stop&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$false&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-CommandExists&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;winget&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Winget not found, unable to continue"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Check on https://github.com/microsoft/winget-cli for instructions"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This script will attempt to install multiple softwares on your system"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$DoInstall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Read-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Prompt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Are you sure you want to install? If so, type 'install' bellow."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$WingetCommandParam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$DoInstall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"install"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$DoInstall&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"search"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$SoftwareList&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;winget&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$WingetCommandParam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Done!"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;${PSScriptRoot}&lt;/span&gt;&lt;span class="s2"&gt;\bootstrap.ps1"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PathType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Leaf&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$DoBootstrap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Read-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Prompt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Type 'bootstrap' bellow if you want to run dotfiles' bootstrap as well"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$DoBootstrap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bootstrap"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;${PSScriptRoot}&lt;/span&gt;&lt;span class="s2"&gt;\bootstrap.ps1"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Para quem não está acostumado com &lt;em&gt;powershell&lt;/em&gt; pode parecer um pouco grego, mas acredite: Não tem nada ali que não seja explicado por um &lt;code&gt;help &amp;lt;comando&amp;gt;&lt;/code&gt; no Powershell e a estrutura é &lt;strong&gt;bem parecida com o algoritmo&lt;/strong&gt;, salvo a quantidade de outputs que faço!&lt;/p&gt;

&lt;p&gt;Agora podemos ir para o próximo passo: preparar nossos &lt;code&gt;dotfiles&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  4 - &lt;code&gt;dotfiles&lt;/code&gt; no Windows
&lt;/h3&gt;

&lt;p&gt;"Mas o que são &lt;code&gt;dotfiles&lt;/code&gt;, Rodolpho?"&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;dotfiles&lt;/code&gt; são arquivos que normalmente começam com . (algo oculto no Unix) e, normalmente, armazenam alguma configuração.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alguns &lt;code&gt;dotfiles&lt;/code&gt; famosos e suas aplicações são:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;.gitignore&lt;/code&gt;: Configura os arquivos que devem ser ignorados em um repositório &lt;em&gt;Git&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.gitconfig&lt;/code&gt;: Configurações do seu usuário, normalmente encontrado em &lt;code&gt;~/.gitconfig&lt;/code&gt; (mesmo no Windows!)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.bashrc&lt;/code&gt;: Configuração do Bash. No caso do Powershell substituímos esse aqui pelo &lt;code&gt;Profile.ps1&lt;/code&gt;!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Se você programa provavelmente vai ter um &lt;code&gt;.gitconfig&lt;/code&gt; mesmo que nunca o tenha criado manualmente, pois os comandos &lt;code&gt;git config --global&lt;/code&gt; normalmente acabam gerando o arquivo para você 🙂.&lt;/p&gt;

&lt;p&gt;Explicarei na próxima seção a ideia do &lt;code&gt;$PROFILE&lt;/code&gt;, mas para finalizarmos sobre &lt;code&gt;dotfiles&lt;/code&gt; vamos gravar o conceito:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Um repositório de &lt;code&gt;dotfiles&lt;/code&gt; contem arquivos de configurações que você quer compartilhar entre ambientes e um script (&lt;code&gt;.sh&lt;/code&gt; ou &lt;code&gt;.ps1&lt;/code&gt;) que gera links simbólicos (&lt;em&gt;são tipo atalhos, mas pra gente grande&lt;/em&gt;) entre os arquivos do repositório e os arquivos armazenados no sistema operacional.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ou seja, uma vez criado seu &lt;code&gt;dotfiles&lt;/code&gt;, você só precisa fazer um &lt;code&gt;git pull&lt;/code&gt;, &lt;code&gt;./script_que_cria_links.ps1&lt;/code&gt; e sua máquina está sincronizada com o que quer que vc tenha colocado no repositório.&lt;/p&gt;
&lt;h4&gt;
  
  
  O PROFILE do Powershell
&lt;/h4&gt;

&lt;p&gt;Quanto ao &lt;code&gt;Profile.ps1&lt;/code&gt; você provavelmente o acessará através do powershell com o comando &lt;code&gt;code|vi|vim|nvim $PROFILE&lt;/code&gt; (só tire dali os editores que você não usa).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;O &lt;code&gt;$PROFILE&lt;/code&gt; é um arquivo que é executado &lt;strong&gt;sempre que você abre um powershell&lt;/strong&gt;. Normalmente ele conterá atalhos, importações de módulos e verificações de que algo está disponível ou não.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;O meu &lt;code&gt;$PROFILE&lt;/code&gt; normalmente contem uma série de &lt;strong&gt;atalhos do Git&lt;/strong&gt;, &lt;strong&gt;ativação de Modules&lt;/strong&gt;, &lt;strong&gt;verificações do ambiente&lt;/strong&gt;, &lt;strong&gt;atalhos para navegação&lt;/strong&gt; e &lt;strong&gt;segredos específicos do Powershell&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Um pequeno trecho do meu &lt;code&gt;$PROFILE&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# All Shortcuts&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Set-Alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"back"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Pop-Location&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Set-Alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"push"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Push-Location&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Set-Alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"touch"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;New-Item&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Set-Alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Invoke-Item&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="cm"&gt;&amp;lt;#
&lt;/span&gt;&lt;span class="cs"&gt;.SYNOPSIS&lt;/span&gt;&lt;span class="cm"&gt;
    Calls fetches and pulls updates from the upstream branch. Stashes and Pops if there are uncomitted changes
&lt;/span&gt;&lt;span class="cs"&gt;.DESCRIPTION&lt;/span&gt;&lt;span class="cm"&gt;
    Shortcut function for calling git fetch and git pull while keeping uncomitted changes
#&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Invoke-Git-FetchAndPull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--all&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;diff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--exit-code&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$hasDiff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$LASTEXITCODE&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hasDiff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;stash&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pull&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hasDiff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;stash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pop&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="cm"&gt;&amp;lt;#
&lt;/span&gt;&lt;span class="cs"&gt;.SYNOPSIS&lt;/span&gt;&lt;span class="cm"&gt;
    Calls fetch and then rebases into the target branch
&lt;/span&gt;&lt;span class="cs"&gt;.DESCRIPTION&lt;/span&gt;&lt;span class="cm"&gt;
    Shortcut function for calling git fetch and git rebase -i
&lt;/span&gt;&lt;span class="cs"&gt;.PARAMETER&lt;/span&gt;&lt;span class="cm"&gt; TargetBranch
    The branch to rebase on, defaults to origin/develop
#&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;Function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Invoke-Git-FetchAndRebase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CmdletBinding&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HelpMessage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The branch to rebase on, defaults to origin/develop"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$TargetBranch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"origin/develop"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--all&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rebase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TargetBranch&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Attempt to load posh-git&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Import-Module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;posh-git&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Unable to Import-Module posh-git, it wasn't found"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Checks if SSH Agent is running and git is configured&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$sshAgentStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Get-Service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ssh-agent"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Status&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sshAgentStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-ne&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Running"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Write-Warning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ssh-agent isn't running, you should change its initialization"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;$null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$GitSshConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Write-Warning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GIT_SSH isn't set, it might not use windows' OpenSSH"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Write-Warning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Unable to check on ssh-agent status"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Como eu tenho &lt;strong&gt;vários&lt;/strong&gt; atalhos, para mim, é vantajoso manter uma cópia deste perfil no meu &lt;code&gt;dotfiles&lt;/code&gt; para que eu consiga ter a mesma produtividade em todo ambiente meu que tiver &lt;em&gt;Powershell&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Caso você queira uma base para começar seu &lt;code&gt;dotfiles&lt;/code&gt; para Windows pode dar uma olhada no meu repositório abaixo 👇&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rodolphocastro" rel="noopener noreferrer"&gt;
        rodolphocastro
      &lt;/a&gt; / &lt;a href="https://github.com/rodolphocastro/dotfiles" rel="noopener noreferrer"&gt;
        dotfiles
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Dotfiles for my Windows and Linux environments
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Rodolpho Alves' dotFiles&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Those are my personal settings for my Windows' environments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you want to use this consider forking it and changing it to your fit your own needs&lt;/em&gt;, don't go around blindly running scripts from the internet!&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What are dotfiles&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;In a nutshell:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Basically this is an old-school way to OneDrive/Google Sync/Dropbox your settings. At least those that are &lt;em&gt;easy to automate&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A &lt;code&gt;dotfiles&lt;/code&gt; repository is an attempt to keep multiple settings in-sync across multiple environments &lt;strong&gt;without relying on external solutions.&lt;/strong&gt; Basically once you have this repository done all you gotta do is &lt;code&gt;git clone&lt;/code&gt; it into a new environment and run the &lt;code&gt;bootstrap&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;The biggest challenge for &lt;em&gt;this&lt;/em&gt; set of &lt;code&gt;dotfiles&lt;/code&gt; is to deal with the Windows' way of setting stuff up. Thus why you'll notice some powershell wizardry going on. Things like what I'm trying to achieve here are way easier on &lt;code&gt;nix&lt;/code&gt;…&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/rodolphocastro/dotfiles" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  ▶ Executando!
&lt;/h2&gt;

&lt;p&gt;Tudo planejado, salvo em um pen drive e/ou na nuvem? Hora de botar a mão na massa!&lt;/p&gt;

&lt;h3&gt;
  
  
  5 - Rodando scripts
&lt;/h3&gt;

&lt;p&gt;O primeiro passo pra sair rodando coisas num Powershell zerado é ajustar a &lt;code&gt;ExecutionPolicy&lt;/code&gt; da sua sessão.&lt;/p&gt;

&lt;p&gt;O comando &lt;code&gt;Get-ExecutionPolicy&lt;/code&gt; irá te dizer qual o atual nível. Para você fazer alterações normalmente quer defini-la (&lt;code&gt;Set-ExecutionPolicy&lt;/code&gt;) como &lt;code&gt;Bypass&lt;/code&gt; ou &lt;code&gt;RemoteUnsigned&lt;/code&gt; com o &lt;code&gt;-Scope Process&lt;/code&gt;. Traduzindo: &lt;strong&gt;você irá permitir que o atual powershell que você está executando faça mais alterações que o normal&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Feito isso é só partir para o abraço e rodar o script que fizemos na seção acima para instalar seus softwares.&lt;/p&gt;

&lt;h3&gt;
  
  
  6 - Baixando nossos &lt;code&gt;dotfiles&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Tudo instalado? Hora de fazer um &lt;code&gt;git clone &amp;lt;seu_repositório_dotfiles&amp;gt;&lt;/code&gt;, entrar no repositório e executar o script &lt;code&gt;bootstrap.ps1&lt;/code&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  7 - Usando o OpenSSH do Windows 10 com o Git
&lt;/h3&gt;

&lt;p&gt;É curioso como algo tão útil passa tão batido. Porém o &lt;strong&gt;Windows 10 possui uma versão do OpenSSH embutida!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Você não precisa usar um cliente de terceiros para poder usufruir do &lt;code&gt;ssh&lt;/code&gt; com o &lt;em&gt;Git&lt;/em&gt;. Basta fazer alguns ajustes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Colocar o OpenSSH Agent para executar
a. Você pode fazer isso via Powershell ou via Serviços do Windows&lt;/li&gt;
&lt;li&gt;Criar um par de chaves&lt;/li&gt;
&lt;li&gt;Adicionar essas chaves ao OpenSSH Agent&lt;/li&gt;
&lt;li&gt;Cadastrar a chave pública com o seu Repositório Remoto&lt;/li&gt;
&lt;li&gt;Ajustar uma variável de ambiente&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Irei pular o item #4 pois isso vai ser específico do seu servidor. Seja ele um GitHub, GitLab, BitBucket ou Gitea.&lt;/p&gt;

&lt;h4&gt;
  
  
  Ligando o OpenSSH Agent
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Com Powershell
&lt;/h5&gt;

&lt;p&gt;Para os fãs de Powershell basta rodar esse script numa instância de admin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Busca o serviço do windows chamado ssh-agent&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$sshAgentService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-Service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ssh-agent"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# Se o serviço não estiver iniciando com delay...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sshAgentService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-ne&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AutomaticDelayedStart"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Warning&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Setting SSH Agent to DelayedStart"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c"&gt;# Mande ele iniciar com delay!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Set-Service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ssh-agent"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-StartupType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AutomaticDelayedStart&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h5&gt;
  
  
  Sem Powershell
&lt;/h5&gt;

&lt;p&gt;Para quem não é fã do Powershell:&lt;/p&gt;

&lt;p&gt;Abra o &lt;em&gt;Executar&lt;/em&gt; (Atalho: Win+R) e digite &lt;code&gt;services.msc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ordene a lista por nome e encontre um serviço chamado "OpenSSH Authentication Agent", dê um clique-duplo nele.&lt;/p&gt;

&lt;p&gt;Na tela de propriedades altere o "Startup Type" para "Automatic (Delayed Start)", conforme:&lt;/p&gt;

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

&lt;p&gt;Clique em &lt;em&gt;Apply&lt;/em&gt; e &lt;em&gt;Ok&lt;/em&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Criando e Adicionando Chaves SSH
&lt;/h4&gt;

&lt;p&gt;Através de um terminal execute o seguinte comando, substituindo pelo seu email:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ssh-keygen -o -a 100 -t ed25519 -C "meu-email@gmail.com"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Isso irá criar um par de chaves &lt;code&gt;ED25519&lt;/code&gt; no diretório &lt;code&gt;~/.ssh&lt;/code&gt;, uma com o final &lt;code&gt;.pub&lt;/code&gt; (&lt;em&gt;Spoiler: Essa é a que você copia e cola no seu servidor!&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Agora para cadastrar sua chave com o OpenSSH Agent rode &lt;code&gt;ssh-add.exe&lt;/code&gt; e confirme a senha da sua chave. Você verá algo como no gif abaixo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flbd357mmj1n2fabnqpal.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flbd357mmj1n2fabnqpal.gif" alt="Adicionando chaves ao OpenSSH agent"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A mensagem "Identity added" é a indicação de que tudo deu certo.&lt;/p&gt;
&lt;h4&gt;
  
  
  Dizendo para o Git usar o OpenSSH
&lt;/h4&gt;

&lt;p&gt;Finalmente só nos resta dizer para o &lt;em&gt;Git&lt;/em&gt;: "&lt;em&gt;ow, usa isso aqui ao invés do seu ssh embutido!&lt;/em&gt;".&lt;/p&gt;

&lt;p&gt;Existem várias maneiras de fazer isso. Por &lt;code&gt;.gitconfig&lt;/code&gt;, por links simbólicos e tudo. Mas, nas versões mais recentes do Git, temos uma maneira bem menos invasiva: Variáveis de ambiente.&lt;/p&gt;

&lt;p&gt;Antes de tudo você precisa encontrar onde está o &lt;code&gt;ssh.exe&lt;/code&gt; do Windows. Pra pegar o da sua máquina rode em um powershell:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Encontrando o comando "ssh" e pegando seu path&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Get-Command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ssh&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Path&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# A output provavelmente será C:\WINDOWS\System32\OpenSSH\ssh.exe&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Com esta output em mãos só nos resta agora &lt;strong&gt;alterar a variável de ambiente GIT_SSH para &lt;code&gt;C:\WINDOWS\System32\OpenSSH\ssh.exe&lt;/code&gt;&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Pelo Windows 10/11:&lt;/p&gt;

&lt;p&gt;Digite no menu iniciar "Variáveis" ou "Environment"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcg4vi9n2ai280nh2v79c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcg4vi9n2ai280nh2v79c.png" alt="pesquisando por environment no iniciar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Selecione o primeiro item e, na nova tela, clique em "Variáveis de Ambiente|Environment Variables..." no canto direito inferior&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnbr63psvh2gyymt95dy2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnbr63psvh2gyymt95dy2.png" alt="configurações do sistema"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Em "System Variables" clique em "New", chame a variável de "GIT_SSH" e insira o valor que pegamos no powershell.&lt;/p&gt;

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

&lt;p&gt;Pronto. Para testar se funcionou faça um &lt;code&gt;git clone&lt;/code&gt; com ssh. Você provavelmente não terá de digitar sua senha da chave! &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Dica&lt;/em&gt;: Além de funcionar para o &lt;em&gt;Git&lt;/em&gt; o ssh-agent também estará funcionando para você configurar seus acessos por ssh para servidores!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  8 - Windows Subsystem for Linux (WSL)
&lt;/h3&gt;

&lt;p&gt;Tem coisas que só fazem &lt;em&gt;bloat&lt;/em&gt; no Windows ou não dão aquela experiência legal, né? Eu, por exemplo, evito ao máximo instalar o &lt;code&gt;node&lt;/code&gt; direto no meu Windows pois a experiência no Linux é simplesmente melhor!&lt;/p&gt;

&lt;p&gt;Por muitos anos, antes do Windows 10, isso significava ter uma Máquina Virtual (normalmente no VirtualBox) e conectar nessa máquina pra, então, abrir uma IDE/Text Editor. Porém agora temos a possibilidade de executar o Linux dentro do próprio Windows (&lt;em&gt;edição Home ou maior&lt;/em&gt;)!&lt;/p&gt;

&lt;p&gt;Para isso basta &lt;strong&gt;instalar o Windows Subsystem for Linux (WSL)&lt;/strong&gt;, instalar sua distro favorita e partir pro abraço! Você terá acesso ao &lt;code&gt;bash&lt;/code&gt; diretamente do seu Powershell.&lt;/p&gt;

&lt;p&gt;As intruções completas (oficiais) podem ser encontradas &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10" rel="noopener noreferrer"&gt;neste link&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Habilitando o Windows Subsystem for Linux
&lt;/h4&gt;
&lt;h5&gt;
  
  
  Pré-Requisitos
&lt;/h5&gt;

&lt;p&gt;Antes de tudo verifique se seu Windows está na versão certa! O WSL requer no mínimo a &lt;strong&gt;build 18362&lt;/strong&gt; do Windows 10 &lt;strong&gt;1903&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Outra coisa importante é que &lt;strong&gt;seu setup deve permitir virtualização!&lt;/strong&gt; Isso normalmente &lt;strong&gt;será habilitado pela sua BIOS&lt;/strong&gt;. É raro isso estar bloqueado mas já vi equipamentos que bloqueavam ou ocultavam a possibilidade de ativar virtualização...&lt;/p&gt;
&lt;h5&gt;
  
  
  Com Powershell
&lt;/h5&gt;

&lt;p&gt;Com Powershell basta você abrir um &lt;em&gt;prompt de Administrador&lt;/em&gt; e executar:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;E pronto! O Windows vai ativar a funcionalidade pra você.&lt;/p&gt;
&lt;h5&gt;
  
  
  Sem Powershell
&lt;/h5&gt;

&lt;p&gt;Sem Powershell você terá de pesquisar, no iniciar, por "Features" e achar o "Turn Windows features on or off".&lt;/p&gt;

&lt;p&gt;Dentro da janela de Features localize o WSL, marque-o, e clique em Ok. O Windows então instalará a feature para você!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzj6ku06nbd7m1l565gg4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzj6ku06nbd7m1l565gg4.png" alt="ativando wsl pelas features"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Habilitando Virtualização no Windows
&lt;/h4&gt;
&lt;h5&gt;
  
  
  Com Powershell
&lt;/h5&gt;

&lt;p&gt;Novamente em um Powershell de Administradores, execute:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart&lt;/code&gt;&lt;/p&gt;
&lt;h5&gt;
  
  
  Sem Powershell
&lt;/h5&gt;

&lt;p&gt;Novamente lá na tela de "Turn Windows features on or off", encontre e marque a opção "Virtual Machine Platform":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3r1amkwk5m8cnpfsv7wt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3r1amkwk5m8cnpfsv7wt.png" alt="ativando VMs do Windows"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Atualizando o Kernel do Linux
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10#step-4---download-the-linux-kernel-update-package" rel="noopener noreferrer"&gt;Localize&lt;/a&gt; e &lt;a href="https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi" rel="noopener noreferrer"&gt;baixe&lt;/a&gt; o Kernel mais recente do Linux para WSL.&lt;/p&gt;
&lt;h4&gt;
  
  
  Ativando o WSL2
&lt;/h4&gt;

&lt;p&gt;Finalmente podemos ativar o WSL2, esse passo só pode ser executado pelo Powershell:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wsl --set-default-version 2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Pronto! O WSL estará pronto para você utilizar após um reboot da máquina!&lt;/p&gt;
&lt;h4&gt;
  
  
  Instalando sua Distro favorita
&lt;/h4&gt;

&lt;p&gt;Você agora poderá instalar as Distros de Linux &lt;a href="https://aka.ms/wslstore" rel="noopener noreferrer"&gt;presentes na Windows Store&lt;/a&gt; ou através do &lt;code&gt;winget&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Algumas distros que estão disponíveis são:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ubuntu&lt;/li&gt;
&lt;li&gt;Debian&lt;/li&gt;
&lt;li&gt;Alpine&lt;/li&gt;
&lt;li&gt;openSUSE&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Agora é uma questão de você escolher a sua favorita, instalar, abrir o terminal e digitar &lt;code&gt;wsl&lt;/code&gt; para lançar uma sessão no bash da sua distro!&lt;/p&gt;
&lt;h3&gt;
  
  
  9 - Docker Desktop
&lt;/h3&gt;

&lt;p&gt;Quando comecei a escrever este Post (faz um tempo! Ele está como rascunho a meses já!) o processo era um pouco mais complicado 😅&lt;/p&gt;

&lt;p&gt;Porém, hoje em dia, colocar o &lt;strong&gt;Docker Desktop&lt;/strong&gt; para rodar, em sua máquina Windows, é pra lá de tranquilo.&lt;/p&gt;

&lt;p&gt;Se você tiver acesso ao "winget" (recomendo!) basta rodar um &lt;code&gt;winget install DockerDesktop&lt;/code&gt; e seguir as instruções na tela.&lt;/p&gt;

&lt;p&gt;Se você quiser seguir pelo método old-school, acesse o &lt;a href="https://www.docker.com/products/docker-desktop" rel="noopener noreferrer"&gt;site oficial do Docker Desktop&lt;/a&gt; e clique em "Download for Windows".&lt;/p&gt;
&lt;h4&gt;
  
  
  Alterações recentes (2021) na licença do Docker Desktop
&lt;/h4&gt;

&lt;p&gt;Para quem está fora do loop: No final de Agosto de 2021 a equipe do Docker alterou a licença do Docker Desktop. (&lt;a href="https://www.docker.com/blog/updating-product-subscriptions/" rel="noopener noreferrer"&gt;Link da notícia oficial&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Para o seu uso pessoal pode ficar tranquilo. Salvo que você fature, como indivíduo, mais de U$ 10 milhões ao ano) essas alterações na licença deverão impactar &lt;em&gt;apenas empresas de grande porte&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Em resumo, se na sua organização:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ou&lt;/strong&gt; tem mais de 250 funcionários&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ou&lt;/strong&gt; fatura um montante que ultrapassa U$ 10 milhões&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Você terá de pagar para poder utilizar o Docker Desktop&lt;/strong&gt; para Windows ou MacOS.&lt;/p&gt;
&lt;h3&gt;
  
  
  Notas Finais
&lt;/h3&gt;

&lt;p&gt;Muito do que eu falei aqui provavelmente ficará desatualizado quando, finalmente, recebermos o Windows 11 em Outubro/2021!&lt;/p&gt;

&lt;p&gt;Mas, até lá, sinta-se à vontade para utilizar este post como uma base para configurar seu ambiente Windows. (Diga-se de passagem, eu mesmo tenho utilizado esse artigo, mesmo em rascunho, sempre que reinstalo o Windows em um de meus computadores)&lt;/p&gt;

&lt;p&gt;Outra recomendação, e esta será atualizada para o Windows 11, é buscar no &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; repositórios que tenham &lt;em&gt;dotfiles&lt;/em&gt; para ambientes Windows. O meu repositório de dotfiles está disponível lá! 👇&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rodolphocastro" rel="noopener noreferrer"&gt;
        rodolphocastro
      &lt;/a&gt; / &lt;a href="https://github.com/rodolphocastro/dotfiles" rel="noopener noreferrer"&gt;
        dotfiles
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Dotfiles for my Windows and Linux environments
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Rodolpho Alves' dotFiles&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Those are my personal settings for my Windows' environments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you want to use this consider forking it and changing it to your fit your own needs&lt;/em&gt;, don't go around blindly running scripts from the internet!&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What are dotfiles&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;In a nutshell:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Basically this is an old-school way to OneDrive/Google Sync/Dropbox your settings. At least those that are &lt;em&gt;easy to automate&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A &lt;code&gt;dotfiles&lt;/code&gt; repository is an attempt to keep multiple settings in-sync across multiple environments &lt;strong&gt;without relying on external solutions.&lt;/strong&gt; Basically once you have this repository done all you gotta do is &lt;code&gt;git clone&lt;/code&gt; it into a new environment and run the &lt;code&gt;bootstrap&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;The biggest challenge for &lt;em&gt;this&lt;/em&gt; set of &lt;code&gt;dotfiles&lt;/code&gt; is to deal with the Windows' way of setting stuff up. Thus why you'll notice some powershell wizardry going on. Things like what I'm trying to achieve here are way easier on &lt;code&gt;nix&lt;/code&gt;…&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/rodolphocastro/dotfiles" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>windows</category>
      <category>tutorial</category>
      <category>dotfiles</category>
      <category>git</category>
    </item>
    <item>
      <title>Cell CMS - Organizando, reutilizando e testando consultas do EntityFramework Core</title>
      <dc:creator>Rodolpho Alves</dc:creator>
      <pubDate>Thu, 04 Feb 2021 00:37:43 +0000</pubDate>
      <link>https://dev.to/ardc_overflow/cell-cms-organizando-reutilizando-e-testando-consultas-do-entityframework-core-o1p</link>
      <guid>https://dev.to/ardc_overflow/cell-cms-organizando-reutilizando-e-testando-consultas-do-entityframework-core-o1p</guid>
      <description>&lt;h1&gt;
  
  
  Cell CMS - Organizando, reutilizando e testando consultas do EntityFramework Core
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;No &lt;a href="https://dev.to/ardc_overflow/cell-cms-criando-testes-de-maneira-pratica-26o9"&gt;último post&lt;/a&gt; falamos sobre testes unitários e como tornar a escrita e manutenção deles prática. Mas também tocamos em um assunto controverso para alguns no universo .NET:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Devo utilizar &lt;strong&gt;EFCore.InMemory&lt;/strong&gt; nos meus testes unitários &lt;strong&gt;ou&lt;/strong&gt; devo abstrair &lt;strong&gt;uma UnitOfWork e repositórios&lt;/strong&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A ideia do post de hoje é apresentar, brevemente, os dois lados e uma solução que apesar de não ser aderente à Orientação a Objeto é uma solução prática, testável e escalável.&lt;/p&gt;

&lt;p&gt;Lembrando que boa parte dos snippets aqui estão implementados no Cell CMS, disponível lá no &lt;a href="https://github.com/rodolphocastro/cell-cms/tree/develop"&gt;GitHub&lt;/a&gt;!&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rodolphocastro"&gt;
        rodolphocastro
      &lt;/a&gt; / &lt;a href="https://github.com/rodolphocastro/cell-cms"&gt;
        cell-cms
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      CMS leve, self-contained e prático de utilizar! Feito por desenvolvedores e para desenvolvedores!
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Cell CMS&lt;/h1&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Branch&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Descrição&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Master&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/rodolphocastro/cell-cms/workflows/Build%20and%20Test/badge.svg?branch=master"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dfrxEcFX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/rodolphocastro/cell-cms/workflows/Build%2520and%2520Test/badge.svg%3Fbranch%3Dmaster" alt="Build and Test"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Ciclo estável, recomendado para produção&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Develop&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/rodolphocastro/cell-cms/workflows/Build%20and%20Test/badge.svg?branch=develop"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7ErPhYdx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/rodolphocastro/cell-cms/workflows/Build%2520and%2520Test/badge.svg%3Fbranch%3Ddevelop" alt="Build and Test"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Ciclo em desenvolvimento, recomendado para entusiastas&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Cell CMS&lt;/strong&gt; é um content management system que visa ser:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Leve&lt;/li&gt;
&lt;li&gt;Auto Contido (self-contained)&lt;/li&gt;
&lt;li&gt;Prático de Utilizar&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nosso foco é em disponibilizar um CMS que desenvolvedores possam facilmente referenciar em seus aplicativos, sites e sistemas.&lt;/p&gt;
&lt;h2&gt;
📚 Instruções&lt;/h2&gt;
&lt;h3&gt;
Utilizando uma Versão publicada&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;WIP, iremos suportar imagens Docker e executáveis&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
Compilando&lt;/h3&gt;
&lt;p&gt;Você &lt;strong&gt;precisará ter instalado&lt;/strong&gt; em seu ambiente o &lt;strong&gt;SDK 5.0.101 do Dotnet&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Uma vez configurado basta executar &lt;code&gt;dotnet build .\cell-cms.sln&lt;/code&gt; na raiz do repositório.&lt;/p&gt;
&lt;h3&gt;
Testando&lt;/h3&gt;
&lt;p&gt;Execute &lt;code&gt;dotnet test .\cell-cms.sln&lt;/code&gt; na raiz do repositório.&lt;/p&gt;
&lt;p&gt;Caso queira capturar informações de cobertura de testes utilize:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dotnet test --no-restore --collect:"XPlat Code Coverage" .\cell-cms.sln&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
⚙ Configurações&lt;/h2&gt;
&lt;h3&gt;
Autenticação/Autorização&lt;/h3&gt;
&lt;p&gt;O CellCMS utiliza o &lt;strong&gt;Azure Active Directory&lt;/strong&gt; como &lt;em&gt;provider&lt;/em&gt; de identidade, então você terá de configurar sua instância do &lt;strong&gt;AAD&lt;/strong&gt; conforme explicado &lt;a href="https://dev.to/ardc_overflow/cell-cms-autenticando-o-admin-270b" rel="nofollow"&gt;neste post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As seguintes variáveis de ambiente…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/rodolphocastro/cell-cms"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://undraw.co/"&gt;Cover pega no unDraw.co&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivação para Abstrair o EF Core
&lt;/h2&gt;

&lt;p&gt;A principal motivação para abstrair o EFCore é simples:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;É mais fácil fazer Mocks/Stubs de &lt;strong&gt;Interfaces, classes abstratas e métodos virtuais&lt;/strong&gt;, portanto &lt;strong&gt;elas são mais testáveis&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Devido à essa preocupação que o próprio projeto do EntityFramework Core criou o Provider InMemory&lt;/strong&gt;! Afinal, como esperar que seu framework seja amplamente adotado por projetos de grande porte se tudo que utiliza ele deixa de ser testável?&lt;/p&gt;

&lt;p&gt;Mas, vamos às abordagens práticas!&lt;/p&gt;

&lt;h2&gt;
  
  
  Abordagem "clássica" - UnitOfWork + Repositories
&lt;/h2&gt;

&lt;p&gt;A &lt;a href="https://www.martinfowler.com/eaaCatalog/unitOfWork.html"&gt;UnitOfWork&lt;/a&gt; é um dos "Enterprise Patterns". A ideia é bem simples:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Você precisa de &lt;strong&gt;uma classe que faça a gestão centralizada do estado do banco de dados durante um processo de negócio.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ou seja, é função da &lt;code&gt;UnitOfWork&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Prover acesso aos &lt;code&gt;objects&lt;/code&gt; que estão armazenados nas tabelas do seu banco&lt;/li&gt;
&lt;li&gt;Prover mecanismos para salvar alterações (Commit)&lt;/li&gt;
&lt;li&gt;Prover mecanismos para reverter alterações (Rollback)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Normalmente junto à &lt;code&gt;UnitOfWork&lt;/code&gt; vemos um segundo padrão chamado de &lt;a href="https://www.martinfowler.com/eaaCatalog/repository.html"&gt;Repository&lt;/a&gt;. A ideia de um &lt;code&gt;Repository&lt;/code&gt; é:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Centralizar as consultas (&lt;em&gt;Queries&lt;/em&gt;) a algum &lt;code&gt;object&lt;/code&gt;&lt;/strong&gt;, de maneira que possamos utilizar lógicas adicionais (como um Cache) em um único lugar e minimizar a quantidade de código replicado dentro do sistema.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Soa familiar com o que um &lt;code&gt;DbSet&amp;lt;T&amp;gt;&lt;/code&gt; e um &lt;code&gt;DbContext&lt;/code&gt; faz, não? &lt;em&gt;Pois é&lt;/em&gt;! Mas chega de &lt;em&gt;foreshadowing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Como são patterns eles normalmente serão introduzidos como dependências da sua classes através de suas interfaces, normalmente chamadas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;IUnitOfWork&lt;/code&gt;, &lt;code&gt;IProjetoUnitOfWork&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IMeuObjetoRepository&lt;/code&gt;, &lt;code&gt;GenericRepository&amp;lt;T&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Agora vamos pensar em suas vantagens e desvantagens, sempre &lt;strong&gt;considerando que estamos utilizando o EntityFramework Core como nosso ORM&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Caso você queira saber mais sobre os Enterprise Patterns: Dê uma olhada no livro &lt;a href="https://www.amazon.com.br/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420"&gt;Patterns of Enterprise Application Architecture do Martin Fowler&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Vantagens
&lt;/h3&gt;

&lt;p&gt;As principais vantagens são:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Abstração&lt;/strong&gt;: Permite que utilizemos Mocks e Stubs de maneira extremamente prática&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amplamente conhecido&lt;/strong&gt;: Esse pattern é quase tão conhecido como Singletons&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mantém o padrão de Orientação a Objeto&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;As interfaces podem ser exportadas facilmente para outros projetos&lt;/strong&gt;: Se você quiser fazer uma reutilização máxima de código você poderia declarar um repositório no seu projeto de Domínio e deixar que os diferentes runtimes façam a implementação.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Para mim as principais vantagens são #1 e #4. Especialmente se pensarmos em utilizar uma abordagem que preza pela reutilização: A mesma interface pode ser implementada no frontend com acesso a uma API REST e no backend com acesso a um banco relacional!&lt;/p&gt;

&lt;h3&gt;
  
  
  Desvantagens
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Mais&lt;/strong&gt; uma classe para &lt;strong&gt;Manutenção&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difícil de adicionar novas funcionalidade&lt;/strong&gt;: Como você deve alterar a assinatura da Interface (ou criar uma nova) cada nova query é um novo método&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difícil de compor queries&lt;/strong&gt;: Se você permite múltiplos filtros terá de ter métodos com inúmeros parâmetros!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Utilização de &lt;code&gt;generics&lt;/code&gt; orientados a Copy-Paste&lt;/strong&gt;: Essa acho que é o maior downside que vi para a abordagem até hoje! Inúmeras vezes você vê o mesmo código colado em diferentes projetos para criar um &lt;code&gt;GenericRepository&amp;lt;T&amp;gt;&lt;/code&gt; que alivia alguns dos problemas acima&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quebra do próprio Pattern&lt;/strong&gt;: Para tentar balancear com a nova dependência é comum ver Repositories que retornam DTOs, fazem alterações no banco, etc...&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Para mim as principais desvantagens são as #1, #2 e #5. Eu mesmo já fiz repositories que fazem mais que consultar e já tive de quebrar interfaces por causa de um parâmetro a mais de filtro.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exemplos
&lt;/h3&gt;

&lt;p&gt;O seguinte snippet contém uma implementação (feita para exemplo apenas, bem incompleta!) de uma &lt;em&gt;Unit of Work&lt;/em&gt; para o Cell CMS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Descreve métodos para uma UnitOfWork do CellCMS.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;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;ICellCMSUnitOfWork&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Feed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Feeds&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="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;CommitChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Descreve métodos para um repositório.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;typeparam name="T"&amp;gt;&amp;lt;/typeparam&amp;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;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
&lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nc"&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;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ListAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Implementação genérica de um repository.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;typeparam name="T"&amp;gt;&amp;lt;/typeparam&amp;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;GenericRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
&lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nc"&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;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_set&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;GenericRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CellContext&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;_set&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;Set&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="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;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ListAll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Simulação de uma Unit of Work para o CELL CMS.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;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;CellCMSUnitOfWork&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ICellCMSUnitOfWork&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;CellContext&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="n"&gt;IRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Feed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Feeds&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;private&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="nf"&gt;CellCMSUnitOfWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CellContext&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&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;Feeds&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;GenericRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Feed&lt;/span&gt;&lt;span class="p"&gt;&amp;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;Task&lt;/span&gt; &lt;span class="nf"&gt;CommitChanges&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;_context&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Abordagem funcional - Extension Methods
&lt;/h2&gt;

&lt;p&gt;Primeiramente um disclaimer: &lt;strong&gt;Essa solução é boa para o universo .NET, onde temos suporte a Extension Methods&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Um &lt;code&gt;extension method&lt;/code&gt; é um &lt;strong&gt;método que adiciona funcionalidade a uma classe/interface existente sem precisar alterar o código original&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Boa parte das operações &lt;code&gt;LINQ&lt;/code&gt; são extension methods, assim como maior parte das opções do próprio EntityFrameworkCore! Sem acesso ao código podemos confiar no &lt;code&gt;Intellisense&lt;/code&gt; para mostrar quando estamos lidando com um método que foi adicionado a um tipo por &lt;code&gt;extension method&lt;/code&gt;, note o &lt;em&gt;ícone diferente&lt;/em&gt; nesta imagem:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--omIU9lYp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7z89afj8zim4ztqic16n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--omIU9lYp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7z89afj8zim4ztqic16n.png" alt="Extension Method icon on intellisense" width="702" height="139"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Extension Methods 101
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Mas Rodolpho, como posso fazer um &lt;code&gt;extension method&lt;/code&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;É mais simples do que parece! Você só precisa:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;De uma &lt;code&gt;static class&lt;/code&gt; onde seu método será armazenado&lt;/li&gt;
&lt;li&gt;De acesso aos métodos do &lt;code&gt;Tipo&lt;/code&gt; a ser estendido&lt;/li&gt;
&lt;li&gt;Criar um método &lt;code&gt;static&lt;/code&gt; onde o primeiro parâmetro recebe a &lt;code&gt;keyword&lt;/code&gt; especial &lt;code&gt;this&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Por exemplo, os seguintes &lt;code&gt;extension method&lt;/code&gt; permitem que eu serialize um objeto para JSON, recupere o mesmo objeto a partir de um JSON e realize uma cópia do objeto através de serialização:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note que consegui estender um &lt;code&gt;generic T&lt;/code&gt; e uma &lt;code&gt;string&lt;/code&gt; e, nas últimas 3 linhas, eles são chamados como se fosse realmente parte do tipo &lt;code&gt;Feed&lt;/code&gt; e &lt;code&gt;string&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyExtensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Serializa um objeto para um JSON.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;typeparam name="T"&amp;gt;&amp;lt;/typeparam&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="obj"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;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="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Dump&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;obj&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;T&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="k"&gt;return&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;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Restaura um objeto de a partir de um JSON.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;typeparam name="T"&amp;gt;&amp;lt;/typeparam&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="t"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;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;T&lt;/span&gt; &lt;span class="n"&gt;Restore&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;t&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;T&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Clona um objeto.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;typeparam name="T"&amp;gt;&amp;lt;/typeparam&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="from"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;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;T&lt;/span&gt; &lt;span class="n"&gt;Clone&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="k"&gt;from&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;T&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
            &lt;span class="nf"&gt;Dump&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
            &lt;span class="n"&gt;Restore&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usando os extension methods&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cobaia&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;Feed&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;copy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cobaia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;copy&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como o Visual Studio mostraria que estou usando uma extension:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MTpwEfIC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/d1dihmzpdwh7roj0wi5w.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MTpwEfIC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/d1dihmzpdwh7roj0wi5w.gif" alt="Utilizando um extension method com Intellisense" width="504" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Vantagens
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Testável por unidade&lt;/strong&gt;: Como fazemos extensão de um &lt;code&gt;IQueryable&amp;lt;T&amp;gt;&lt;/code&gt; podemos testar através de uma &lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testável por integração&lt;/strong&gt;: Novamente, como estendemos &lt;code&gt;IQueryable&amp;lt;T&amp;gt;&lt;/code&gt; podemos testar através de um &lt;code&gt;DbSet&amp;lt;T&amp;gt;&lt;/code&gt; do EFCore&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Armazenável próximo ao Model&lt;/strong&gt;: Se você curte Clean Code, podemos deixar estas queries no mesmo arquivo que a classe do Model!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permite composição&lt;/strong&gt;: Podemos chamar filtros após filtros conforme nossa necessidade, sempre precisar de um novo método para cada nova combinação de filtros&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Não duplica o pattern &lt;code&gt;UoW&lt;/code&gt; + &lt;code&gt;Repository&lt;/code&gt;&lt;/strong&gt; em sua aplicação.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Todos são pontos bem legais, na minha opinião, mas vou dar uma ênfase no #5 e faço isso pois &lt;strong&gt;o próprio EntityFramework Core é uma implementação deste pattern&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;O &lt;code&gt;DbContext&lt;/code&gt; que herdamos é uma &lt;code&gt;UnitOfWork&lt;/code&gt; de cara por nos permitir &lt;strong&gt;criar transações, realizar commits e rollbacks&lt;/strong&gt;. E cada &lt;strong&gt;&lt;code&gt;DbSet&amp;lt;T&amp;gt;&lt;/code&gt; também é repositório pois nos permite interagir com o objetos armazenados no banco!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A própria &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext?view=efcore-5.0"&gt;documentação do EntityFrameworkCore&lt;/a&gt; explica:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Uma instância do DbContext representa uma sessão com o banco de dados e pode ser utilizado para consultar e salvar instâncias de suas entidades. O DbContext é uma combinação dos patterns Unit Of Work e Repository. &lt;em&gt;(tradução livre pelo autor)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Com base nisso podemos concluir que:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Uma &lt;code&gt;UnitOfWork&lt;/code&gt; sobre um &lt;code&gt;DbContext&lt;/code&gt; é &lt;strong&gt;uma abstração feita sobre uma abstração!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Desvantagens
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Menos reutilizável longe do EFCore&lt;/strong&gt;: Como dependemos do &lt;code&gt;IQueryable&amp;lt;T&amp;gt;&lt;/code&gt; fica mais complicado reutilizar essas queries em um mobile Xamarin, por exemplo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pouca familiaridade com &lt;code&gt;extension methods&lt;/code&gt;&lt;/strong&gt;: Apesar de estarem ai a anos nem todo desenvolvedor já teve de escrever seu próprio &lt;code&gt;extension method&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependendo do &lt;code&gt;namespace&lt;/code&gt; podem causar confusão&lt;/strong&gt;: Extension methods são carregados por &lt;code&gt;namespace&lt;/code&gt; de uma única vez. Você não consegue escolher quais métodos serão importados.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Exemplos
&lt;/h3&gt;

&lt;p&gt;O seguinte snippet contém algumas das queries do Cell CMS que migrei para este padrão e alguns testes mostrando como elas podem ser utilizadas, como se comportam com os diferentes &lt;em&gt;datasets&lt;/em&gt; e &lt;em&gt;chains&lt;/em&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="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// Queries relacionadas a Feeds.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/summary&amp;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;FeedQueries&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Obtem todos os feeds.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="feeds"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;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;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Feed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;AllFeeds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Feed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;feeds&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;feeds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Filtra feeds com base no ID.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="feeds"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="id"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;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;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Feed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;WithId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Feed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;feeds&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;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&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;Equals&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="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Filtra feeds com base no Nome.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="feeds"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;param name="name"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;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;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Feed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;FilterByNome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Feed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;feeds&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;{&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;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;feeds&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;feeds&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;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&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="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvariantCultureIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Testes&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Theory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreateData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Trait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TraitsConstants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TraitsConstants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Domain&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;WithNome_EmptyData_ReturnsEmpty&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;nome&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Arrange&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_emptyResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsQueryable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Act&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;subject&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FilterByNome&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="c1"&gt;// Assert&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;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;BeEmpty&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;Theory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreateData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Trait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TraitsConstants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TraitsConstants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Domain&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;WithNome_WithDataAndValidName_ReturnsData&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;Feed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Feed&lt;/span&gt; &lt;span class="n"&gt;extraFeed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Arrange&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;nome&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;extraFeed&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;feeds&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Concat&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;Feed&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;extraFeed&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsQueryable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Act&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;subject&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FilterByNome&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="c1"&gt;// Assert&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;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;NotBeEmpty&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;Theory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreateData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Trait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TraitsConstants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TraitsConstants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Domain&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;WithNome_WithDataAndInvalidName_ReturnsData&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;Feed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Frozen&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;fixedName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Arrange&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;nome&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fixedName&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="s"&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;subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsQueryable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Act&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;subject&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FilterByNome&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="c1"&gt;// Assert&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;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;BeEmpty&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;Theory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreateData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Trait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TraitsConstants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TraitsConstants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Domain&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;WithIdAndNome_WithDataAndValidNameAndValidID_ReturnsData&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;Feed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Feed&lt;/span&gt; &lt;span class="n"&gt;extraFeed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Arrange&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;nome&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;extraFeed&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="kt"&gt;var&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;extraFeed&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;feeds&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Concat&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;Feed&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;extraFeed&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsQueryable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Act&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;subject&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithId&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FilterByNome&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="c1"&gt;// Assert&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;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;NotBeEmpty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Eu, pessoalmente, sempre utilizei o padrão &lt;code&gt;UnitOfWork&lt;/code&gt; + &lt;code&gt;Repository&lt;/code&gt;. Porém, ao descobrir que os &lt;code&gt;extension methods&lt;/code&gt; poderiam me dar a mesma funcionalidade mas com um menor &lt;em&gt;overhead&lt;/em&gt; &lt;strong&gt;passei a preferir &lt;code&gt;extension methods&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Caso ainda não esteja convencido dê uma olhada nos testes e refactors feitos no Cell CMS, que agora está seguindo este padrão, para basear sua decisão!&lt;/p&gt;

&lt;p&gt;No fundo essa decisão é uma questão de gosto pessoal. Eu prefiro ser pragmático e minimizar a quantidade de coisas que preciso dar manutenção e testar. Mas para seu projeto/equipe pode ser que o padrão &lt;code&gt;UnitOfWork&lt;/code&gt; seja melhor aceito!&lt;/p&gt;

&lt;p&gt;O post de hoje vai ficando por aqui, espero que tenha dado o que pensar! O próximo post provavelmente será sobre &lt;code&gt;Analyzers&lt;/code&gt; ou &lt;em&gt;Configuração de um Ambiente Windows para desenvolvimento&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;Fiquem ligados para o próximo, obrigado por lerem e até então!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>refactor</category>
      <category>tutorial</category>
      <category>patterns</category>
    </item>
    <item>
      <title>Cell CMS - Criando testes de maneira prática</title>
      <dc:creator>Rodolpho Alves</dc:creator>
      <pubDate>Sun, 31 Jan 2021 18:17:33 +0000</pubDate>
      <link>https://dev.to/ardc_overflow/cell-cms-criando-testes-de-maneira-pratica-26o9</link>
      <guid>https://dev.to/ardc_overflow/cell-cms-criando-testes-de-maneira-pratica-26o9</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;No &lt;a href="https://dev.to/ardc_overflow/cell-cms-utilizando-docker-para-criar-containers-3h4o"&gt;último post&lt;/a&gt; falamos sobre como utilizar Docker e suas ferramentas de suporte dentro do Visual Studio, quais são suas vantagens e como Containers resolvem vários problemas "clássicos" do deploy.&lt;/p&gt;

&lt;p&gt;No post de hoje vamos fazer algo que deveríamos ter feito desde o começo do Cell CMS: Criar nossos projetos de testes unitários.&lt;/p&gt;

&lt;p&gt;O branch principal para o post de hoje será o &lt;code&gt;feature/create-tests&lt;/code&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rodolphocastro"&gt;
        rodolphocastro
      &lt;/a&gt; / &lt;a href="https://github.com/rodolphocastro/cell-cms"&gt;
        cell-cms
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      CMS leve, self-contained e prático de utilizar! Feito por desenvolvedores e para desenvolvedores!
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Cell CMS&lt;/h1&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Branch&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Descrição&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Master&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/rodolphocastro/cell-cms/workflows/Build%20and%20Test/badge.svg?branch=master"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dfrxEcFX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/rodolphocastro/cell-cms/workflows/Build%2520and%2520Test/badge.svg%3Fbranch%3Dmaster" alt="Build and Test"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Ciclo estável, recomendado para produção&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Develop&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/rodolphocastro/cell-cms/workflows/Build%20and%20Test/badge.svg?branch=develop"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7ErPhYdx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/rodolphocastro/cell-cms/workflows/Build%2520and%2520Test/badge.svg%3Fbranch%3Ddevelop" alt="Build and Test"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Ciclo em desenvolvimento, recomendado para entusiastas&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Cell CMS&lt;/strong&gt; é um content management system que visa ser:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Leve&lt;/li&gt;
&lt;li&gt;Auto Contido (self-contained)&lt;/li&gt;
&lt;li&gt;Prático de Utilizar&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nosso foco é em disponibilizar um CMS que desenvolvedores possam facilmente referenciar em seus aplicativos, sites e sistemas.&lt;/p&gt;
&lt;h2&gt;
📚 Instruções&lt;/h2&gt;
&lt;h3&gt;
Utilizando uma Versão publicada&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;WIP, iremos suportar imagens Docker e executáveis&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
Compilando&lt;/h3&gt;
&lt;p&gt;Você &lt;strong&gt;precisará ter instalado&lt;/strong&gt; em seu ambiente o &lt;strong&gt;SDK 5.0.101 do Dotnet&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Uma vez configurado basta executar &lt;code&gt;dotnet build .\cell-cms.sln&lt;/code&gt; na raiz do repositório.&lt;/p&gt;
&lt;h3&gt;
Testando&lt;/h3&gt;
&lt;p&gt;Execute &lt;code&gt;dotnet test .\cell-cms.sln&lt;/code&gt; na raiz do repositório.&lt;/p&gt;
&lt;p&gt;Caso queira capturar informações de cobertura de testes utilize:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dotnet test --no-restore --collect:"XPlat Code Coverage" .\cell-cms.sln&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
⚙ Configurações&lt;/h2&gt;
&lt;h3&gt;
Autenticação/Autorização&lt;/h3&gt;
&lt;p&gt;O CellCMS utiliza o &lt;strong&gt;Azure Active Directory&lt;/strong&gt; como &lt;em&gt;provider&lt;/em&gt; de identidade, então você terá de configurar sua instância do &lt;strong&gt;AAD&lt;/strong&gt; conforme explicado &lt;a href="https://dev.to/ardc_overflow/cell-cms-autenticando-o-admin-270b" rel="nofollow"&gt;neste post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As seguintes variáveis de ambiente…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/rodolphocastro/cell-cms"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://undraw.co/"&gt;Cover do artigo pega lá no unDraw&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Últimas alterações do Cell CMS
&lt;/h2&gt;

&lt;p&gt;Faz bastante tempo desde o meu último post. Pois é! Infelizmente acabei de distraindo com outros afazeres e estudos e acabei deixando de lado o bom hábito de escrever 😅.&lt;/p&gt;

&lt;p&gt;Mas o Cell CMS teve algumas alterações entre o último post e este! De maneira resumida:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Migramos para .NET 5 (originalmente estávamos no .NET Core 3.1)&lt;/li&gt;
&lt;li&gt;Criamos uma pipeline no GitHub Actions para compilar o projeto continuamente&lt;/li&gt;
&lt;li&gt;Inúmeras alterações no meu ambiente de desenvolvimento&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Eventualmente escreverei um pouco sobre estes processos, provavelmente começando pelo do ambiente (em breve devo reconfigurar &lt;em&gt;mais uma vez&lt;/em&gt;). Prometo!&lt;/p&gt;

&lt;p&gt;Sem mais delongas, vamos para o conteúdo em si!&lt;/p&gt;

&lt;h2&gt;
  
  
  Testes Unitários: O que são, de onde vieram e para que servem
&lt;/h2&gt;

&lt;p&gt;De maneira bem resumida podemos dizer que &lt;strong&gt;testes unitários&lt;/strong&gt; &lt;em&gt;(na programação orientada a objetos)&lt;/em&gt; &lt;strong&gt;são rotinas automatizadas para garantir o funcionamento de uma classe&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Mas o que isso quer dizer, na prática?&lt;/p&gt;

&lt;p&gt;Na prática isso quer dizer que você terá um conjunto de classes e métodos especializadas em testar de maneira rápida cada classe que compõe o seu sistema, mas preste atenção pois &lt;strong&gt;eles não fazem tudo&lt;/strong&gt;! Como o próprio nome já diz o &lt;strong&gt;escopo deste teste é a unidade&lt;/strong&gt; (que, eventualmente, compõem o todo do seu sistema 🤷‍♂️). &lt;/p&gt;

&lt;p&gt;Outra grande vantagem de ter uma boa cobertura de testes unitários é poder refatorar sem medo, afinal os testes vão garantir que você não quebrou nenhuma API pública (ou se você precisou quebrar pelo menos vai te lembrar de avisar os consumidores dessa API como um &lt;code&gt;[Obsolete]&lt;/code&gt; antes de remover de vez!).&lt;/p&gt;

&lt;p&gt;Se você quiser saber mais sobre refatorar um amigo meu está fazendo uma série de vídeos especialmente sobre isso, dê uma olhada no canal &lt;a href="https://www.youtube.com/c/AspiraPlay/featured"&gt;AspiraPlay&lt;/a&gt; no YouTube!&lt;/p&gt;

&lt;p&gt;Existem, além dos testes unitários, vários outros tipos de testes (manuais e automatizados) mas para o desenvolvedor o teste mais rápido de fazer e executar será, sempre, o unitário. E por isso vamos falar primeiros deles antes de pensarmos em testes de integração, funcionalidades, aceite, etc.&lt;/p&gt;

&lt;p&gt;Uma boa leitura para esse assunto é o &lt;a href="https://martinfowler.com/testing/"&gt;próprio site do grande Martin Fowler&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Como escrever um bom teste unitário
&lt;/h3&gt;

&lt;p&gt;Seja qual for o framework ou linguagem de programação que você esteja utilizando sempre encontrará essas características em um bom teste unitário.&lt;/p&gt;

&lt;h4&gt;
  
  
  Nome do Teste
&lt;/h4&gt;

&lt;p&gt;O primeiro elemento importante é o &lt;strong&gt;nome do seu teste&lt;/strong&gt;. É no nome em que vamos dar aquela resumida em:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;O que estamos testando&lt;/li&gt;
&lt;li&gt;Sob quais condições/estado estamos testando&lt;/li&gt;
&lt;li&gt;O que esperamos&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Em alguns frameworks o &lt;strong&gt;nome do teste será o nome do método que executa a rotina de teste&lt;/strong&gt;, em outros são utilizadas anotações, classes, atributos, comentários... Mas a constante é sempre essa:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Você deve identificar o que está testando, sob quais condições e o que você espera de resultado.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Entradas (Inputs)
&lt;/h4&gt;

&lt;p&gt;O segundo elemento importante para seu teste é identificar &lt;strong&gt;as entradas&lt;/strong&gt; para a rotina que você está testando. Note que isso poderá mudar &lt;em&gt;bastante&lt;/em&gt; mas você sempre deve pensar em isolar bem o que você quer que entre no seu teste para que garanta que ele é preciso!&lt;/p&gt;

&lt;p&gt;Alguns exemplos de testes e inputs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Testar uma classe que cria um JSON -&amp;gt; Input seriam objetos diferentes&lt;/li&gt;
&lt;li&gt;Testar uma classe que utiliza outra para salvar algo -&amp;gt; Input seria a classe que será utilizada via &lt;em&gt;mock&lt;/em&gt; e o dado que seria salvo&lt;/li&gt;
&lt;li&gt;Testar uma regra de negócio -&amp;gt; Input que consiga disparar esta regra de negócio e inputs que falhem a regra de negócio&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Não se preocupe ainda com o que é um &lt;em&gt;mock&lt;/em&gt;, falaremos disso depois!&lt;/p&gt;

&lt;h4&gt;
  
  
  O que está sendo testado (Subject)
&lt;/h4&gt;

&lt;p&gt;O terceiro elemento (que &lt;strong&gt;apesar da ordem é o mais importante!&lt;/strong&gt;) é o que você quer testar de verdade!&lt;/p&gt;

&lt;p&gt;A importância de saber quem é este elemento é vital para que seu teste seja unitário de verdade. &lt;strong&gt;Se você não consegue identificar um único elemento você provavelmente está escrevendo um teste de integração ou você precisa rever as dependências de sua classe.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  O estado do que está sendo testado (State)
&lt;/h4&gt;

&lt;p&gt;Em quarto lugar você precisa pensar qual o estado que você quer testar. Isso pode ser:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Uma dependência falhando&lt;/li&gt;
&lt;li&gt;Uma input inválida&lt;/li&gt;
&lt;li&gt;Uma referência nula&lt;/li&gt;
&lt;li&gt;Um parâmetro sendo omitido&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Nunca tente testar todos os possíveis estados&lt;/strong&gt;! Uma cobertura de 100% de testes pode significar que você está investindo mais tempo em testes do que funcionalidades novas!&lt;/p&gt;

&lt;p&gt;Minha preferência &lt;em&gt;pessoal&lt;/em&gt; é começar testando sempre dois estados: &lt;strong&gt;O válido e o principal inválido&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  O que é esperado
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Finalmente&lt;/em&gt; o último elemento é: &lt;strong&gt;o que você espera que aconteça?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Por exemplo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retorne null&lt;/li&gt;
&lt;li&gt;Retorne um objeto válido&lt;/li&gt;
&lt;li&gt;Lance uma &lt;code&gt;Exception&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Dê timeout após ... milissegundos &lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Juntando tudo: AAA
&lt;/h3&gt;

&lt;p&gt;Como lembrar de tudo isso? Uma das práticas mais comuns é sempre lembras dos 3 As:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Arrange&lt;/strong&gt;: Escolha as inputs, o que será testado e monte o cenário&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Act&lt;/strong&gt;: Realize a ação que você quer que seja testada&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assert&lt;/strong&gt;: Verifique que a ação fez o que você esperava&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Eu normalmente abro meus testes já escrevendo 3 linhas de comentários e então vou preenchendo a lógica:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Arrange&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subject&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;MinhaClasseSendoTestada&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="c1"&gt;// Act&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;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FazAlgumaCoisa&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Assert&lt;/span&gt;
&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNull&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Mocks e Stubs
&lt;/h3&gt;

&lt;p&gt;Não sou um expert no assunto (dê uma pesquisada sobre Kent Beck, TDD Chicago e London para saber mais sobre isso) mas de maneira bem resumida:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Um Mock é um objeto que simula o comportamento de outro objeto&lt;/strong&gt;, permitindo que você controle o que o objeto real faria e valide quantas vezes ele foi chamado, com quais parâmetros, etc. Você estará testando por &lt;em&gt;comportamento&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Um STUB é um objeto que simula apenas o retorno de outro objeto&lt;/strong&gt;, entregando respostas fixas para chamadas fixas, controlando o que é retornado quando chamariam o objeto real e apenas isso. Você estará testando por &lt;strong&gt;estado&lt;/strong&gt;, maior parte das vezes, porém também poderia testar por &lt;em&gt;comportamento&lt;/em&gt; com algumas alterações.&lt;/p&gt;
&lt;h2&gt;
  
  
  Unit Tests com .NET
&lt;/h2&gt;

&lt;p&gt;Agora que temos uma ideia do que são testes unitários vamos pensar neles no universo do .NET, de cara já podemos dizer que existem 3 frameworks populares de testes unitários:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;nUnit&lt;/strong&gt; -&amp;gt; Mais clássico mas amplamente utilizado, com diversos plugins e runners&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;xUnit&lt;/strong&gt; -&amp;gt; Mais moderno e próximo à ideia de TDD, também é amplamente utilizado&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MSTest&lt;/strong&gt; -&amp;gt; Não recomendado mais atualmente&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A principal diferença que nós, como desenvolvedores, vamos notar entre o nUnit e o xUnit é a maneira com que eles lidam com as classes de teste.&lt;/p&gt;

&lt;p&gt;Por padrão &lt;strong&gt;o xUnit cria uma instância da classe para executar cada método de teste&lt;/strong&gt;. Você sempre terá uma certa segurança de que o estado da sua classe de teste está limpo.&lt;/p&gt;

&lt;p&gt;Enquanto isso &lt;strong&gt;o nUnit utiliza a mesma instância da classe para executar todos os métodos de teste&lt;/strong&gt;. Você terá de tomar cuidado com instâncias que podem ser alteradas pelos testes em si, levando a interdependências, travando a ordem de execução, etc...&lt;/p&gt;

&lt;p&gt;Porém, seja xUnit ou nUnit, as classes de teste conterão os seguintes elementos:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Métodos()&lt;/code&gt; indicando as rotinas de teste&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[Atributos]&lt;/code&gt; indicando que um método é um teste com ou sem parâmetros&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[OutrosAtributos]&lt;/code&gt; indicando métodos de configuração e limpeza dos seus testes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Não vou entrar a fundo nos atributos e setups de nenhum dos dois, porém na seção de codificação mesmo você poderá ver como fica uma classe de teste utilizando o xUnit!&lt;/p&gt;
&lt;h3&gt;
  
  
  AutoFixture - Criação automática de massa de dados
&lt;/h3&gt;

&lt;p&gt;Comentamos acima sobre a necessidade de sabermos as inputs dos nossos testes, certo? Porém as vezes você &lt;strong&gt;sabe o que precisa de input&lt;/strong&gt; mas &lt;strong&gt;não se importa com os detalhes&lt;/strong&gt;, certo? Nesses cenários pode ser tornar chato você ter de sempre adicionar N parâmetros no seu teste e sempre criar um &lt;code&gt;new ObjetoInput(param1, param2, ...);&lt;/code&gt;. A biblioteca &lt;strong&gt;AutoFixture&lt;/strong&gt; resolve exatamente isso!&lt;/p&gt;

&lt;p&gt;De maneira sucinta:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;AutoFixture gera&lt;/strong&gt; a massa de &lt;strong&gt;dados para testes automaticamente para você&lt;/strong&gt;. Permitindo que você &lt;strong&gt;gaste menos tempo na fase de Arrange&lt;/strong&gt; de seu teste.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;E melhor ainda: Ele &lt;strong&gt;funciona&lt;/strong&gt;, de cara, &lt;strong&gt;sem nenhuma configuração&lt;/strong&gt; em boa parte dos casos, &lt;strong&gt;porém você pode configurar ele com regras específicas para casos específicos&lt;/strong&gt;!&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/AutoFixture"&gt;
        AutoFixture
      &lt;/a&gt; / &lt;a href="https://github.com/AutoFixture/AutoFixture"&gt;
        AutoFixture
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      AutoFixture is an open source library for .NET designed to minimize the 'Arrange' phase of your unit tests in order to maximize maintainability. Its primary goal is to allow developers to focus on what is being tested rather than how to setup the test scenario, by making it easier to create object graphs containing test data.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  NSubstitute - Criação e Configuração de Mocks
&lt;/h3&gt;

&lt;p&gt;A biblioteca &lt;strong&gt;NSubstitute&lt;/strong&gt; é mais uma facilitadora para a nossa fase de &lt;strong&gt;Arrange&lt;/strong&gt; dos testes. Lembra sobre mocks e stubs? Esse carinha aqui é quem vai criar os mocks pra gente.&lt;/p&gt;

&lt;p&gt;De maneira curta:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NSubstitute &lt;strong&gt;cria&lt;/strong&gt;, automaticamente, &lt;strong&gt;mocks para interfaces e métodos virtuais&lt;/strong&gt;. Além disso você poderá &lt;strong&gt;controlar o que esses mocks retornam, quantas vezes podem ser chamados, etc&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/nsubstitute"&gt;
        nsubstitute
      &lt;/a&gt; / &lt;a href="https://github.com/nsubstitute/NSubstitute"&gt;
        NSubstitute
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A friendly substitute for .NET mocking libraries.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Nota: Por muito tempo utilizei uma outra biblioteca chamada &lt;a href="https://github.com/moq/moq"&gt;Moq&lt;/a&gt; que faz a mesma coisa porém com sintaxe diferente. Ultimamente tenho dado preferência pelo NSubstitute exatamente por parecer legível.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Um breve exemplo do NSubstitute:&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;interface&lt;/span&gt; &lt;span class="nc"&gt;IEmailPublisher&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;Driver&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="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SendTo&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;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;ct&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&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;publisher&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Substitute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;For&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IEmailPublisher&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;publisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"temp"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;publisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MyDriver123"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//publisher.SendTo().ThrowsForAnyArgs&amp;lt;NotImplementedException&amp;gt;();   // Caso queira simular um erro&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;publisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Driver&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MyDriver123"&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="c1"&gt;// -&amp;gt; True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  FluentAssertions - Sintaxe fluent/builder para validar os cenários
&lt;/h3&gt;

&lt;p&gt;FluentAssertions é uma biblioteca que não vai te ajudar a economizar tempo enquanto prepara seus testes mas &lt;strong&gt;vai tornar seus testes mais legíveis&lt;/strong&gt; a longo prazo!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;FluentAssertions&lt;/strong&gt; permite que você &lt;strong&gt;descreva o que é esperado&lt;/strong&gt; do seu teste &lt;strong&gt;usando uma linguagem&lt;/strong&gt; mais próxima à &lt;strong&gt;natural&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Por exemplo, se eu quisesse validar em um teste que: "O resultado não é nulo, é do tipo X e é equivalente ao objeto Y"&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Usando fluent assertions&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;Should&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NotBeNull&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BeOfType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeEquivalentTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Usando Asserts&lt;/span&gt;
&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NotNull&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="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;resultado&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsEqual&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="n"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/fluentassertions"&gt;
        fluentassertions
      &lt;/a&gt; / &lt;a href="https://github.com/fluentassertions/fluentassertions"&gt;
        fluentassertions
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A very extensive set of extension methods that allow you to more naturally specify the expected outcome of a TDD or BDD-style unit tests. Targets .NET Framework 4.7, as well as .NET Core 2.1, .NET Core 3.0, .NET 6, .NET Standard 2.0 and 2.1. Supports the unit test frameworks MSTest2, NUnit3, XUnit2, MSpec, and NSpec3.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  EntityFrameworkCore.InMemory - Versão do EFCore para testes unitários
&lt;/h3&gt;

&lt;p&gt;Normalmente utilizaríamos um Mock/Stub para simular o acesso ao banco em nossos testes unitários, porém os objetos e tipos do EntityFrameworkCore, apesar de "mockáveis", requerem um setup bem extenso.&lt;/p&gt;

&lt;p&gt;Com isso em mente o próprio EntityFrameworkCore já criou uma biblioteca exatamente para que não precisemos fazer todo esse setup. O provider &lt;code&gt;InMemory&lt;/code&gt; é a maneira de simular um acesso ao banco nos nossos testes unitários.&lt;/p&gt;

&lt;p&gt;Existe uma discussão &lt;em&gt;bem extensa&lt;/em&gt; sobre se usar isso não configuraria seu teste como um teste de integração e se a maneira correta não seria voltar ao velho padrão de Unit Of Work + Repositories (que o próprio EFCore já implementa por si só 🤷‍♂️).&lt;/p&gt;

&lt;p&gt;Meu take pessoal nesse assunto é que devemos ser pragmáticos. A biblioteca está ai, pronta. &lt;strong&gt;Salvo que eu precise muito por que vou criar mais uma camada?&lt;/strong&gt; Abstrações são boas (vida longa a interfaces e classe abstratas!) mas da mesma maneira que &lt;strong&gt;você pode pecar por usar só implementação concretas você também pode pecar por criar abstrações para tudo!&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Abstrair só por abstrair é overengineering.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/dotnet"&gt;
        dotnet
      &lt;/a&gt; / &lt;a href="https://github.com/dotnet/efcore"&gt;
        efcore
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Mãos à obra: Criando nosso projeto de testes
&lt;/h2&gt;

&lt;p&gt;Vamos à pratica! Fora do Visual Studio vamos criar uma nova pasta na raiz do projeto, chamada &lt;code&gt;tests/&lt;/code&gt;. A ideia aqui é que todos os nossos projetos de testes (Unit, Integration e Feature) ficarão nesta pasta para não bagunçar a hierarquia das outras bibliotecas do projeto.&lt;/p&gt;

&lt;h3&gt;
  
  
  Criando o Projeto e Instalando dependências
&lt;/h3&gt;

&lt;p&gt;Pelo Visual Studio:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clique direito na solução e escolha &lt;code&gt;Add new project&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Pesquise nos templates por &lt;code&gt;xUnit&lt;/code&gt; e localize um chamado &lt;code&gt;xUnit Test Project (.NET Core)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Escolha um nome para seu projeto e o coloque para ser salvo na pasta &lt;code&gt;tests/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Opcional, caso o projeto a ser testado seja .NET 5&lt;/em&gt;: 

&lt;ol&gt;
&lt;li&gt;Clique com o direito no projeto e escolha &lt;code&gt;Properties&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Mude o campo &lt;code&gt;Target framework&lt;/code&gt; para &lt;code&gt;.NET 5.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Salve as alterações e recarregue o projeto&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Com o projeto criado abra o &lt;code&gt;NuGet Manager&lt;/code&gt; para o projeto de testes e adicione os seguintes pacotes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;AutoFixture&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AutoFixture.AutoNSubstitute&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AutoFixture.Xunit2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FluentAssertions&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft.EntityFrameworkCore.InMemory&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NSubstitute&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lembre-se de adicionar como referência ao projeto de testes o projeto que será testado.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configurando nossas Ferramentas
&lt;/h3&gt;

&lt;p&gt;Com tudo instalado podemos passar para a parte de preparação. De certa maneira este passo é opcional mas se quisermos escrever nossos testes da maneira mais prática possível este passo será o grande diferencial de produtividade.&lt;/p&gt;

&lt;p&gt;O &lt;code&gt;AutoFixture.Xunit2&lt;/code&gt; nos trás um atributo &lt;code&gt;[AutoData]&lt;/code&gt; que pode ser utilizado para que os parâmetros de um teste sejam criados automaticamente pelo &lt;code&gt;AutoFixture&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A parte mais legal é que &lt;strong&gt;herdar este atributo e customizar como o AutoFixture cria as coisas&lt;/strong&gt;. Isso significa que podemos colocar na "pipeline" do AutoFixture coisas como o &lt;code&gt;EntityFrameworkCore.InMemory&lt;/code&gt;, &lt;code&gt;NSubstitute&lt;/code&gt; e customizar como alguns atributos nossos seriam criados.&lt;/p&gt;

&lt;p&gt;Para fazer isso tudo que precisamos é criar uma nova classe &lt;code&gt;CreateDataAttribute&lt;/code&gt;, herdar a classe &lt;code&gt;AutoDataAttribute&lt;/code&gt; e criar um construtor que chama o &lt;code&gt;base(Func&amp;lt;IFixture&amp;gt; factory)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Minha sugestão é que você crie um método estático que retorna uma &lt;code&gt;IFixture&lt;/code&gt; e nesse método faça toda a configuração!&lt;/p&gt;

&lt;p&gt;Por exemplo, esta é a minha versão deste atributo para o Cell CMS:&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;using&lt;/span&gt; &lt;span class="nn"&gt;System&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;AutoFixture&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;AutoFixture.AutoNSubstitute&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;AutoFixture.Xunit2&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;AutoMapper&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;CellCms.Api&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;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;CellCms.Tests.Unit.Utils&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;/// Atributo para configurar automaticamente os dados de um test case.&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;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;CreateDataAttribute&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AutoDataAttribute&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;CreateDataAttribute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SetupCellCmsFixture&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;

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

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Configura uma fixture com todos os objetos necessários para&lt;/span&gt;
        &lt;span class="c1"&gt;/// testar o CellCMS.&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IFixture&lt;/span&gt; &lt;span class="nf"&gt;SetupCellCmsFixture&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;fix&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;Fixture&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;fix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Customize&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;AutoNSubstituteCustomization&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="nf"&gt;SetupRecursionBehaviors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fix&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;SetupCellContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fix&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;SetupAutoMapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fix&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;fix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Configura e Injeta uma instância do AutoMapper.&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="fix"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;private&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;SetupAutoMapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Fixture&lt;/span&gt; &lt;span class="n"&gt;fix&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;autoMapperConfig&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;MapperConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&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;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMaps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Startup&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="n"&gt;fix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;autoMapperConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;fix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;autoMapperConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateMapper&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Configura e Injeta uma instância do CellContext.&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="fix"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;private&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;SetupCellContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Fixture&lt;/span&gt; &lt;span class="n"&gt;fix&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;dbOptions&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;DbContextOptionsBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseInMemoryDatabase&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="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="n"&gt;fix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Inject&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;CellContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbOptions&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="c1"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// Configura o comportamento da Fixture durante&lt;/span&gt;
        &lt;span class="c1"&gt;/// chamadas recursivas.&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;param name="fix"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;private&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;SetupRecursionBehaviors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Fixture&lt;/span&gt; &lt;span class="n"&gt;fix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Behaviors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&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;ThrowingRecursionBehavior&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="n"&gt;fix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Behaviors&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="nf"&gt;OmitOnRecursionBehavior&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;É bastante verboso na primeira olhada mas lembre-se que &lt;strong&gt;usando esse atributo você terá muito mais produtividade para escrever seus testes!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Escrevendo nossos Testes
&lt;/h3&gt;

&lt;p&gt;Finalmente podemos começar a escrever nossos testes!&lt;/p&gt;

&lt;p&gt;Tudo que você precisa fazer agora é &lt;strong&gt;criar uma nova classe&lt;/strong&gt;, &lt;strong&gt;criar um método para o seu teste&lt;/strong&gt; e &lt;strong&gt;adicionar os atributos&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Por exemplo um dos testes do Cell CMS:&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;Theory&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;        &lt;span class="c1"&gt;// Indica ao xUnit que este teste tem parâmetros&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CreateData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;    &lt;span class="c1"&gt;// Indica que os parâmetros serão criados através do atributo que criamos na seção anterior&lt;/span&gt;
&lt;span class="c1"&gt;// O [Frozen] indica que o AutoFixture deve retornar sempre esta mesma instância para todos que precisarem dentro deste método! É como se fosse um Singleton&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;Handle_ExistingContext_ReturnsList&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;Frozen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;CellContext&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;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Feed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ListAllFeedsHandler&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Note que deixamos que o próprio "objeto a ser testado" seja criado pelo AutoFixture, dessa maneira o mesmo context que ele passou aqui para este método será passado para o subject!&lt;/span&gt;

    &lt;span class="c1"&gt;// Arrange&lt;/span&gt;
    &lt;span class="c1"&gt;// Aqui estou garantindo que os dados criados pelo AutoFixture estão salvos no Context do EntityFramework&lt;/span&gt;
    &lt;span class="n"&gt;context&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;feeds&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;context&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="c1"&gt;// Act&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;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Handle&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;ListAllFeeds&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Assert&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;Should&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NotBeNull&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;And&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HaveSameCount&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;Feeds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Executando os Testes
&lt;/h3&gt;

&lt;p&gt;Para executar os testes por linha de comando use: &lt;code&gt;dotnet test&lt;/code&gt; na pasta da solução.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AN2Qz1FD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/63efv2e7m5gtivl3pezj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AN2Qz1FD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/63efv2e7m5gtivl3pezj.gif" alt="Executando testes pelo terminal" width="880" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para executar pelo Visual Studio o principal será a janela &lt;code&gt;Test Explorer&lt;/code&gt; (atalho: &lt;code&gt;Ctrl+E, T&lt;/code&gt;). Porém temos alguns outros atalhos importantes e práticos:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Ctrl+R, A&lt;/code&gt;: Executar todos os testes;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl+R, Ctrl+A&lt;/code&gt;: Executar todos os testes com Debug;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl+R, T&lt;/code&gt;: Executar o teste selecionado (no caso o teste em que seu cursor estiver);&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl+R, Ctrl+T&lt;/code&gt;: Executar o teste selecionado com Debug;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Iv7slew9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/z0oh9r2aoskbwwnt2pbg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Iv7slew9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/z0oh9r2aoskbwwnt2pbg.png" alt="Test Explorer do Visual Studio" width="300" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;O post de hoje vai ficando por aqui! Espero que após ler esse post fique evidente &lt;strong&gt;que podemos escrever testes unitários de maneira rápida e prática&lt;/strong&gt; graças a diversas bibliotecas &lt;em&gt;Open Source&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;Alguns assuntos que ainda quero abordar, para os próximos posts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Analyzers&lt;/li&gt;
&lt;li&gt;Refactoring&lt;/li&gt;
&lt;li&gt;Domain Driven Design&lt;/li&gt;
&lt;li&gt;Clean Architecture&lt;/li&gt;
&lt;li&gt;Configuração de um ambiente de desenvolvimento&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fiquem ligados para o próximo post, obrigado por lerem e até a próxima!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>testing</category>
      <category>tutorial</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Meet Dixture! A Test Data Builder module for Deno</title>
      <dc:creator>Rodolpho Alves</dc:creator>
      <pubDate>Sat, 12 Sep 2020 03:17:38 +0000</pubDate>
      <link>https://dev.to/ardc_overflow/meet-dixture-a-test-data-builder-module-for-deno-n94</link>
      <guid>https://dev.to/ardc_overflow/meet-dixture-a-test-data-builder-module-for-deno-n94</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR:
&lt;/h2&gt;

&lt;p&gt;While working with Deno have you ever wished to bother less with coming up with random data for your tests?&lt;/p&gt;

&lt;p&gt;If so then &lt;em&gt;Dixture&lt;/em&gt; is the module for you!&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rodolphocastro"&gt;
        rodolphocastro
      &lt;/a&gt; / &lt;a href="https://github.com/rodolphocastro/dixture"&gt;
        dixture
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Dixture is a Deno module that helps you create random data for your tests.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Dixture&lt;/h1&gt;
&lt;p&gt;Dixture helps you create random data for your tests, with zero external dependencies!&lt;/p&gt;
&lt;h2&gt;
🚥 Current Status&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Master status&lt;/strong&gt; &lt;a rel="noopener noreferrer" href="https://github.com/rodolphocastro/dixture/workflows/Build%20and%20Test/badge.svg?branch=master"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ugYsvaNB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/rodolphocastro/dixture/workflows/Build%2520and%2520Test/badge.svg%3Fbranch%3Dmaster" alt="Build and Test"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Project status&lt;/strong&gt;: &lt;em&gt;Under development&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Latest stable version&lt;/strong&gt;: &lt;em&gt;v0.2.2&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
🏆 Acknowledgements&lt;/h2&gt;
&lt;p&gt;Dixture is &lt;em&gt;loosely&lt;/em&gt; based on the great &lt;a href="https://github.com/AutoFixture/AutoFixture"&gt;AutoFixture&lt;/a&gt; library that exists on the &lt;code&gt;.NET&lt;/code&gt; environment.&lt;/p&gt;
&lt;p&gt;I've used this library for so long for my Unit and Integration tests that I needed to, somehow, get some of its functionality when working on my &lt;code&gt;Deno&lt;/code&gt;'s tests.&lt;/p&gt;
&lt;p&gt;Thus I began working on this project.&lt;/p&gt;
&lt;h2&gt;
⚡ Getting Started&lt;/h2&gt;
&lt;p&gt;Simply import us by &lt;code&gt;deno.land/x/&lt;/code&gt; and use your favorite flavor of test data generation!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;As of v0.2.2&lt;/em&gt; you can pick from two "flavors":&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Functions that create and/or assign fields for your classes and instances&lt;/li&gt;
&lt;li&gt;A Factory API that allows you to write down rules for your classes&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-ts notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-s1"&gt;dixtureFns&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-smi"&gt;RuleSet&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-smi"&gt;DixtureFactory&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-smi"&gt;InterfaceRuleSet&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"https://deno.land/x/dixture@v0.2.2/mod.ts"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;class&lt;/span&gt; &lt;span class="pl-smi"&gt;Person&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/rodolphocastro/dixture"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://heymi.fr/"&gt;Cover is a Deno Artwork made by Heymi&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Background and Motivation
&lt;/h2&gt;

&lt;p&gt;Some background on myself: My fulltime job, right now, is as a fullstack engineer for c#. That means that I get to enjoy all of its perks and all the awesome NuGet packages that the .NET community created. &lt;/p&gt;

&lt;p&gt;Some of these packages are soo helpful that they're must-haves for my daily workflow. These NuGets help me be productive while writing my tests and, to be honest, actually make me enjoy writing tests! To name a few:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AutoFixture&lt;/code&gt; and either &lt;code&gt;AutoFixture.nUnit&lt;/code&gt; or &lt;code&gt;AutoFixture.xUnit&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FluentAssertions&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Moq&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So when I'm working with &lt;code&gt;Deno&lt;/code&gt; on my free time I must admit... I miss those libraries 😅.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wello Horld, this is Dixture
&lt;/h2&gt;

&lt;p&gt;So, out of my frustration due to a lack of &lt;em&gt;Test Data Generators&lt;/em&gt; for Deno I began to work on something, now called &lt;code&gt;Dixture&lt;/code&gt;. I had to, somehow, make my workflow faster and since no one was writing one... might as well give it a shot, right?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Dixture&lt;/code&gt; is, basically, a &lt;strong&gt;module that simplifies your Test Data Generation&lt;/strong&gt;. It has zero external dependencies and allows you some room for customization.&lt;/p&gt;

&lt;p&gt;My goal is to keep Dixture as less intrusive as possible. I don't think it's fair to ask anyone to clutter their code with &lt;code&gt;ClassAttributes&lt;/code&gt; or bother with &lt;code&gt;metadata generation&lt;/code&gt; just for some data generation. However, given the not-on-par-with-csharp support for Reflection on Typescript, we still need some input from you, the developer, in order to give us a "blueprint" (called &lt;code&gt;RuleSet&lt;/code&gt; inside &lt;code&gt;Dixture&lt;/code&gt;) of how to build your class.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enough talk, show me the code
&lt;/h3&gt;

&lt;p&gt;You can check out all our samples at &lt;a href="https://github.com/rodolphocastro/dixture"&gt;the GitHub repository&lt;/a&gt;, they're available &lt;a href="https://github.com/rodolphocastro/dixture/tree/master/samples"&gt;as standalone files&lt;/a&gt; and on our &lt;a href="https://github.com/rodolphocastro/dixture/tree/master/tests"&gt;test cases&lt;/a&gt; but here's a small sample on our latest &lt;code&gt;Factory API&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;dixtureFns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;RuleSet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DixtureFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://deno.land/x/dixture@v0.2.0/mod.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;bankBalance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bigint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;isAlive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Creating our factory&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;DixtureFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="c1"&gt;// 2. Writing in-line Rule Sets (blueprints) for our classes&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;RuleSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 3. For each field we pick a resolution function&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dixtureFns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;String&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="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dixtureFns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// 4. We can even define our own resolution functions, as far as they return the expected type&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bankBalance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dixtureFns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bool&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="mi"&gt;10000000&lt;/span&gt;&lt;span class="nx"&gt;n&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="mi"&gt;0&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// 5. We can also omit rules, the field might not be important after all&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What about x/y/z Feature!?
&lt;/h3&gt;

&lt;p&gt;Well, currently, the roadmap is to try and allow generation for &lt;code&gt;interfaces&lt;/code&gt; (so no need to wrap those up into classes anymore 😅), allow a &lt;code&gt;Fluent/Builder&lt;/code&gt; api for &lt;code&gt;RuleSets / Blueprints&lt;/code&gt; and further enhance how data is generated. &lt;/p&gt;

&lt;p&gt;However all suggestions are welcome! Feel free to drop by our GitHub repo and create issues, fork us and send us your pull request!&lt;/p&gt;

&lt;h3&gt;
  
  
  Acknowledgements
&lt;/h3&gt;

&lt;p&gt;Kudos to the great community behind &lt;a href="https://github.com/AutoFixture/AutoFixture"&gt;AutoFixture&lt;/a&gt; for getting me into Testing and to &lt;a href="https://github.com/drashland/rhum"&gt;Rhum&lt;/a&gt; for creating an improved test "framework" for Deno (Rhum is used for testing Dixture!)&lt;/p&gt;

&lt;p&gt;Thanks for reading this far and stay safe!&lt;/p&gt;

</description>
      <category>deno</category>
      <category>typescript</category>
      <category>tdd</category>
      <category>testing</category>
    </item>
    <item>
      <title>Setting up Svelte and Tailwind... with minimal extra dependencies</title>
      <dc:creator>Rodolpho Alves</dc:creator>
      <pubDate>Wed, 19 Aug 2020 22:47:52 +0000</pubDate>
      <link>https://dev.to/ardc_overflow/setting-up-svelte-and-tailwind-with-minimal-extra-dependencies-1g5a</link>
      <guid>https://dev.to/ardc_overflow/setting-up-svelte-and-tailwind-with-minimal-extra-dependencies-1g5a</guid>
      <description>&lt;p&gt;In this post I'll show how to properly setup &lt;em&gt;TailwindCss&lt;/em&gt; within a &lt;em&gt;Svelte&lt;/em&gt; application. I'll show all the required dependencies and changes that should be made while maintaining a minimal number of additional dependencies and script changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;In a hurry? Then just checkout &lt;a href="https://github.com/rodolphocastro/editorconfig-io/commit/bb63ebb36a6ce6e4f757e821afff4aae5dfd8d06" rel="noopener noreferrer"&gt;this commit&lt;/a&gt; over at GitHub!&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rodolphocastro" rel="noopener noreferrer"&gt;
        rodolphocastro
      &lt;/a&gt; / &lt;a href="https://github.com/rodolphocastro/editorconfig-io" rel="noopener noreferrer"&gt;
        editorconfig-io
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Quickly find editorconfigs for your project
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The basics are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add &lt;code&gt;tailwindcss&lt;/code&gt; + &lt;code&gt;postcss-load-config&lt;/code&gt; to your dependencies&lt;/li&gt;
&lt;li&gt;Init &lt;em&gt;tailwindcss.config&lt;/em&gt; + &lt;em&gt;postcss.config&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Create a component to host tailwindcss and import that component into the App&lt;/li&gt;
&lt;li&gt;Change &lt;code&gt;rollup&lt;/code&gt;'s pipeline to run &lt;code&gt;postcss&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Change &lt;code&gt;build&lt;/code&gt; command to &lt;strong&gt;set &lt;code&gt;NODE_ENV&lt;/code&gt; to &lt;code&gt;production&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  TailwindCss
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Initial Setup
&lt;/h3&gt;

&lt;p&gt;First and foremost we need to add all the required dependencies to our project. Run &lt;code&gt;npm install tailwindcss postcss-load-config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Those dependencies are needed for setting up tailwind itself and enabling us to &lt;strong&gt;load poscss configuration from a separate file&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After npm finishes download the dependencies run &lt;code&gt;npx tailwindcss init&lt;/code&gt; to create the &lt;code&gt;tailwind.config.js&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Purging unwanted classes
&lt;/h3&gt;

&lt;p&gt;In order for tailwind to know which classes are used we need to point it into the correct files. This is done by editing the &lt;code&gt;tailwind.config.js&lt;/code&gt; file and telling it to take into account &lt;strong&gt;all &lt;code&gt;.svelte&lt;/code&gt; and &lt;code&gt;.html&lt;/code&gt; files we have&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is done by the following snippet:&lt;/p&gt;

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

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
           &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./**/*.svelte&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Look for .svelte files&lt;/span&gt;
           &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./**/*.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;// Look for .html files&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&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="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Setting up PostCSS Config
&lt;/h3&gt;

&lt;p&gt;Now create a new file called &lt;code&gt;postcss.config.js&lt;/code&gt;. This file will &lt;strong&gt;hold all the PostCSS related configuration&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this file you'll point out which plugins the postcss pipeline should use. The following snippet does that for the basic taildwindcss configuration:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tailwindcss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;autoprefixer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Importing Tailwind into the Application
&lt;/h3&gt;

&lt;p&gt;Finally, importing tailwind into our App can be done in many ways, such as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Importing it from a &lt;code&gt;.css&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Importing it from within a &lt;code&gt;styles&lt;/code&gt; tag&lt;/li&gt;
&lt;li&gt;Importing it from the &lt;code&gt;styles&lt;/code&gt; tag of a specific component&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'll do it as a &lt;strong&gt;separate component&lt;/strong&gt; with a &lt;code&gt;global styles&lt;/code&gt; section. The following snippets creates the component that bootstraps tailwindcss:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;

/* Tailwind.svelte */
&lt;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;After the component is created all we need to do is import it into our App:&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Tailwindcss&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Tailwind.svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

// Omitting unrelated lines

&lt;span class="nt"&gt;&amp;lt;Tailwindcss&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;



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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Cleaning up
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;This is optional, I guess you could keep the old styles if desired.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Delete the &lt;code&gt;public/global.css&lt;/code&gt; file and its &lt;code&gt;&amp;lt;link rel="stylesheet"&amp;gt;&lt;/code&gt; tag from &lt;code&gt;index.html&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Enabling Postcss in Svelte's Rollup
&lt;/h2&gt;

&lt;p&gt;Open up &lt;code&gt;rollup.config.js&lt;/code&gt; and find the &lt;em&gt;plugins &amp;gt; svelte&lt;/em&gt; section.&lt;/p&gt;

&lt;p&gt;Here we'll point out that svelte &lt;strong&gt;needs to run some sort of preprocessing&lt;/strong&gt; and we'll tell svelte &lt;strong&gt;which preprocessing should be executed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The following snippet shows this allongside the default configuration:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Omitted for brevity&lt;/span&gt;
        &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="nf"&gt;svelte&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                        &lt;span class="c1"&gt;// This tells svelte to run some preprocessing&lt;/span&gt;
                        &lt;span class="na"&gt;preprocess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;sveltePreprocess&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                                &lt;span class="na"&gt;postcss&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// And tells it to specifically run postcss!&lt;/span&gt;
                        &lt;span class="p"&gt;}),&lt;/span&gt;

                        &lt;span class="c1"&gt;// Omitted for brevity&lt;/span&gt;
                &lt;span class="p"&gt;}),&lt;/span&gt;

                &lt;span class="c1"&gt;// Omitted for brevity&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;clearScreen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Ensuring NPM builds for Production
&lt;/h2&gt;

&lt;p&gt;If you run &lt;code&gt;npm run build&lt;/code&gt; you'll see that the generated &lt;code&gt;bundle.css&lt;/code&gt; is quite heavy. That's because, by default, &lt;strong&gt;the Svelte's build command doesn't set up NODE_ENV to production!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In order to fix this all we need to do is use the &lt;code&gt;--environment&lt;/code&gt; options built into &lt;code&gt;rollup.js&lt;/code&gt; to set the &lt;code&gt;NODE_ENV&lt;/code&gt; variable to &lt;code&gt;production&lt;/code&gt; whenever we run a build script.&lt;/p&gt;

&lt;p&gt;To do this simply change the following line within your &lt;code&gt;packages.json&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-svelte-app"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rollup -c --environment NODE_ENV:production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;    
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;This will ensure every time &lt;code&gt;npm run build&lt;/code&gt; is called it locally sets the &lt;code&gt;NODE_ENV&lt;/code&gt; to production. This means &lt;strong&gt;it doesn't impact&lt;/strong&gt; your &lt;strong&gt;hot reload experience&lt;/strong&gt; at all!&lt;/p&gt;

&lt;h2&gt;
  
  
  Possible Impacts
&lt;/h2&gt;

&lt;p&gt;Unlike the other methods I've seen on the internet this one doesn't seem to have any side effects on your build (for production) and hot reload experiences! 😀&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>svelte</category>
      <category>rollup</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Monitorando uma aplicação VueJS com o Application Insights</title>
      <dc:creator>Rodolpho Alves</dc:creator>
      <pubDate>Sun, 12 Jul 2020 14:20:14 +0000</pubDate>
      <link>https://dev.to/ardc_overflow/monitorando-uma-aplicacao-vuejs-com-o-application-insights-9h1</link>
      <guid>https://dev.to/ardc_overflow/monitorando-uma-aplicacao-vuejs-com-o-application-insights-9h1</guid>
      <description>&lt;h2&gt;
  
  
  🤔 Motivação
&lt;/h2&gt;

&lt;p&gt;Boa tarde pessoal! Tudo bem? Para quem já leu meus posts anteriores este post é uma espécie de continuação do &lt;a href="https://dev.to/ardc_overflow/publicando-no-github-io-com-vue-js-498"&gt;Publicando no GitHub.io com VueJS&lt;/a&gt;, onde mostrei como era possível utilizar um pouco de scripts do &lt;code&gt;Powershell/Bash&lt;/code&gt; para realizar deploy de um App VueJS no seu domínio pessoal do GitHub!&lt;/p&gt;

&lt;p&gt;Depois que publiquei o site, bem no começo de toda essa pandemia do Covid-19, fiquei um bom tempo com ele em um estado "estável", onde não via necessidade de mudar nada. Porém, um belo dia, visitei o site e vi que a api que ele consumia do Github estava fora do ar (foi algo pontual, depois voltou) e me dei conta como eu deveria ter criado uma rotina de falha e alguma maneira de ser notificado sobre isso.&lt;/p&gt;

&lt;p&gt;Eis que chegamos ao post atual! Onde mostrarei como rapidamente adicionar o Microsoft Application Insights ao seu projeto Vue!&lt;/p&gt;

&lt;p&gt;Todo o código apresentado aqui também está disponível no meu repositório do Github:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rodolphocastro"&gt;
        rodolphocastro
      &lt;/a&gt; / &lt;a href="https://github.com/rodolphocastro/rodolphocastro.github.io"&gt;
        rodolphocastro.github.io
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Repository for my personal webpage.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  👍 Créditos
&lt;/h2&gt;

&lt;p&gt;Cover disponibilizada por &lt;a href="https://unsplash.com/@markusspiske?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Markus Spiske&lt;/a&gt; através do &lt;a href="https://unsplash.com/s/photos/logging-code?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  👉 TL;DR
&lt;/h2&gt;

&lt;p&gt;Os passos para utilizar o AppInsights c/ Vue são:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Crie um ApplicationInsights no &lt;a href="https://portal.azure.com"&gt;Azure&lt;/a&gt; e salve a &lt;strong&gt;instrumentation key&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Adicione o package &lt;code&gt;@microsoft/applicationinsights-web&lt;/code&gt; utilizando &lt;code&gt;npm&lt;/code&gt; ou &lt;code&gt;yarn&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Crie um arquivo &lt;code&gt;.env&lt;/code&gt; para armazenar sua &lt;strong&gt;instrumentation key&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Crie uma instância utilizando &lt;code&gt;new ApplicationInsights({...})&lt;/code&gt;, substituindo &lt;code&gt;...&lt;/code&gt; pelas configurações&lt;/li&gt;
&lt;li&gt;Inicialize esta instância junto de seu aplicativo&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  ☁ Criando o ApplicationInsights no Azure
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Por brevidade estou assumindo que você já tenha uma conta no Azure! Caso não tenha o processo de criar uma é bem rápido e pode ser feito através do mesmo Portal!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Acesse o &lt;a href="https://portal.azure.com"&gt;Portal do Azure&lt;/a&gt; e pesquise na barra superior por &lt;strong&gt;Application Insights&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--apD9cPFF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/jnky9dtqvwkx9h6dzzbh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--apD9cPFF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/jnky9dtqvwkx9h6dzzbh.png" alt="Alt Text" width="698" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clique em &lt;strong&gt;Add/Adicionar&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1MHw7RTe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bc6llusw5awzvp4iij0h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1MHw7RTe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bc6llusw5awzvp4iij0h.png" alt="Alt Text" width="406" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Escolha um &lt;strong&gt;Grupo de Recursos&lt;/strong&gt; e um &lt;strong&gt;Nome&lt;/strong&gt; para sua instância do Application Insights e clique em &lt;strong&gt;Create&lt;/strong&gt; no fim da página:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MBQr9gef--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/a9m028afxz7pi3h4urn4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MBQr9gef--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/a9m028afxz7pi3h4urn4.png" alt="Alt Text" width="850" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finalmente, após sua instância ser criada, anote a sua &lt;strong&gt;Instrumentation Key&lt;/strong&gt;.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3CHTcnkz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tz1tmwtkgclv6b75289p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3CHTcnkz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tz1tmwtkgclv6b75289p.png" alt="Alt Text" width="880" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Caso queira saber um pouco mais sobre o que podemos fazer dentro do Application Insights do Azure dê uma olhada neste post abaixo&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/ardc_overflow" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--18pYUWV_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--Jpqa4yws--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/414829/10bcad0e-2fc9-44f6-919a-40a1309d637a.jpg" alt="ardc_overflow"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/ardc_overflow/cell-cms-criando-logs-robustos-e-monitorando-uma-api-16ed" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Cell CMS — Criando logs robustos e monitorando uma API&lt;/h2&gt;
      &lt;h3&gt;Rodolpho Alves ・ Jun 24 '20 ・ 9 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#azure&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#applicationinsights&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#logging&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#csharp&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  ⚙ Instalando e Utilizando no Vue
&lt;/h2&gt;

&lt;p&gt;Para instalar o &lt;em&gt;Application Insights&lt;/em&gt; basta adicionar o package &lt;code&gt;@microsoft/applicationinsights-web&lt;/code&gt; utilizando seu &lt;em&gt;package manager&lt;/em&gt; de preferência.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @microsoft/applicationinsights-web
yarn install @microsoft/applicationinsights-web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔑 Armazenando a Instrumentation Key
&lt;/h3&gt;

&lt;p&gt;Lembra a Instrumentation Key que salvamos ao criar o App Insights no Azure? Precisamos dela para inicializar nosso AppInsights no Vue!&lt;/p&gt;

&lt;p&gt;Como boa prática é sempre bom manter as configurações o mais distante possível do código puro, &lt;em&gt;magic strings&lt;/em&gt; e &lt;strong&gt;dependências hardcoded podem dar uma baita dor de cabeça&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Felizmente se você estiver utilizando o &lt;code&gt;@vue/cli&lt;/code&gt; (que, alias, recomendo!) isso pode ser facilmente resolvido através de variáveis de ambientes!&lt;/p&gt;

&lt;p&gt;No Vue, ao contrário do dotNet, as variáveis de ambiente serão relevantes apenas no momento de compilação da aplicação! Para passarmos estas variáveis tudo que precisamos é um arquivo &lt;code&gt;.env&lt;/code&gt; na raiz de nosso projeto! Dentro deste arquivo todas as variáveis que começarem com &lt;code&gt;VUE_APP_&lt;/code&gt; ficarão disponíveis à nossa Aplicação!&lt;/p&gt;

&lt;p&gt;Por exemplo o seguinte arquivo &lt;code&gt;.env&lt;/code&gt; pode ser utilizado para armazenar nossa instrumentation key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VUE_APP_INSIGHTS_KEY=Minha-Instrumentation-Key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E, pelo código, podemos recuperar o valor desta variável através do &lt;code&gt;process.env&lt;/code&gt;, como veremos abaixo!&lt;/p&gt;

&lt;p&gt;Caso queira saber mais sobre as variaveis de ambiente com o &lt;code&gt;@vue/cli&lt;/code&gt; dê uma olhada &lt;a href="https://cli.vuejs.org/guide/mode-and-env.html#environment-variables"&gt;na documentação&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🏆 Criando e Utilizando o AppInsights
&lt;/h3&gt;

&lt;p&gt;Agora que temos nossa &lt;strong&gt;instrumentation key&lt;/strong&gt; disponível ao App precisamos criar e configurar a instância do Application Insights.&lt;/p&gt;

&lt;p&gt;Uma prática que gosto de fazer nestes cenários é criar um &lt;strong&gt;arquivo separado&lt;/strong&gt; que, internamente, &lt;strong&gt;cria e configura a instância&lt;/strong&gt; e apenas publica &lt;strong&gt;uma função para recuperar esta instância&lt;/strong&gt;, pronta para uso.&lt;/p&gt;

&lt;p&gt;Criar a instância é simples, a library nos disponibiliza um construtor que só precisamos chamar e passar os argumentos, conforme:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appInsights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ApplicationInsights&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;instrumentationKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VUE_APP_INSIGHTS_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// A instrumentation key&lt;/span&gt;
    &lt;span class="na"&gt;autoTrackPageVisitTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Flag para ativar rastreio automático de tempo por página&lt;/span&gt;
    &lt;span class="na"&gt;enableAutoRouteTracking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;// Flag para ativar o rastreio automático de rotas&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 única variável obrigatória é a &lt;code&gt;instrumentationKey&lt;/code&gt;. As outras são detalhes que (pelo menos para um SPA) são desejáveis. A referência completa destas variáveis pode ser encontrada na &lt;a href="https://github.com/microsoft/ApplicationInsights-JS#configuration"&gt;documentação&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Após criar a instância é necessário chamar o método &lt;code&gt;.loadAppInsights()&lt;/code&gt; para carrega-la.&lt;/p&gt;

&lt;p&gt;Finalmente, para disponibilizar a instância criada ao resto do Aplicativo, só precisamos &lt;strong&gt;exportar&lt;/strong&gt; uma função que a retorna, conforme:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApplicationInsights&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@microsoft/applicationinsights-web&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appInsights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ApplicationInsights&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;instrumentationKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VUE_APP_INSIGHTS_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;autoTrackPageVisitTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;enableAutoRouteTracking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;appInsights&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadAppInsights&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Obtem a instância do AppInsights.
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useAppInsights&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="nx"&gt;appInsights&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora precisamos garantir que ela seja carregada assim que o usuário abrir o App. Uma boa maneira de fazer isso é adiciona-la ao &lt;code&gt;main.ts/js&lt;/code&gt; e já disparar um track sinalizando que o App Insights foi carregado!&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="c1"&gt;// Configurações do Vue...&lt;/span&gt;

&lt;span class="c1"&gt;// Criando um evento só para garantir que carregamos tudo&lt;/span&gt;
&lt;span class="nx"&gt;useAppInsights&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;trackEvent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;App Loaded!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Criando o app em si&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;$mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🙋‍♂️ Possíveis melhorias
&lt;/h2&gt;

&lt;p&gt;Uma possível melhoria para todo esse processo seria criar um &lt;code&gt;Mixin&lt;/code&gt; para que todo &lt;code&gt;component&lt;/code&gt; pudesse acessar o AppInsights, porém pelo tamanho do projeto acreditei que seria um &lt;em&gt;overengineering&lt;/em&gt; haha.&lt;/p&gt;

&lt;p&gt;Outra melhoria seria utilizar as capacidades de &lt;strong&gt;Analytics&lt;/strong&gt; do Application Insights. Este exemplo demonstra apenas o básico para que tenhamos algum tipo de monitoramento de falhas e acessos.&lt;/p&gt;

&lt;p&gt;Além das melhorias na aplicação em si poderiamos configurar &lt;strong&gt;Alertas automáticos&lt;/strong&gt; no Application Insights para quando falhas fossem detectadas!&lt;/p&gt;

</description>
      <category>vue</category>
      <category>azure</category>
      <category>logging</category>
      <category>applicationinsights</category>
    </item>
    <item>
      <title>Reescrevendo a StarWars API em Deno</title>
      <dc:creator>Rodolpho Alves</dc:creator>
      <pubDate>Tue, 23 Jun 2020 11:23:28 +0000</pubDate>
      <link>https://dev.to/ardc_overflow/reescrevendo-a-starwars-api-em-deno-2k5n</link>
      <guid>https://dev.to/ardc_overflow/reescrevendo-a-starwars-api-em-deno-2k5n</guid>
      <description>&lt;p&gt;&lt;em&gt;Créditos da capa: &lt;a href="https://masbagal.com/"&gt;Dimitrij Agal&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  👉 TL;DR;
&lt;/h2&gt;

&lt;p&gt;Reescrevi a API do &lt;a href="https://swapi.dev"&gt;https://swapi.dev&lt;/a&gt; utilizando Deno e Svelte. Disponível para testes através do &lt;a href="https://swapi-deno.azurewebsites.net/"&gt;https://swapi-deno.azurewebsites.net/&lt;/a&gt; e do &lt;a href="https://hub.docker.com/r/rodolphoalves/swapi-deno?utm_source=docker4win_2.3.1.0&amp;amp;utm_medium=repo_open&amp;amp;utm_campaign=referral"&gt;DockerHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O código da API + Frontend está disponível no &lt;a href="https://github.com/rodolphocastro/deno-swapi"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rodolphocastro"&gt;
        rodolphocastro
      &lt;/a&gt; / &lt;a href="https://github.com/rodolphocastro/deno-swapi"&gt;
        deno-swapi
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A StarWars API written with Deno and powered by Oak and Svelte!
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  💭 Inspiração
&lt;/h2&gt;

&lt;p&gt;Quem nunca se inspirou em um projeto existente para auxiliar no aprendizado de uma nova linguagem de programação ou Tecnologia?&lt;/p&gt;

&lt;p&gt;Eu sou culpado de fazer isso. Direto. Chega a dar vergonha de volta e meia olhar meu GitHub e ver tantos projetos que começo e acabo pausando só &lt;em&gt;pra dar uma olhadinha&lt;/em&gt; em alguma outra tecnologia que me chamou a atenção 😅&lt;/p&gt;

&lt;p&gt;O projeto da vez, &lt;em&gt;inspirado pelas primeiras versões "production ready" do &lt;a href="https://deno.land/"&gt;Deno&lt;/a&gt;&lt;/em&gt;, foi a reescrita da &lt;a href="https://swapi.dev"&gt;Star Wars API&lt;/a&gt;. Faz tempos que utilizo a &lt;strong&gt;Swapi&lt;/strong&gt; para testar Apps (Web e Mobile) que precisam fazer chamadas a APIs REST e sempre me "frustou" um pouco ela não ter sido atualizada com os dados da trilogia mais recente!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nota: Não que a versão v0.2.0 deste projeto esteja com os dados recentes! 😂&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🦕 Deno
&lt;/h2&gt;

&lt;p&gt;Para aqueles que não estão acompanhando o mundo "Node":&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Deno é um runtime simples, moderno e seguro para a execução de Type/Javascript, utilizando a engine V8 e desenvolvido através do RUST&lt;br&gt;
&lt;em&gt;(Fonte: &lt;a href="https://deno.land/"&gt;https://deno.land/&lt;/a&gt;, traduzido pelo autor)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Em suma a ideia do Deno é pegar todo o aprendizado da comunidade com o NodeJS manter o que é bom e refinar o que "precisa" ser refinado.&lt;/p&gt;

&lt;p&gt;Em minha opinião algumas grandes vantagens do Deno são:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Suporte nativo a &lt;strong&gt;Typescript&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Versionamento integrado ao Git (nada de &lt;code&gt;packages.json&lt;/code&gt; ou o inferno do &lt;code&gt;node_modules&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Possui um conjunto "nativo" de bibliotecas suportadas &lt;code&gt;std&lt;/code&gt; (o que me lembra bastante o .NET)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Caso esteja lendo no futuro: Lembre-se que este post foi escrito com base na versão v1.1.1 do Deno! Então algumas coisas ainda eram novas!&lt;/p&gt;

&lt;h3&gt;
  
  
  Escolhendo nossas dependências
&lt;/h3&gt;

&lt;p&gt;Antes de começar a bater código comecei olhando as bibliotecas que já existiam no ecossistema Deno para fazer duas tarefas primordias de uma API REST:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Servir o conteúdo através dos endpoints&lt;/li&gt;
&lt;li&gt;Armazenar, de alguma maneira, o conteúdo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Começando por servir o conteúdo vi que o Deno possui vários "sabores" de frameworks para APIs REST, alguns são:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://deno.land/x/oak"&gt;Oak&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://deno.land/x/drash"&gt;Drash&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://deno.land/x/dactyl"&gt;Dactyl&lt;/a&gt; [&lt;em&gt;obs: Baseado no Oak&lt;/em&gt;]&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No momento em que comecei o projeto o que me pareceu mais tentador foi o &lt;strong&gt;Oak&lt;/strong&gt;, especialmente por ser diferente do padrão &lt;em&gt;dotNet Core&lt;/em&gt; ao qual estou acostumado 😅.&lt;/p&gt;

&lt;p&gt;Em seguida precisava de uma maneira de armazenar o conteúdo de maneira prática. Já existem &lt;strong&gt;várias&lt;/strong&gt; bibliotecas para conectar com bancos SQL e NoSQL, porém para manter o menor &lt;em&gt;footprint&lt;/em&gt; possível para a API pensei em embarcar os dados junto à API. &lt;/p&gt;

&lt;p&gt;Olhei nas bibliotecas &lt;code&gt;std&lt;/code&gt; e econtrei o que precisava para lidar com arquivos: a biblioteca &lt;strong&gt;fs&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sugestões para o ambiente de desenvolvimento
&lt;/h3&gt;

&lt;p&gt;Antes de passarmos ao código, algumas sugestões:&lt;/p&gt;

&lt;p&gt;Para desenvolver a API e o Portal utilizei o &lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt; com as extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno"&gt;Deno&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=JamesBirtles.svelte-vscode"&gt;Svelte&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ardenivanov.svelte-intellisense"&gt;Svelte Intellisense&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lendo os dados de arquivos json
&lt;/h3&gt;

&lt;p&gt;Comecei modelando os dados disponíveis na &lt;a href="https://swapi.dev"&gt;API Original&lt;/a&gt; e os transcrevendo para seis interfaces diferentes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Film&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Person&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Planet&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Specie&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Starship&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Vehicle&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Utilizando o &lt;a href="https://insomnia.rest/"&gt;Insomnia&lt;/a&gt; acessei os endpoints da API Original e extrai os dados disponíveis publicamente, higienizando alguns dados (como os "ids") e removendo o envelope.&lt;/p&gt;

&lt;p&gt;Desta maneira construi alguns arquivos &lt;code&gt;.json&lt;/code&gt; no seguinte formato, para armazenar os dados junto à API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com este formato estabelecido abstrai criei um arquivo para cada um dos &lt;code&gt;models&lt;/code&gt; e escrevi algumas interfaces e classes para consolidar a lógica de carregar os dados a partir de arquivos e disponibilizados em um &lt;code&gt;Array&lt;/code&gt; de seu devido tipo &lt;code&gt;T&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// As of v0.55.0 this module requires the --unstable flag to be used&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://deno.land/std@v0.55.0/fs/mod.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Describes the expected structure for a .json storage.
 */&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;JsonStorable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Loads data and deserializes data from a json file.
 * @param dataDir directory containing the file
 * @param filename filename containing the data, serialized
 */&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;loadDataFromFiles&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;dataDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ensureDir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataDir&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataDir&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;JsonStorable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&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 permitir, eventualmente, a abstração para outra fonte de dados também criei uma estrutura (baseada bem vagamente no Vuex) para armazenar estes dados na aplicação, como um singleton:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;ModelState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;IState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&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="na"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&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;Finalmente, para casar tudo, criei algums &lt;code&gt;factory methods&lt;/code&gt; para gerar meus &lt;code&gt;States&lt;/code&gt; com base nos &lt;code&gt;.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Creates and seeds a ModelState for Films.
 * @param dataDir directory holding the json file, defaults to ./data
 * @param filmFile json file containing films, defaults to films.json
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createFilmStateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;dataDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;filmFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;films.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Film&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;films&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;loadDataFromFiles&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Film&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filmFile&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="nx"&gt;ModelState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Film&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;films&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Creates and seeds a ModelState for Species.
 * @param dataDir directory holding the json file, defaults to ./data
 * @param speciesFile json file containing species, defautls to species.json
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createSpecieStateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;dataDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;speciesFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;species.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Specie&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;species&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;loadDataFromFiles&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Specie&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;speciesFile&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="nx"&gt;ModelState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Specie&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;species&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Creates and seeds a ModelState for Vehicles.
 * @param dataDir directory holding the json file, defaults to ./data
 * @param vehiclesFile json file containing species, defautls to vehicles.json
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createVehicleStateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;dataDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;vehiclesFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vehicles.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Vehicle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vehicles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;loadDataFromFiles&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Vehicle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vehiclesFile&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="nx"&gt;ModelState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Vehicle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vehicles&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Creates and seeds a ModelState for Starships.
 * @param dataDir directory holding the json file, defaults to ./data
 * @param starshipsFile json file containing starships, defaults to startships.json
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createStarshipStateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;dataDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;starshipsFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;starships.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Starship&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;starships&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;loadDataFromFiles&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Starship&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;starshipsFile&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="nx"&gt;ModelState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Starship&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;starships&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Creates and seeds a ModelState for Planets.
 * @param dataDir directory holding the json file, defaults to ./data
 * @param planetsFile json file containing planets, defaults to planets.json
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createPlanetsStateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;dataDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;planetsFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;planets.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Planet&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;planets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;loadDataFromFiles&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Planet&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;planetsFile&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="nx"&gt;ModelState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Planet&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;planets&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Creates and Seeds a ModelState for People.
 * @param dataDir directory holding the json file, defaults to ./data
 * @param peopleFile json file containing people, defaults to people.json
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createPeopleStateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;dataDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;peopleFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;people.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;people&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;loadDataFromFiles&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;peopleFile&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="nx"&gt;ModelState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;people&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 tudo isso pronto, vamos aos endpoints!&lt;/p&gt;

&lt;h3&gt;
  
  
  Servindo os dados através de endpoints com o Oak
&lt;/h3&gt;

&lt;p&gt;A premissa do &lt;strong&gt;Oak&lt;/strong&gt; é que temos uma &lt;code&gt;Application&lt;/code&gt; e podemos adicionar diversos &lt;code&gt;Middlewares&lt;/code&gt; a ela. Desta maneira a própria camada do Oak irá gerar, para nós, as chamadas necessárias para fazer o &lt;code&gt;Http-Server&lt;/code&gt; do &lt;code&gt;Deno&lt;/code&gt; trabalhar conforme esperamos!&lt;/p&gt;

&lt;p&gt;Para os fins da API vamos utilizar o &lt;code&gt;RouterMiddleware&lt;/code&gt;. Este middleware nos permite especificar &lt;code&gt;functions&lt;/code&gt; para lidar com os &lt;em&gt;verbos http&lt;/em&gt; em &lt;em&gt;rotas específicas&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Por exemplo, para os endpoints de &lt;strong&gt;listar&lt;/strong&gt; e &lt;strong&gt;obter um específico&lt;/strong&gt; a implementação com o Oak fica assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Criando a Application do Oak&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Assuma que o filmsState já está criado&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filmsRouter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/films&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;filmsRouter&lt;/span&gt;
  &lt;span class="c1"&gt;// Indicando que este router deverá escutar na rota GET /&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;filmsState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="c1"&gt;// E na rota GET /:id&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/:id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;filmsState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Adicionando nossos middlewares à Aplicação&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;...[&lt;/span&gt;
    &lt;span class="nx"&gt;filmsRouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// e, finalmente, rodando nossa aplicação&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resto dos endpoints seguem todos o mesmo padrão, salvo o endpoint que apresenta os arquivos do nosso frontend!&lt;/p&gt;

&lt;h2&gt;
  
  
  🤖 Svelte
&lt;/h2&gt;

&lt;p&gt;Após terminar os 6 endpoints originais comecei a pensar como seria interessante ter um SwaggerUI ou alguma interface documentando a API. Porém, &lt;em&gt;no momento&lt;/em&gt;, &lt;strong&gt;não existem bibliotecas para gerar a OpenApi&lt;/strong&gt; spec a partir dos endpoints implementados.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ou seja, não temos algo como o &lt;a href="https://docs.nestjs.com/recipes/swagger"&gt;Swagger Plugin do NestJS&lt;/a&gt; ou o &lt;a href="https://github.com/domaindrivendev/Swashbuckle.AspNetCore"&gt;Swashbuckle&lt;/a&gt; do .NET Core.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Então, para aproveitar o embalo, decidi sair da zona de conforto de Vue + TS e dar a cara a tapa para testar outro framework Javascript que muitas pessoas estão adotando: &lt;em&gt;Svelte&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A ideia do &lt;em&gt;Svelte&lt;/em&gt; é similar à do React e do Vue: &lt;strong&gt;Um único arquivo (chamado de component) agrega a lógica, o estilo visual e a estrutura para exibição.&lt;/strong&gt;. Com a mesma "pegada" do Vue o Svelte é &lt;strong&gt;reativo por padrão&lt;/strong&gt;. (Com algumas diferenças!)&lt;/p&gt;

&lt;p&gt;Vindo do Vue as principais diferenças que senti ao usar o Svelte foram:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Menos "verboso" para declarar componentes&lt;/li&gt;
&lt;li&gt;Não depender de um CLI para um kickstart da configuração&lt;/li&gt;
&lt;li&gt;Utilização de 'blocos' para condicionais e loops, ao invés de atributos no HTML&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No geral eu gostei, bastante, da breve experiência com o Svelte e pretendo utiliza-lo mais no futuro. Admito que fiquei tentado a ir atrás de como utilizar Typescript junto ao Svelte mas como a ideia era implementar logo, deixei de lado!&lt;/p&gt;

&lt;h3&gt;
  
  
  Components do Svelte
&lt;/h3&gt;

&lt;p&gt;A sintaxe de components do Svelte é a seguinte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
// Código javascript
&amp;lt;/script&amp;gt;

&amp;lt;style&amp;gt;
# CSS do component
&amp;lt;/style&amp;gt;

&amp;lt;!-- Corpo HTML --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O principal component do Frontend é o "Browse" genérico. A ideia aqui é que como a estrutura é sempre a mesma (afinal, não estou fazendo um Portal, apenas exibindo possíveis retornos!) um único component, configurável, dá conta de exibir o que é necessário!&lt;/p&gt;

&lt;p&gt;Os elementos que notei que repetiam entre cada exibição de dados eram: O endpoint em si, o nome do endpoint, um emoji para exibir no &lt;code&gt;heading&lt;/code&gt; e quais propriedades do elemento deviam ser exibidas na lista interna.&lt;/p&gt;

&lt;p&gt;O component resultante dessa parametrização é:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  export let endpointName = "Generic Endpoint";
  export let endpointEmoji = "❓";
  export let displayProperties = ["url", "name"];
  export let endpoint;

  const endpointPromise = fetchData();

  let showJson = false;
  let showList = false;

  async function fetchData() {
    const response = await fetch(endpoint);
    return response.json();
  }

  function toggleList() {
    showList = !showList;
  }

  function toggleJson() {
    showJson = !showJson;
  }
&amp;lt;/script&amp;gt;

&amp;lt;section container&amp;gt;
  &amp;lt;h3 id="{endpointName}"&amp;gt;{endpointEmoji} {endpointName}&amp;lt;/h3&amp;gt;
  &amp;lt;p&amp;gt;
    The {endpointName} endpoint is served on the route
    &amp;lt;code&amp;gt;{endpoint}&amp;lt;/code&amp;gt;
  &amp;lt;/p&amp;gt;
  {#await endpointPromise}
    &amp;lt;p&amp;gt;Please wait, loading data...&amp;lt;/p&amp;gt;
  {:then dataResult}
    &amp;lt;p&amp;gt;
      There are {dataResult.length} objects of type {endpointName} on the API
    &amp;lt;/p&amp;gt;
    &amp;lt;hr /&amp;gt;
    &amp;lt;button on:click={toggleJson}&amp;gt;
      {showJson ? 'Hide Json' : 'Show Json'}
    &amp;lt;/button&amp;gt;
    &amp;lt;button on:click={toggleList}&amp;gt;
      {showList ? 'Hide list' : 'Show list'}
    &amp;lt;/button&amp;gt;
    {#if showJson}
      &amp;lt;h4&amp;gt;JSON&amp;lt;/h4&amp;gt;
      &amp;lt;p&amp;gt;A {endpointName}'s JSON looks like this:&amp;lt;/p&amp;gt;
      &amp;lt;pre&amp;gt;
        &amp;lt;code&amp;gt;{JSON.stringify(dataResult[0], null, '\t')}&amp;lt;/code&amp;gt;
      &amp;lt;/pre&amp;gt;
    {/if}
    {#if showList}
      &amp;lt;h4&amp;gt;Result from the API&amp;lt;/h4&amp;gt;
      &amp;lt;ul&amp;gt;
        {#each dataResult as data}
          &amp;lt;li&amp;gt;{data[displayProperties[0]]}: {data[displayProperties[1]]}&amp;lt;/li&amp;gt;
        {/each}
      &amp;lt;/ul&amp;gt;
    {/if}
  {:catch _}
    &amp;lt;p&amp;gt;
      Ops, something went wrong while fetching data! Please refresh the page
    &amp;lt;/p&amp;gt;
  {/await}
&amp;lt;/section&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;A cara final do portal ficou assim:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mmJZdrQr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/c0p23kgln98766xx03va.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mmJZdrQr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/c0p23kgln98766xx03va.png" alt="Alt Text" width="880" height="764"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Servindo nossa SPA através do Oak
&lt;/h3&gt;

&lt;p&gt;A última alteração necessária foi adicionar um novo &lt;code&gt;Middleware&lt;/code&gt; ao Oak, apontando ao servidor que os arquivos do subdiretório &lt;code&gt;./portal/public&lt;/code&gt; deviam ser publicados na rota &lt;code&gt;/&lt;/code&gt; do servidor!&lt;/p&gt;

&lt;p&gt;O código resultante é:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Criando o middleware&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;servePortal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Middleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Deno&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/portal/public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Indicando que ele deve ser utilizando, junto aos outros&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;...[&lt;/span&gt;
    &lt;span class="nx"&gt;filmsRouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;speciesRouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;vehiclesRouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;starshipRouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;planetsRouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;peopleRouter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;servePortal&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;O projeto está longe de finalizado, ainda tenho alguns itens do &lt;em&gt;Roadmap&lt;/em&gt; a ser sanados (como habilitar CORS, atualizar os dados e melhorar a tipagem!) porém estou satisfeito com o resultado atual!&lt;/p&gt;

&lt;p&gt;Meu foco no futuro próximo será criar uma imagem &lt;code&gt;Docker&lt;/code&gt; da aplicação e hospeda-la em algum lugar, espero que de graça 😅&lt;/p&gt;

&lt;p&gt;Quem quiser ver o &lt;strong&gt;código&lt;/strong&gt; completo, o &lt;em&gt;roadmap&lt;/em&gt; e o &lt;em&gt;histórico de alterações&lt;/em&gt; pode encontrar tudo isso no repositório &lt;a href="https://github.com/rodolphocastro/deno-swapi"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Obrigado por lerem este post e até a próxima!&lt;/p&gt;

</description>
      <category>deno</category>
      <category>typescript</category>
      <category>rest</category>
      <category>svelte</category>
    </item>
    <item>
      <title>Cell CMS — Utilizando Docker para criar Containers</title>
      <dc:creator>Rodolpho Alves</dc:creator>
      <pubDate>Sat, 18 Apr 2020 14:01:00 +0000</pubDate>
      <link>https://dev.to/ardc_overflow/cell-cms-utilizando-docker-para-criar-containers-3h4o</link>
      <guid>https://dev.to/ardc_overflow/cell-cms-utilizando-docker-para-criar-containers-3h4o</guid>
      <description>&lt;h3&gt;
  
  
  Cell CMS — Utilizando Docker para criar Containers
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LkC5fCGR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AU0Hph8m2HqsyDWTq" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LkC5fCGR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AU0Hph8m2HqsyDWTq" alt="" width="880" height="586"&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@jonassmith?utm_source=medium&amp;amp;utm_medium=referral"&gt;Jonas Smith&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No &lt;a href="https://dev.to/ardc_overflow/cell-cms-criando-logs-robustos-e-monitorando-uma-api-2ddg-temp-slug-3701851"&gt;último post&lt;/a&gt;falamos sobre &lt;strong&gt;geração de logs&lt;/strong&gt; , utilizando o &lt;strong&gt;Serilog&lt;/strong&gt; e o &lt;strong&gt;ApplicationInsights&lt;/strong&gt; para termos um monitoramento robusto, com métricas de performance e erros! No post de hoje vamos permitir que nossa API seja executada em um &lt;strong&gt;Container&lt;/strong&gt; através do &lt;strong&gt;Docker&lt;/strong&gt;! Além disso vamos dar uma olhada em como o Visual Studio 2019 permite a integração, durante o desenvolvimento, com os containers executados pelo &lt;strong&gt;Docker Desktop.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Os branches para o código de hoje serão 2: feature/dockerfile e feature/docker-compose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O que são Containers e o qual problema eles resolvem&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Mas na minha máquina funciona! — &lt;em&gt;Vários desenvolvedores, circa sempre.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Quem nunca, em algum ponto, não soltou a exclamação acima? Quando estamos desenvolvendo costumamos estar no “melhor ambiente” possível. Todas as dependências estão instaladas (e normalmente atualizadas), temos todas as nossas ferramentas de Debug e temos acesso administrativo ao sistema operacional.&lt;/p&gt;

&lt;p&gt;Eis que chega a hora de publicar a aplicação e… não dá certo. É erro aqui, erro ali, configuração faltante e assim vai. Isso pra não falarmos de problemas de rede, que ficam mais para o lado do pessoal de operações, normalmente.&lt;/p&gt;

&lt;p&gt;Com a chegada de maior poder computacional começamos a utilizar &lt;strong&gt;Máquinas Virtuais&lt;/strong&gt; para executar nossos sistemas de maneira mais isolada. Com máquinas virtuais ainda temos todo o trabalho de manter um sistema operacional e configura-lo, mas temos um isolamento melhor das dependências.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nas clouds/vps&lt;/strong&gt; temos essa opção que normalmente acaba sendo chamada de &lt;strong&gt;IAAS&lt;/strong&gt; (&lt;em&gt;Infrastructure As A Service&lt;/em&gt;), basicamente é assim:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Aqui está uma máquina virtual com X processamento, Y ram e Z storage. A gente garante que ela fica no ar 99.99999% do tempo mas você fica responsável por instalar dependências, configurar e aplicar atualizações.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Porém ainda temos um problema. &lt;strong&gt;Usar uma VM&lt;/strong&gt; , completa, &lt;strong&gt;ainda requer&lt;/strong&gt; muita &lt;strong&gt;manutenção e configuração&lt;/strong&gt; , especialmente se nosso foco for agilidade. Eis que surgem o conceito de &lt;strong&gt;PAAS&lt;/strong&gt; (&lt;em&gt;Platform As A Service&lt;/em&gt;). Basicamente nesse modelo seu fornecedor de cloud/vps se encarrega de atualizar, manter e configurar o &lt;strong&gt;ambiente onde seu sistema será executado.&lt;/strong&gt; Basicamente é assim:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Aqui está um ambiente baseado em Linux, com NGINX instalado e com limites de X Y Z. A gente garante que estará sempre atualizado e ficará no ar 99.99999999% do tempo mas você ficará responsável por copiar sua aplicação e as configurações necessárias”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;O pulo do gato aqui é que agora &lt;strong&gt;essa plataforma não necessariamente é dedicada a você&lt;/strong&gt;. Não entrarei em detalhes mas neste modelo mas, através de configurações do sistema base e da máquina virtual, o fornecedor pode dedicar cotas de performance diferentes a aplicações diferentes e segregar o que está instalado para cada aplicação. Isso permite um custo mais baixo para o fornecedor e para você cliente!&lt;/p&gt;

&lt;p&gt;E então temos, recentemente, a popularização do conceito de &lt;strong&gt;Containers.&lt;/strong&gt; De maneira resumida:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Containers são “pacotes” para distribuição da sua aplicação junto com o ambiente e as dependências necessárias para sua execução. Ao contrário de máquinas virtuais, que simulam o Hardware, os Containers focam em simular o Sistema Operacional.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Em termos práticos: Você cria uma &lt;strong&gt;Imagem&lt;/strong&gt; que irá conter todos os passos e dependências para rodar sua aplicação, como:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Compilar sua aplicação&lt;/li&gt;
&lt;li&gt;Com o resultado da compilação, copiar para uma pasta X (por exemplo onde seu WebServer estará servindo)&lt;/li&gt;
&lt;li&gt;Aplicar as configurações necessárias (seja por variáveis de ambiente dentro da imagem, cópia de arquivos ou linha de comando. Podemos inclusive utilizar &lt;em&gt;apt get&lt;/em&gt; para instalar dependências embarcadas)&lt;/li&gt;
&lt;li&gt;Executar a sua aplicação&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Com a &lt;strong&gt;Imagem&lt;/strong&gt; pronta você finalmente pode executa-la. Nesse ponto temos o &lt;strong&gt;Container da sua aplicação.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Agora vem a questão de um milhão de reais:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Mas Rodolpho, como criamos essas imagens!?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Meus amigos, como quase tudo em TI, temos diversas tecnologias para criarmos Imagens, algumas são:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt; (o queridinho da vez!)&lt;/li&gt;
&lt;li&gt;Rkt&lt;/li&gt;
&lt;li&gt;Containerd&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Então vamos falar, finalmente, sobre Docker!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker, Imagens, Containers e o DockerHub&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cNGzl8z8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/601/0%2AQxoazngPihW25Z8K.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cNGzl8z8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/601/0%2AQxoazngPihW25Z8K.png" alt="" width="601" height="431"&gt;&lt;/a&gt;Moby, o mascote do Docker (fonte: &lt;a href="https://www.docker.com/sites/default/files/d8/2019-07/Moby-logo.png"&gt;&lt;/a&gt;&lt;a href="https://www.docker.com/sites/default/files/d8/2019-07/Moby-logo.png"&gt;https://www.docker.com/sites/default/files/d8/2019-07/Moby-logo.png&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Antes de partirmos para os detalhes técnicos, vamos conhecer um pouco sobre a empresa. &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; é a desenvolvedora de duas soluções que facilitam, bastante, a utilização de containers por desenvolvedores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker Desktop&lt;/strong&gt; : Ferramenta para facilitar a instalação e uso de containers em ambiente Windows e Mac OS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DockerHub&lt;/strong&gt; : Repositório central para publicação de imagens (&lt;em&gt;um NuGet ou NPM para imagens Docker&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Existem várias outras contribuições da empresa para a comunidade Open Source e na criação da &lt;a href="https://www.opencontainers.org/"&gt;OCI (Open Container Initiative)&lt;/a&gt;, porém essa parte escapa da ideia do post de focarmos em como utilizar! Recomendo uma boa lida no site da Docker e do OCI, vale a pena!&lt;/p&gt;

&lt;p&gt;Anyway, vamos para a parte prática! Não vou abordar neste post como instalar e configurar o Docker Desktop porém se você precisa deste passo a passo pode conferir um &lt;a href="https://docs.docker.com/docker-for-windows/install/"&gt;tutorial muito bom no própio site da Docker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zSRPfsUe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AA0Apys9Aee_SLShT0rTd4g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zSRPfsUe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AA0Apys9Aee_SLShT0rTd4g.png" alt="" width="880" height="517"&gt;&lt;/a&gt;Partiremos daqui, Docker Desktop instalado e pronto para rodar!&lt;/p&gt;

&lt;p&gt;Vamos abrir um terminal e digitar a sugestão do Docker Desktop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wvibBf4Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AIyIZLpzqLcMyJf2qWRyF7g.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wvibBf4Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AIyIZLpzqLcMyJf2qWRyF7g.gif" alt="" width="880" height="465"&gt;&lt;/a&gt;Executando a imagem do Tutorial&lt;/p&gt;

&lt;p&gt;Após rodar o comando você poderá acessar &lt;a href="http://localhost"&gt;http://localhost&lt;/a&gt; e acompanhar o propio tutorial do pessoal da Docker! Porém, vamos destrinchar aqui o comando que foi executado:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;docker indica que vamos utilizar o docker-cli 😅&lt;/li&gt;
&lt;li&gt;run indica que vamos &lt;strong&gt;executar&lt;/strong&gt; uma imagem&lt;/li&gt;
&lt;li&gt;-dp 80:80 indica duas coisas: &lt;strong&gt;-d&lt;/strong&gt; sinaliza que o daemon execute em background e &lt;strong&gt;-p 80:80&lt;/strong&gt; indica que queremos que a &lt;strong&gt;porta 80 do sistema atual seja mapeada para a porta 80 do container&lt;/strong&gt; (ou seja, quando acessarmos a porta 80 da nossa máquina a chamada será encaminhada para a porta 80 do container)&lt;/li&gt;
&lt;li&gt;docker/getting-started sinaliza a &lt;strong&gt;imagem&lt;/strong&gt; a ser executada. Podemos identificar que será uma imagem com o nome de &lt;strong&gt;getting-started&lt;/strong&gt; e virá do repositório/usuário &lt;strong&gt;docker&lt;/strong&gt;. Note que não descrevemos nenhuma tag (através de :tag no fim da imagem) então a tag &lt;strong&gt;latest&lt;/strong&gt; será utilizada&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Existem outros argumentos importantes e que utilizaremos bastante, eles são:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;--volume ou -v: Permite &lt;strong&gt;mapearmos uma pasta/volume&lt;/strong&gt; da nossa máquina &lt;strong&gt;para dentro do container&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;--env ou -e : Permite que definamos valores para &lt;strong&gt;variáveis de ambiente&lt;/strong&gt; dentro do container&lt;/li&gt;
&lt;li&gt;--network: Permite que defininamos &lt;strong&gt;uma rede&lt;/strong&gt; (virtual) onde &lt;strong&gt;o container será conectado&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A referência completa dos parâmetros pode ser encontrada &lt;a href="https://docs.docker.com/engine/reference/commandline/run/"&gt;aqui&lt;/a&gt;. Agora vamos discutir o local de onde pegamos a imagem: DockerHub.&lt;/p&gt;

&lt;p&gt;O &lt;a href="https://hub.docker.com/"&gt;DockerHub&lt;/a&gt; é o &lt;strong&gt;Registry&lt;/strong&gt; (ou seja um repositório de imagens) oficial da &lt;strong&gt;Docker&lt;/strong&gt;. Existem outros e é possível executar seu próprio registry (inclusive atrás de Containers!), mas o mais famoso é o DockerHub. Nele podemos encontrar imagens para várias aplicações, como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/_/mariadb"&gt;MariaDb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/_/mongo"&gt;MongoDb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/_/rabbitmq"&gt;RabbitMQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/_/microsoft-mssql-server"&gt;SqlServer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/r/docker/getting-started"&gt;E o próprio Getting-Started que baixamos!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Antes de falarmos sobre como as imagens são criadas, vamos abordar mais uma questão:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Como desenvolvedor, qual a vantagem de utilizar Containers ao invés de instalar tudo na minha máquina!?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Do ponto de vista técnico eu diria que a principal vantagem é &lt;strong&gt;poder aproximar ao máximo seu ambiente de desenvolvimento ao ambiente de produção&lt;/strong&gt; mantendo as mesmas versões das dependências. Inclusive, &lt;strong&gt;você pode garantir que todos os desenvolvedores estarão utilizando as mesmas versões de dependências&lt;/strong&gt; para seu projeto!&lt;/p&gt;

&lt;p&gt;Outra vantagem em qualidade de “vida”, especialmente no caso de quem utiliza o Windows, é que &lt;strong&gt;containers podem ser criados e deletados sob demanda e facilmente&lt;/strong&gt;. Assim &lt;strong&gt;quando você estiver trabalhando em outro projeto&lt;/strong&gt; basta parar ou remover os containers e &lt;strong&gt;os recursos serão liberados&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Por exemplo, eu gosto de experimentar com diferentes bancos de dados (inclusive armazeno vários docker-compose &lt;a href="https://github.com/rodolphocastro/Docker-Databases"&gt;em meu GitHub&lt;/a&gt;para facilitar o scaffold de projetos) e se eu fosse instalar todos em meu PC (que também uso para jogar! hahaha) seria um inferno de performance. Ter uma VM para cada um? Haja disco! Então, pra mim, a flexibilidade de poder subir e descer estes containers é algo que me ajudou bastante a estudar e utilizar diferentes bancos de dados em vários projetos .NET!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Bem, vamos agora ver &lt;strong&gt;como&lt;/strong&gt; estas imagens &lt;strong&gt;são criadas&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Criando o Dockerfile para nossa API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Vamos realizar o processo da maneira fácil, utilizando o Visual Studio 2019 (&lt;em&gt;também dá para fazer no 2017&lt;/em&gt;), mas pegarei o resultado e comentarei passo-a-passo o que está sendo feito 😄.&lt;/p&gt;

&lt;p&gt;Abra a Solution no Visual Studio e no Solution Explorer clique com o direito sobre a API e escolha &lt;em&gt;Add &amp;gt; Docker Support…&lt;/em&gt; e, quando aparecer o Dialog, escolha a opção de &lt;em&gt;Linux Containers&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--olKirDh_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/482/1%2A6d1GS9s_bd9thsg-Ncq8cw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--olKirDh_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/482/1%2A6d1GS9s_bd9thsg-Ncq8cw.png" alt="" width="482" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2bVgYLYM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/366/1%2AfCCEl1aUqFyraBaEXmU6Ng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2bVgYLYM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/366/1%2AfCCEl1aUqFyraBaEXmU6Ng.png" alt="" width="366" height="161"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Após finalizar o processo o Visual Studio fará as seguintes operações:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Um novo arquivo, Dockerfile, será criado na raiz do projeto&lt;/li&gt;
&lt;li&gt;Uma nova entrada será adicionada no launchSettings.json do projeto&lt;/li&gt;
&lt;li&gt;Algumas novas linhas serão adicionadas no .csproj do projeto&lt;/li&gt;
&lt;li&gt;Um novo arquivo .dockerignore será criado na raiz da solution&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Vamos por partes, primeiro olhando o mais importante de todos: o &lt;strong&gt;Dockerfile&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;O Dockerfile é o passo-a-passo para que o Docker Daemon (processo que realiza a “compilação” das imagens) saiba de onde começar, o que realizar e para onde jogar os resultados. É uma receita de bolo a ser executada para disponibilizar nosso aplicativo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/94df0ea7345472f920daea8ea38d2762/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/94df0ea7345472f920daea8ea38d2762/href"&gt;https://medium.com/media/94df0ea7345472f920daea8ea38d2762/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Basicamente o Dockerfile vai conter &lt;strong&gt;todos os passos que você executaria para publicar sua API&lt;/strong&gt; através &lt;strong&gt;da linha de comando&lt;/strong&gt;. Para quem está acostumado a executar as coisas sempre pelo Visual Studio os comandos podem parecer estranhos, mas rapidamente nos acostumamos com eles.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;No começo da minha jornada com Docker passei duas semanas utilizando apenas o Visual Studio Code e o Powershell, para ficar mais familiar ao processo “manual” de compilar e distribuir aplicações .NET.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Algo interessante é que o padrão do Visual Studio já vem com uma otimização muito boa chamada de &lt;strong&gt;Multi-Stage Builds.&lt;/strong&gt; Isso permite que o Docker utilize um cache entre cada passo do Dockerfile e dessa maneira tenha de recompilar apenas partes alteradas da Imagem.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Por exemplo: O step 8 (Restore dos NuGets) só será refeito caso ocorra alguma alteração nas dependências do csproj, que é copiado no step 7!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A segunda alteração, no launchSettings.json , nos permite executar e depurar o nosso container através do Visual Studio.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pRgWmOhJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/639/1%2AcU6KZYLbArJ9d7u5DWTDPw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pRgWmOhJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/639/1%2AcU6KZYLbArJ9d7u5DWTDPw.png" alt="" width="639" height="486"&gt;&lt;/a&gt;LaunchSettings.json com Docker&lt;/p&gt;

&lt;p&gt;A adição das propriedades httpPort e sslPort nos permitem &lt;strong&gt;garantir que o container executado&lt;/strong&gt; para Debug &lt;strong&gt;responda sempre nestas mesmas portas&lt;/strong&gt;! Pessoalmente não sei por qual motivo estas configurações não são adicionadas automaticamente assim como são para um projeto executado da maneira convencional.&lt;/p&gt;

&lt;p&gt;A alteração no .csproj &lt;strong&gt;permite a integração entre o Visual Studio e o Docker Desktop&lt;/strong&gt; , vamos dar uma olhada:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SiJNEh_t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/716/1%2AF9aou_DSx9T_CkrAinXsTQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SiJNEh_t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/716/1%2AF9aou_DSx9T_CkrAinXsTQ.png" alt="" width="716" height="260"&gt;&lt;/a&gt;.csproj com as Tags adicionais. A DockerfileTag grifada foi adicionada manualmente e será explicada quando formos publicar a imagem!&lt;/p&gt;

&lt;p&gt;Finalmente o .dockerignore adicionado:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dVQWHnTq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/338/1%2A3rDyElBbD0fXgkd7oWCqww.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dVQWHnTq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/338/1%2A3rDyElBbD0fXgkd7oWCqww.png" alt="" width="338" height="517"&gt;&lt;/a&gt;Algumas linhas foram adicionadas&lt;/p&gt;

&lt;p&gt;O .dockerignore é para o Docker o que o .gitignore é para o Git. Um .dockerignore bem escrito &lt;strong&gt;permite que apenas os arquivos estritamente necessários sejam copiados para a nossa imagem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Finalmente podemos ver a integração sendo executada através do &lt;strong&gt;Containers View.&lt;/strong&gt; Caso ele não esteja visível por padrão em seu Visual Studio o menu para habilita-lo é View &amp;gt; Other Windows &amp;gt; Containers. O &lt;strong&gt;Container View&lt;/strong&gt; nos &lt;strong&gt;permite visualizar&lt;/strong&gt; rapidamente quais &lt;strong&gt;containers estão sendo executados&lt;/strong&gt; , seus &lt;strong&gt;logs&lt;/strong&gt; , &lt;strong&gt;portas expostas, arquivos&lt;/strong&gt; dentro do container e as &lt;strong&gt;variáveis de ambiente definidas&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YNm-VkHv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A9o27eRosXyZ4YFs2B2utyQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YNm-VkHv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A9o27eRosXyZ4YFs2B2utyQ.png" alt="" width="880" height="156"&gt;&lt;/a&gt;Logs do Container expostos no Containers View&lt;/p&gt;

&lt;p&gt;A adição do Docker à API é simples assim utilizando o Visual Studio. Porém leia cuidadosamente o Dockerfile para entender o que está sendo feito.&lt;/p&gt;

&lt;p&gt;Procure no GitHub exemplos de outros Dockerfiles de outras aplicações, dê uma boa olhada nos comandos do Docker. Existem vários conceitos (como Volumes e Networks) que precisam bem mais do que um post para serem explicados e são melhor vistos na prática do que na teoria! Eventualmente farei um post mais focado nos detalhes do Docker e, um dia, sobre Kubernetes. Porém o foco desta série é o &lt;strong&gt;Cell CMS&lt;/strong&gt; &lt;em&gt;ponta-a-ponta.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Criando o Orquestrador (Docker Compose) para a API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Antes de falarmos sobre a integração com o Visual Studio, vamos ver o que é um “Docker Compose”:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Um &lt;strong&gt;docker compose é um documento&lt;/strong&gt; em formatoyaml que &lt;strong&gt;descreve quais imagens deve ser executadas&lt;/strong&gt; , suas &lt;strong&gt;dependências&lt;/strong&gt; , suas &lt;strong&gt;configurações&lt;/strong&gt; e &lt;strong&gt;como devem se comunicar&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Apesar da ideia do Cell CMS ser uma aplicação “lobo solitário”, vamos adicionar suporte á Orquestração de Containers ao nosso Projeto! Será mais para demonstrar a utilidade do &lt;strong&gt;Docker Compose,&lt;/strong&gt; e sua integração com o Visual Studio, &lt;strong&gt;durante o desenvolvimento&lt;/strong&gt;. De maneira resumida a &lt;strong&gt;vantagem&lt;/strong&gt; de &lt;strong&gt;adicionar um docker compose à sua Solution é garantir que desenvolvedores&lt;/strong&gt; com Docker Desktop &lt;strong&gt;facilmente tenham um ambiente pronto&lt;/strong&gt; para desenvolver (e debugar) assim que abrirem seu projeto.&lt;/p&gt;

&lt;p&gt;Ou seja: &lt;strong&gt;junto com o seu código você armazenará um documento que descreve as dependências necessárias do ambiente de sua aplicação&lt;/strong&gt;. Apesar de não serem muito recomendados para produção os composes facilitam, e muito, os ambientes de desenvolvimento e testes.&lt;/p&gt;

&lt;p&gt;Vamos lá! Com o Solution Explorer aberto clique em sua Api com botão direito e escolha &lt;em&gt;Add &amp;gt; Container Orchestrator Support…&lt;/em&gt; e escolha “Docker Compose” no primeiro dialog.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DtimyxGr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/478/1%2AoRpYTGE5UYluaYXkrp2fBg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DtimyxGr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/478/1%2AoRpYTGE5UYluaYXkrp2fBg.png" alt="" width="478" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rSALveID--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/406/1%2AyaMfUZT7iigOtFaCz_97cw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rSALveID--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/406/1%2AyaMfUZT7iigOtFaCz_97cw.png" alt="" width="406" height="118"&gt;&lt;/a&gt;Para este exemplo escolha Docker Compose, em outro post falaremos sobre Kubernetes!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SivpTeeb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/409/1%2A56tyc492ib17Gjgo70z-MA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SivpTeeb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/409/1%2A56tyc492ib17Gjgo70z-MA.png" alt="" width="409" height="159"&gt;&lt;/a&gt;Caso você tenha editado o Dockerfile o seguinte Dialog será apresentado, caso queira substituir o arquivo clique em Yes.&lt;/p&gt;

&lt;p&gt;O Visual Studio irá gerar as seguintes alterações no projeto (e solution):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Adicionar um novo projeto .dcproj à solution&lt;/li&gt;
&lt;li&gt;Gerar um arquivo .docker-compose.yml, este será nosso compose para o ambiente normal&lt;/li&gt;
&lt;li&gt;Gerar um arquivo .docker-compose.override.yml, este será nosso completamento (para ambientes de debug) ao compose principal&lt;/li&gt;
&lt;li&gt;Adicionar, ao projeto da API, uma referência ao .dcproj&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Vamos focar nos arquivos .yml, a sintaxe é bem simples e fácil de decorar e achar referências, o que é bom pois o suporte do Visual Studio aos arquivos deixa a desejar 😅&lt;/p&gt;

&lt;p&gt;Vamos dar uma olhada nos arquivos gerados:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kjjB8Eex--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/619/1%2AV5NX6xTHS6E72zZv5dCOiA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kjjB8Eex--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/619/1%2AV5NX6xTHS6E72zZv5dCOiA.png" alt="" width="619" height="212"&gt;&lt;/a&gt;docker-compose.yml&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eUqK9Bks--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/897/1%2Ahoayb5avW5pqdRueNd7ryA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eUqK9Bks--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/897/1%2Ahoayb5avW5pqdRueNd7ryA.png" alt="" width="880" height="270"&gt;&lt;/a&gt;docker-compose.override.yml&lt;/p&gt;

&lt;p&gt;Note a semelhança dos parâmetros das chaves do nosso docker-compose com os parâmetros do docker run. Isso não é mera coincidência! No fundo podemos encarar que o &lt;strong&gt;docker-compose é uma maneira de organizar e unificar diversos docker run.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Finalmente note, através do Containers View ou do Dashboard do Docker Desktop, que o Visual Studio automaticamente botou todos serviços que estão no docker-compose.yml para rodar:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GYUoYm1q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AUEaKychlWk1xWnpAVOrSdg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GYUoYm1q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AUEaKychlWk1xWnpAVOrSdg.png" alt="" width="880" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para arrumar as portas, e mantendo a integração que nos permite Debug através do Visual Studio, seguimos a mesma ideia da alteração do launchSettings.json. Como queremos isso apenas para ambientes de desenvolvimento vamos abrir o arquivo docker-compose.override.yml e adicionar um mapeamento explícito para portas do nosso container:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JNdk-0l0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/266/1%2AYHcqhUAckTnaGcAdqV5gPA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JNdk-0l0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/266/1%2AYHcqhUAckTnaGcAdqV5gPA.png" alt="" width="266" height="107"&gt;&lt;/a&gt;Achando o override no Solution Explorer&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uD2juPP7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/617/1%2AVNoRX8vh1W-JlLlTjz-4Pg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uD2juPP7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/617/1%2AVNoRX8vh1W-JlLlTjz-4Pg.png" alt="" width="617" height="227"&gt;&lt;/a&gt;Alteração para necessária&lt;/p&gt;

&lt;p&gt;Relembrando a sintaxe lá do docker run: &lt;strong&gt;À esquerda temos a porta do host&lt;/strong&gt; (sua máquina) e &lt;strong&gt;à direita temos a porta do container&lt;/strong&gt; (nossa api). No caso a configuração está &lt;strong&gt;dizendo que sempre que acessarmos&lt;/strong&gt; &lt;a href="http://localhost"&gt;&lt;strong&gt;http://localhost&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;:5000 e&lt;/strong&gt; &lt;a href="https://localhost"&gt;&lt;strong&gt;https://localhost&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;:5001 na máquina local a chamada será redirecionada para a posta 80/443&lt;/strong&gt; dentro &lt;strong&gt;do container&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Agora vamos confirmar que o Debug funciona? Para demonstrar vou adicionar um Breakpoint em um dos meus métodos do Startup.cs e mandar o Visual Studio iniciar o Debug:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JX8l5zOL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AU7_5QSdimPqhQg3syTEhEw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JX8l5zOL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AU7_5QSdimPqhQg3syTEhEw.gif" alt="" width="" height=""&gt;&lt;/a&gt;Breakpoint (no host) sendo ativado pelo container.&lt;/p&gt;

&lt;p&gt;Note que meu IConfiguration está devidamente populado com as configurações do appsettings.json, appsettings.Development.json, &lt;em&gt;variáveis de ambiente&lt;/em&gt; e meu secrets.json. O mapeamento dos appsettingsocorrem durante a criação da nossa imagem, porém o mapeamento do secrets.json é um “bonus” do Scaffold que o Visual Studio fez do docker-compose.override.yml. Em específico a linha de volume que mapeia o diretório ${APPDATA}/Microsoft/UserSecrets da minha máquina (host) para o diretório /root/.microsoft/usersecrets do container, com o modo ro (&lt;em&gt;readonly).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Para demonstrar como podemos &lt;strong&gt;alterar&lt;/strong&gt; as &lt;strong&gt;configurações&lt;/strong&gt; presentes no IConfiguration &lt;strong&gt;através de variáveis de ambiente&lt;/strong&gt; nos containers vou renomear nossa database sqlite para “outraStorage.db” através da ConnectionString no docker-compose.override.yml:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_jPA4-78--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/566/1%2AwulCu1rqBctw3I7KtOtIGA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_jPA4-78--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/566/1%2AwulCu1rqBctw3I7KtOtIGA.png" alt="" width="566" height="118"&gt;&lt;/a&gt;Alterando, por ambiente, a ConnectionString da Database&lt;/p&gt;

&lt;p&gt;Colocando um breakpoint e analisando o IConfiguration podemos ver os diversos providers e ver que o valor definido pelo docker-compose está lá:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8_XYk4MU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/829/1%2AexeGTqsfmzbL_eRL1V-wYA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8_XYk4MU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/829/1%2AexeGTqsfmzbL_eRL1V-wYA.png" alt="" width="829" height="200"&gt;&lt;/a&gt;Identificando o Provider de Variáveis de Ambiente&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7FwDzqGF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/793/1%2Alv0P3ncss85Lw0x2FLqTag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7FwDzqGF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/793/1%2Alv0P3ncss85Lw0x2FLqTag.png" alt="" width="793" height="373"&gt;&lt;/a&gt;Encontrando nossas alterações dentro do Provider!&lt;/p&gt;

&lt;p&gt;Este é o básicão sobre o Docker Compose. Como sempre recomendo a leitura da documentação para ver melhor os detalhes e até dicas mais avançadas! A documentação está disponível &lt;a href="https://docs.docker.com/compose/compose-file/"&gt;aqui&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Considerações Finais&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hoje vimos, &lt;em&gt;bem superficialmente&lt;/em&gt;, o que são Containers, quais suas utilidades e como podemos utilizar o Docker Desktop junto do Visual Studio para facilitar nossa vida durante o desenvolvimento de aplicações. Este foi, acredito, o maior post da série até agora e ainda assim ficou extremamente superficial. Espero que o post tenha sido o suficiente para atiçar a curiosidade daqueles que ainda estavam indecisos sobre as vantagens de containers!&lt;/p&gt;

&lt;p&gt;Prometo fazer um post algum dia indo nos pormenores do Docker e, inclusive, trazendo exemplos mais complexos de Orquestração (como, por exemplo, subir uma api, um worker, Um frontend e um banco ao mesmo tempo) mas no momento estou dedicado em avançar com o Cell CMS!&lt;/p&gt;

&lt;p&gt;Para o próximo post vamos dar uma olhada em &lt;strong&gt;Testes Unitários&lt;/strong&gt; de maneira &lt;strong&gt;produtiva&lt;/strong&gt; e prática! Utilizando as bibliotecas &lt;strong&gt;xUnit, AutoFixture, Moq e FluentAssertions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Obrigado por lerem mais este post, até a próxima e abraços!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dockercompose</category>
      <category>github</category>
      <category>devops</category>
    </item>
    <item>
      <title>Cell CMS — Criando logs robustos e monitorando uma API</title>
      <dc:creator>Rodolpho Alves</dc:creator>
      <pubDate>Mon, 13 Apr 2020 10:31:01 +0000</pubDate>
      <link>https://dev.to/ardc_overflow/cell-cms-criando-logs-robustos-e-monitorando-uma-api-16ed</link>
      <guid>https://dev.to/ardc_overflow/cell-cms-criando-logs-robustos-e-monitorando-uma-api-16ed</guid>
      <description>&lt;h3&gt;
  
  
  Cell CMS — Criando logs robustos e monitorando uma API
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S0-m_nkr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AwqkuJAgPn2geI8Lm" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S0-m_nkr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AwqkuJAgPn2geI8Lm" alt="" width="880" height="586"&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@lukechesser?utm_source=medium&amp;amp;utm_medium=referral"&gt;Luke Chesser&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No &lt;a href="https://dev.to/ardc_overflow/cell-cms-validando-inputs-e-mapeamento-automatico-3a70-temp-slug-5105973"&gt;último post&lt;/a&gt; falamos sobre duas bibliotecas para facilitar nossa vida o &lt;strong&gt;AutoMapper&lt;/strong&gt; e o &lt;strong&gt;FluentValidation&lt;/strong&gt;. No post de hoje vamos olhar mais algumas bibliotecas que facilitam a nossa vida, porém as bibliotecas de hoje irão nos &lt;strong&gt;auxiliar quando as&lt;/strong&gt;  &lt;strong&gt;coisas não saírem conforme o esperado!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Então hoje faremos três novos branches: feature/serilog, feature/app-insights e feature/healthcheck.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Por que devemos nos importar com Logs?&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Debug é um crime onde o programador é, ao mesmo tempo, o Detetive, a Vítima e o Criminoso.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A &lt;em&gt;clássica&lt;/em&gt; piadinha tem um belo fundo de verdade. Muitas vezes durante em ambientes de desenvolvimento (&lt;em&gt;e até mesmo em produção, quem nunca?&lt;/em&gt;) precisamos parar features novas e ir caçar bugs.&lt;/p&gt;

&lt;p&gt;O pior ainda é quando temos um bug que só acontece em um cenário complexo ou quando a descrição do bug se resume a alguém falar &lt;strong&gt;“Explodiu!”&lt;/strong&gt; e sequer apontar o que estava fazendo na hora que “explodiu”. Nesses casos sua esperança é ter uma base de código organizada o suficiente para que seja possível, pelo menos, &lt;strong&gt;identificar os pontos de entrada&lt;/strong&gt; que foram executados pelo usuário.&lt;/p&gt;

&lt;p&gt;Agora imagine que sua base não é organizada. Ou é gigantesca. Ou você tem dezenas de &lt;em&gt;microservices&lt;/em&gt; rodando ao mesmo tempo. Nesses casos você precisa sair &lt;strong&gt;caçando no código&lt;/strong&gt; onde &lt;em&gt;infernos&lt;/em&gt; a Exception aconteceu, sem muita esperança. É breakpoint pra tudo quanto é lado!&lt;/p&gt;

&lt;p&gt;Não seria muito bom se tivessemos pelo menos um &lt;strong&gt;Rastro&lt;/strong&gt; ou um &lt;strong&gt;Stacktrace&lt;/strong&gt; decente &lt;strong&gt;e armazenado em algum lugar&lt;/strong&gt; que não precisassemos fazer &lt;strong&gt;SSH, conectar em VPNs&lt;/strong&gt; , etc?&lt;/p&gt;

&lt;p&gt;Então, &lt;strong&gt;isso tudo existe&lt;/strong&gt; já! E existem &lt;strong&gt;várias ferramentas&lt;/strong&gt; para &lt;strong&gt;Produzir, Armazenar e Processar/Visualizar logs!&lt;/strong&gt; Algumas das mais famosas para visualizar e extrair métricas são:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kibana&lt;/li&gt;
&lt;li&gt;Grafana&lt;/li&gt;
&lt;li&gt;Seq&lt;/li&gt;
&lt;li&gt;Prometheus&lt;/li&gt;
&lt;li&gt;Azure Monitor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para a parte de armazenamento temos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure Log Analytics&lt;/li&gt;
&lt;li&gt;Logstash&lt;/li&gt;
&lt;li&gt;Stackify&lt;/li&gt;
&lt;li&gt;Splunk&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E boa parte destas ferramentas permitem que utilizemos um tipo de log mais “avançado”: o &lt;strong&gt;Log estruturado.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Um log estruturado é simplesmente um log que contem uma estrutura que facilita seu processamento. Esta estrutura pode ser um JSON ou XML por exemplo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Com uma estrutura bem definida você pode utilizar todas estas ferramentas para buscar informações relevantes no meio de seus Logs. Por exemplo, imagine que em um Controller, ao capturar um erro, você faça o log de alguns dados da Request e os chame de @Request. Ao utilizar uma destas ferramentas você poderia &lt;strong&gt;filtrar&lt;/strong&gt; , facilmente (&lt;em&gt;nada de precisar de trocentas REGEX!&lt;/em&gt;), &lt;strong&gt;por eventos contendo&lt;/strong&gt; apenas &lt;strong&gt;objetos&lt;/strong&gt; do tipo “Request”.&lt;/p&gt;

&lt;p&gt;Tudo isso é bem legal, mas e como podemos gerar logs neste formato? Faço um ConvertToJson toda vez que escrever no ILogger?&lt;/p&gt;

&lt;p&gt;Bem… você poderia fazer isso! Mas pra que reinventar a roda? Existem várias bibliotecas no universo .NET que já fazem isso para nós e, uma delas, é o  &lt;strong&gt;Serilog!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gerandos logs estruturados em um sistema .NET — Serilog&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Utilizando a biblioteca &lt;a href="https://serilog.net/"&gt;&lt;strong&gt;Serilog&lt;/strong&gt;&lt;/a&gt;podemos manter nossa utilização do ILogger apenas alterando as dependências e configurações da nossa API.&lt;/p&gt;

&lt;p&gt;Primeiro vamos instalar o &lt;strong&gt;Serilog&lt;/strong&gt;. Adicione o NuGet Serilog.AspNetCore e abra o Program.cs para adicionar as seguintes configurações:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/2065cedc7854461ecc2d0e2e22905116/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/2065cedc7854461ecc2d0e2e22905116/href"&gt;https://medium.com/media/2065cedc7854461ecc2d0e2e22905116/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Execute a API e já ficará evidente a diferença com o log padrão:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W1VUaWLv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/979/1%2ATK5bZd9B0tVr100IB-MORg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W1VUaWLv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/979/1%2ATK5bZd9B0tVr100IB-MORg.png" alt="" width="880" height="460"&gt;&lt;/a&gt;Padrão dos logs de Console do Serilog&lt;/p&gt;

&lt;p&gt;Para finalizarmos a configuração vamos ao Startup.cs e adicionemos mais uma linha ao Configure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/48630fb81d1ac13d76f661fa32f004a4/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/48630fb81d1ac13d76f661fa32f004a4/href"&gt;https://medium.com/media/48630fb81d1ac13d76f661fa32f004a4/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora sempre que utilizarmos os parâmetros (params object[]) ao escrever um evento seus dados serão armazenados junto da mensagem em nosso log! Por exemplo a seguinte utilização salvará os dados de uma request de POST /feed :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/dff87d30a34e56b22f1e960b6ab6dd56/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/dff87d30a34e56b22f1e960b6ab6dd56/href"&gt;https://medium.com/media/dff87d30a34e56b22f1e960b6ab6dd56/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note a utilização do operador @Command ao invés de apenas Command . A presença do operador @ sinaliza que o Serilog deve serializar o objeto ao invés de utilizar o ToString para salva-lo!&lt;/p&gt;

&lt;p&gt;Outro ponto legal sobre o Serilog são as diversas possibilidades de &lt;strong&gt;“Sinks”&lt;/strong&gt;. &lt;strong&gt;Um sink é um “destino”&lt;/strong&gt; para os seus logs. No exemplo acima utilizamos três sinks: &lt;em&gt;Console, Debug e Arquivos (File).&lt;/em&gt; Porém a comunidade mantem mais de 20 diferentes sinks e para diversos destinos! Alguns são:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon CloudWatch&lt;/li&gt;
&lt;li&gt;ApplicationInsights&lt;/li&gt;
&lt;li&gt;Console&lt;/li&gt;
&lt;li&gt;Elasticsearch&lt;/li&gt;
&lt;li&gt;Telegram&lt;/li&gt;
&lt;li&gt;Microsoft Teams&lt;/li&gt;
&lt;li&gt;Stackify&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lista completa pode ser encontrada na &lt;a href="https://github.com/serilog/serilog/wiki/Provided-Sinks"&gt;wiki&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Recomendo a leitura da documentação completa para mais informações sobre como o Serilog processa os dados de seus eventos: &lt;a href="https://github.com/serilog/serilog/wiki/Writing-Log-Events"&gt;https://github.com/serilog/serilog/wiki/Writing-Log-Events&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitoramento em Tempo Real — ApplicationInsights&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Agora que temos logs estruturados em nossa api e alguns arquivos com os logs, podemos pensar em monitorar nossa API. Temos várias opções para realizar isso, porém a mais &lt;em&gt;plug and play&lt;/em&gt; possível é, na minha opinião, utilizarmos o &lt;strong&gt;Azure Application Insights.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;O &lt;strong&gt;Azure Application Insights é uma solução para ingestão, monitoramento e análise de logs.&lt;/strong&gt; Através dele você poderá, em um único lugar, obter métricas sobre:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disponibilidade (Quanto tempo fica rodando, sem erros)&lt;/li&gt;
&lt;li&gt;Falhas (Exceptions sem tratamento)&lt;/li&gt;
&lt;li&gt;Falhas em Dependências (por ex: chamadas a APIs de Terceiros)&lt;/li&gt;
&lt;li&gt;Consumidores (quem está chamando sua API)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Além das métricas também podemos, graças ao &lt;strong&gt;Azure Monitor, definir&lt;/strong&gt; regras para &lt;strong&gt;alertas e alarmes com base nestas métricas&lt;/strong&gt; , &lt;strong&gt;ver&lt;/strong&gt; (em tempo real e série histórica) &lt;strong&gt;as instâncias que estão executando&lt;/strong&gt; e &lt;strong&gt;receber dicas sobre possíveis gargalos em nossa aplicação.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Os detalhes do &lt;strong&gt;Application Insights&lt;/strong&gt; são vastos! Por isso, hoje, vamos focar em coloca-lo em ação! Em um post futuro podemos fazer um &lt;em&gt;completão&lt;/em&gt; sobre esta solução e o &lt;strong&gt;Azure Monitor&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Para configurar o Application Insights SDK temos duas opções: Realizar a configuração manualmente ou Deixar que o Visual Studio realize a configuração para nós. Vou fazer o passo-a-passo para o Visual Studio, porém a ideia é a mesma para o manual:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Adicionar&lt;/strong&gt; o NuGet Microsoft.ApplicationInsights.AspNetCore&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Criar&lt;/strong&gt; , no Portal do &lt;strong&gt;Azure&lt;/strong&gt; , um novo &lt;strong&gt;recurso do Application Insights&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Adicionar, via appsettings, variavel de ambiente ou hardcoded, a &lt;strong&gt;Instrumentation Key&lt;/strong&gt; à  &lt;strong&gt;API&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Configurando o Application Insights através do Visual Studio&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Com a Solution aberta &lt;strong&gt;clique com o direito&lt;/strong&gt; em sua API, vá ao submenu &lt;strong&gt;Add&lt;/strong&gt; e selecione a opção &lt;strong&gt;Application Insights Telemetry&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_jTTef_2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/647/1%2Aa6X2uD8Kn-SA3CP3y7-Qdg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_jTTef_2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/647/1%2Aa6X2uD8Kn-SA3CP3y7-Qdg.png" alt="" width="647" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na tela que abrir clique em &lt;strong&gt;Get Started&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4yba__Nw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/806/1%2Aq0dv1L5PVeApKWc73DCFSQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4yba__Nw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/806/1%2Aq0dv1L5PVeApKWc73DCFSQ.png" alt="" width="806" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na próxima tela você deverá &lt;strong&gt;realizar login&lt;/strong&gt; (caso ainda não o tenha feito) com sua conta do Azure, &lt;strong&gt;escolher uma subscription&lt;/strong&gt; onde o recurso será utilizado/criado e, finalmente, &lt;strong&gt;escolher o resource&lt;/strong&gt;. Feito isso, clique em &lt;strong&gt;Register&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p5q4e3Yn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/548/1%2AXAJ3mYsMGbZ9eqDU0eFHhw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p5q4e3Yn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/548/1%2AXAJ3mYsMGbZ9eqDU0eFHhw.png" alt="" width="548" height="562"&gt;&lt;/a&gt;As configurações default para criar o resource&lt;/p&gt;

&lt;p&gt;Após confirmar tudo será configurado para você e uma nova tela será apresentada:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mop9dSmh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/731/1%2AAYhhjeA9FfvA0j0Wxoje1g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mop9dSmh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/731/1%2AAYhhjeA9FfvA0j0Wxoje1g.png" alt="" width="731" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;E se acessarmos nossa conta no Azure poderemos ver o recurso criado:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qK_01tet--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Ase8Xjr0CVuxncwCwaLbbTw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qK_01tet--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Ase8Xjr0CVuxncwCwaLbbTw.png" alt="" width="880" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Se executarmos a API e esperarmos alguns minutos (pode levar até 5 minutos) poderemos ver os dados sendo enviados para o Azure. Abra o Application Insights criado e você poderá visualizar, de cara, algumas métricas:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bXv8zNkP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A3VP-zdTbYG-gRBwb92RF4g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bXv8zNkP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A3VP-zdTbYG-gRBwb92RF4g.png" alt="" width="880" height="495"&gt;&lt;/a&gt;Métricas comuns do Application Insights&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3U-e9s5p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A_ZCIk3JivlBVn12U8t1Pig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3U-e9s5p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A_ZCIk3JivlBVn12U8t1Pig.png" alt="" width="880" height="395"&gt;&lt;/a&gt;O application map, que nos permite visualizar um Grafo com os clients da API e suas dependências&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Importante&lt;/strong&gt; : O padrão salvará uma Instrumentation Key em seu AppSettings. Eu recomendo mover para o secrets.json ou para uma variável de ambiente. Feito isso, atualize a chamada ao AddAplicationInsightsTelemetry com um IConfiguration contendo todas as fontes desejadas de configurações! Isso é importante pois &lt;strong&gt;por padrão o SDK olha apenas o appsettings.json!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Quase tudo configurado, apenas nos &lt;strong&gt;falta integrar&lt;/strong&gt; o &lt;strong&gt;Serilog&lt;/strong&gt; e o &lt;strong&gt;ApplicationInsights&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enviando logs do Serilog para o ApplicationInsight&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lembra das &lt;strong&gt;Sinks&lt;/strong&gt; que comentei no começo do post? Iremos utilizar uma nova sink para integrar com o ApplicationInsights!&lt;/p&gt;

&lt;p&gt;Adicione em sua API uma referência ao NuGet Serilog.Sinks.ApplicationInsights e voltemos ao Program.cs para configurar a escrita ao ApplicationInsights:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/afed5cabf4244eb68db65c8e1d3920c8/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/afed5cabf4244eb68db65c8e1d3920c8/href"&gt;https://medium.com/media/afed5cabf4244eb68db65c8e1d3920c8/href&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Importante:&lt;/strong&gt; Dependendo das versões das bibliotecas um &lt;strong&gt;Warning&lt;/strong&gt; poderá ser emitido para a &lt;strong&gt;TelemetryConfiguration.Active&lt;/strong&gt; &lt;em&gt;.&lt;/em&gt; Até o momento da escrita deste post (abril de 2020) a documentação não foi alterada para informar a melhor maneira de contornar isso, &lt;a href="https://github.com/serilog/serilog-sinks-applicationinsights/issues/121"&gt;porém existe uma issue&lt;/a&gt; no GitHub do projeto contendo &lt;em&gt;workarounds&lt;/em&gt; e sua possíveis alterações. Uma delas é utilizar TelemetryConfiguration.CreateDefault() ao inves do TelemetryConfiguration.Active.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Agora, se você acessar os traces em seu &lt;strong&gt;ApplicationInsights&lt;/strong&gt; poderá ver os logs da API:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QFZ1Xmoc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AKsZ9f_dNunhImQbHY9MS-w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QFZ1Xmoc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AKsZ9f_dNunhImQbHY9MS-w.png" alt="" width="880" height="394"&gt;&lt;/a&gt;Logs (comuns) da API dentro do ApplicationInsights&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verificando se a API está no ar — Healthchecks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A cereja no bolo 🍰 para “finalizarmos” esta parte da API será adicionar (e configurar) um &lt;strong&gt;HealthCheck&lt;/strong&gt; para a API.&lt;/p&gt;

&lt;p&gt;Um &lt;strong&gt;Healthcheck é&lt;/strong&gt; , basicamente, &lt;strong&gt;um endpoint&lt;/strong&gt; que outros programas (normalmente balanceadores de carga) podem chamar &lt;strong&gt;para saber se a API está funcionando&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;No universo .NET temos uma biblioteca já pronta para isso e que vem embutida com os projetos .NET Core: &lt;strong&gt;Microsoft.AspNetCore.Diagnostics.HealthChecks&lt;/strong&gt;. Então só precisamos &lt;strong&gt;adicionar os serviços à injeção de dependência&lt;/strong&gt; e &lt;strong&gt;configurar qual será a rota do endpoint!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Para isso vamos alterar nosso Startup.cs da seguinte maneira:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/2b09a9462aeb2e5a8322786a664510f3/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/2b09a9462aeb2e5a8322786a664510f3/href"&gt;https://medium.com/media/2b09a9462aeb2e5a8322786a664510f3/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para testar que deu certo execute a API e acesse a rota /health, você deverá ser recebido com uma mensagem: &lt;strong&gt;Healthy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XGj6TprP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/447/1%2Ah_EVa0QChZdnLL--rQHKyw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XGj6TprP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/447/1%2Ah_EVa0QChZdnLL--rQHKyw.png" alt="" width="447" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Podemos condicionar o nosso Healthy/Unhealthy também ao status da conexão da nossa API com o banco de dados! Caso você utilize o EntityFrameworkCore é só adicionar o NuGet Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore e encadear uma chamada ao .AddDbContext() após o AddHealthChecks(), por exemplo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/a40d8ad6c46fcc13be78563de9c114ba/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/a40d8ad6c46fcc13be78563de9c114ba/href"&gt;https://medium.com/media/a40d8ad6c46fcc13be78563de9c114ba/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Considerações Finais&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hoje vimos algumas bibliotecas que facilitam com o monitoramento da nossa API. Antes de publicarmos pode parecer &lt;em&gt;overkill&lt;/em&gt; ter tudo isso porém é um esforço não muito grande e com um &lt;em&gt;payoff&lt;/em&gt; absurdo quando você precisar descobrir o motivo de seu app/site/api está fora do ar ou quais são aqueles gargalos que te custam seus &lt;em&gt;leads&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Para o próximo posts vamos dar uma olhada em &lt;strong&gt;Docker, Docker Compose&lt;/strong&gt; , a &lt;strong&gt;Integração deles com o Visual Studio,&lt;/strong&gt; e algumas configurações finais antes de botarmos a API no ar!&lt;/p&gt;

&lt;p&gt;Obrigado por lerem mais este post, até a próxima e Abraços!&lt;/p&gt;

</description>
      <category>azure</category>
      <category>applicationinsights</category>
      <category>logging</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Cell CMS — Validando Inputs e Mapeamento Automático</title>
      <dc:creator>Rodolpho Alves</dc:creator>
      <pubDate>Thu, 09 Apr 2020 01:45:40 +0000</pubDate>
      <link>https://dev.to/ardc_overflow/cell-cms-validando-inputs-e-mapeamento-automatico-5h4p</link>
      <guid>https://dev.to/ardc_overflow/cell-cms-validando-inputs-e-mapeamento-automatico-5h4p</guid>
      <description>&lt;h3&gt;
  
  
  Cell CMS — Validando Inputs e Mapeamento Automático
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NcmIyK2o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/979/1%2AI9Sfn7ezDH_U7SWPFbnCwQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NcmIyK2o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/979/1%2AI9Sfn7ezDH_U7SWPFbnCwQ.png" alt="" width="880" height="460"&gt;&lt;/a&gt;Cell-CMS, rodando no Kestrel&lt;/p&gt;

&lt;p&gt;No &lt;a href="https://dev.to/ardc_overflow/cell-cms-design-patterns-e-endpoints-86a-temp-slug-1020308"&gt;último post&lt;/a&gt; falamos sobre o &lt;strong&gt;MediatR&lt;/strong&gt; e o &lt;em&gt;design pattern&lt;/em&gt; &lt;strong&gt;Mediator,&lt;/strong&gt; implementando os endpoints com Commands e Queries. No post de hoje vamos continuar de onde paramos e adicionar &lt;strong&gt;validação&lt;/strong&gt; aos nossos &lt;strong&gt;Commands&lt;/strong&gt; e realizar o &lt;strong&gt;mapeamento automático&lt;/strong&gt; dos Commands e seus campos para nossos  &lt;strong&gt;Models&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Hoje faremos dois novos branches: feature/fluent-validation e feature/auto-mapper, um deles será para a instalação e configuração do &lt;strong&gt;FluentValidation&lt;/strong&gt; e o outro para a instalação de configuração do &lt;strong&gt;AutoMapper&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validando inputs com a sintaxe padrão&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Por padrão, no universo .NET, temos os DataAnnotations para realizar a validação automática de nossos objetos. A documentação completa para eles pode ser encontrada &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations?view=netcore-3.1"&gt;neste link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Utilizando estes componentes temos de, em nossas classes, adicionar alguns Attributes para realizar as validações. Por exemplo, em um Command, eu poderia realizar a validação da seguinte maneira:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/0b97caf17dadc5cb014839cb690dbf6d/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/0b97caf17dadc5cb014839cb690dbf6d/href"&gt;https://medium.com/media/0b97caf17dadc5cb014839cb690dbf6d/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A vantagem&lt;/strong&gt; desta abordagem é que o .NET &lt;strong&gt;MVC automaticamente identifica&lt;/strong&gt; estas anotações e as &lt;strong&gt;executa&lt;/strong&gt; , permitindo que acessemos seu status através do ModelState! Desta maneira podemos, por exemplo, &lt;strong&gt;verificar o ModelState em nossos endpoints e retornar&lt;/strong&gt; erros de validação como &lt;strong&gt;BadRequests&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/a16692cc8c37122cd620e1225ccbdb0e/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/a16692cc8c37122cd620e1225ccbdb0e/href"&gt;https://medium.com/media/a16692cc8c37122cd620e1225ccbdb0e/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A desvantagem&lt;/strong&gt; desta abordagem é que estamos &lt;strong&gt;misturando lógicas de validação&lt;/strong&gt; com lógicas de &lt;strong&gt;armazenamento&lt;/strong&gt; de nossas classes (&lt;em&gt;se você for da galera SOLID, podemos dizer que fere o Single Resposability de nossa classe&lt;/em&gt;). &lt;strong&gt;Outra desvantagem&lt;/strong&gt; é que esta abordagem é prática &lt;strong&gt;apenas para validações mais simples&lt;/strong&gt;. Se, em algum momento, você decidir realizar uma &lt;strong&gt;validação assincrona ou mais complexa deverá criar seu próprio Attribute&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Mas Rodolpho, existe outra maneira que possamos realizar validações sem criar nosso próprio framework ou poluir o código de execução com infinitos IFs!?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sim! Existem alternativas prontas para utilizarmos! Uma delas é o &lt;strong&gt;FluentValidation!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validando inputs com sintaxe Fluent — FluentValidation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Utilizando a biblioteca &lt;strong&gt;FluentValidation&lt;/strong&gt; podemos escrever nossas &lt;strong&gt;validações em classes separadas&lt;/strong&gt; , &lt;strong&gt;integrar estas validações&lt;/strong&gt; com o middleware &lt;strong&gt;MVC&lt;/strong&gt; e &lt;strong&gt;utilizar&lt;/strong&gt; o mesmo &lt;strong&gt;ModelState&lt;/strong&gt; para armazenar nossos resultados e erros de validação!&lt;/p&gt;

&lt;p&gt;No Projeto da API adicione o NuGetFluentValidation.AspNetCore às dependências. Primeiros vamos configurar a integração com o MVC! Acesse o seu Startup.cs e realize as seguintes alterações:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/621794af1d506a884da60c4d6796ffe0/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/621794af1d506a884da60c4d6796ffe0/href"&gt;https://medium.com/media/621794af1d506a884da60c4d6796ffe0/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora precisamos escrever nossas regras de validação. Utilizarei o CreateFeed como exemplo. &lt;strong&gt;Crie uma nova classe&lt;/strong&gt; (pode ser em um novo arquivo ou no CreateFeed.cs) chamada &lt;strong&gt;CreateFeedValidator&lt;/strong&gt; e &lt;strong&gt;herde&lt;/strong&gt; a classe &lt;strong&gt;AbstractValidator.&lt;/strong&gt; Agora, graças ao AbstractValidator temos acesso &lt;strong&gt;à sintaxe Fluent dentro do Construtor&lt;/strong&gt; da nossa classe. Com isso podemos escrever nossas regras de validação:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/98defd0c7d54db49279ec9b18cfd1ed6/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/98defd0c7d54db49279ec9b18cfd1ed6/href"&gt;https://medium.com/media/98defd0c7d54db49279ec9b18cfd1ed6/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Outras vantagens do FluentValidations são:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Criação de &lt;strong&gt;Rulesets&lt;/strong&gt; para agrupar as validações (por exp: &lt;em&gt;Ruleset para validações síncronas e assíncronas&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Validação &lt;strong&gt;condicionais&lt;/strong&gt; (por exp: &lt;em&gt;Se informar X, Y não pode ser null&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Validações &lt;strong&gt;assíncronas&lt;/strong&gt; (por exp: &lt;em&gt;Consultar no Context se existe alguém com a Propriedade X ou Y&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Por exemplo uma regra assíncrona para verificar que não existe um Content com o mesmo Nome na base de dados:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/f3f0c67e78fd87ceb07c796f20460aa0/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/f3f0c67e78fd87ceb07c796f20460aa0/href"&gt;https://medium.com/media/f3f0c67e78fd87ceb07c796f20460aa0/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A documentação completa está disponível em &lt;a href="https://fluentvalidation.net/"&gt;https://fluentvalidation.net/&lt;/a&gt; e sugerido uma boa olhada! A biblioteca é bem ampla e possui várias funcionalidades úteis.&lt;/p&gt;

&lt;p&gt;Só para não deixar passar vazio temos um exemplo da validação para uma request inválida:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/ab5dfc1ca3fd881ee8ec443921ccb36b/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/ab5dfc1ca3fd881ee8ec443921ccb36b/href"&gt;https://medium.com/media/ab5dfc1ca3fd881ee8ec443921ccb36b/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mapeando propriedades manualmente&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Atualmente realizamos a “tradução” de nossos Commands de maneira manual. Ou seja, &lt;strong&gt;criamos um novo Model&lt;/strong&gt; e o &lt;strong&gt;preenchemos com os valores que vieram do nosso Command.&lt;/strong&gt; Por exemplo, na atualização de uma Tag:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/600dce8952265d1131f6521cba29bd28/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/600dce8952265d1131f6521cba29bd28/href"&gt;https://medium.com/media/600dce8952265d1131f6521cba29bd28/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Isso, com o passar do tempo, torna-se repetitivo e meio braçal. Uma solução seria escrevermos &lt;strong&gt;Adaptors&lt;/strong&gt; ou &lt;strong&gt;Factories&lt;/strong&gt; (&lt;em&gt;design patterns&lt;/em&gt;) para separar a lógica do mapeamento da lógica de execução, porém &lt;strong&gt;ainda temos o problema original&lt;/strong&gt; , salvo que &lt;strong&gt;escrevamos algo que realiza o mapeamento através de Reflection,&lt;/strong&gt; com base nos nomes das Propriedades de Origem e Destino.&lt;/p&gt;

&lt;p&gt;Felizmente alguém já escreveu isto para nós! Do mesmo criador que o &lt;strong&gt;MediatR&lt;/strong&gt; temos o famosíssimo &lt;strong&gt;AutoMapper&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mapeando propriedades automaticamente — AutoMapper&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Utilizando a biblioteca &lt;strong&gt;AutoMapper&lt;/strong&gt; podemos contar com &lt;strong&gt;o mapeamento automático&lt;/strong&gt; de propriedades &lt;strong&gt;através de padrões de nomenclatura e Reflection.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Para começarmos vamos adicionar o NuGet AutoMapper.Extensions.Microsoft.DependencyInjection à API e alterar nosso Startup.cs para registrar o AutoMapper nos services injetáveis:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/c911a184182bfb24c2616d1f2a844ae9/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/c911a184182bfb24c2616d1f2a844ae9/href"&gt;https://medium.com/media/c911a184182bfb24c2616d1f2a844ae9/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora precisamos criar nossos &lt;strong&gt;Mapeamentos&lt;/strong&gt;. O AutoMapper, ao ser utilizado com injeção de dependência, requer que criemos &lt;strong&gt;“Profiles”&lt;/strong&gt; para organizar nossos mapeamentos. Estes Profiles irão conter o “registro” para que o IMapper saiba entre quais objetos pode atuar!&lt;/p&gt;

&lt;p&gt;Comecei criando o Profile para nossos Feeds no arquivo Features/Feeds/FeedProfile.cs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/2417ce1e26aa2c545339673cbe8f76a5/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/2417ce1e26aa2c545339673cbe8f76a5/href"&gt;https://medium.com/media/2417ce1e26aa2c545339673cbe8f76a5/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para utilizarmos o Profile basta injetar o IMapper através do construtor e utilizar os métodos Map() para delegar ao AutoMapper o trabalho de popular as propriedades!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/8f1c8241899635e2fa32d1423c30aa93/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/8f1c8241899635e2fa32d1423c30aa93/href"&gt;https://medium.com/media/8f1c8241899635e2fa32d1423c30aa93/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Assim como o FluentValidation o &lt;strong&gt;AutoMapper&lt;/strong&gt; é amplamente customizável e você pode dar uma olhada na documentação completa em &lt;a href="https://automapper.org/"&gt;https://automapper.org/&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Considerações Finais&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hoje vimos duas bibliotecas bem úteis para o universo .NET, seja para o Frontend ou para o Backend estas bibliotecas nos auxiliam a manter o código limpo e organizado!&lt;/p&gt;

&lt;p&gt;Para o próximo post a ideia será melhorarmos o processo de &lt;strong&gt;Log de Erros&lt;/strong&gt; utilizando o &lt;strong&gt;Serilog,&lt;/strong&gt; obter métricas automaticamente com o &lt;strong&gt;Application Insights&lt;/strong&gt; e adicionarmos &lt;strong&gt;Healthchecks&lt;/strong&gt; à nossa API e banco de dados!&lt;/p&gt;

&lt;p&gt;Obrigado por lerem mais este post e até a próxima! Abraços!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>rest</category>
      <category>csharp</category>
      <category>automapper</category>
    </item>
  </channel>
</rss>
