<?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: Rubem Vasconcelos</title>
    <description>The latest articles on DEV Community by Rubem Vasconcelos (@rubemfsv).</description>
    <link>https://dev.to/rubemfsv</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%2F536627%2Fb62ecd76-87a9-458a-91cc-999ef69dc592.png</url>
      <title>DEV Community: Rubem Vasconcelos</title>
      <link>https://dev.to/rubemfsv</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rubemfsv"/>
    <language>en</language>
    <item>
      <title>Building Authentication Without Collecting Any Personal Data</title>
      <dc:creator>Rubem Vasconcelos</dc:creator>
      <pubDate>Thu, 26 Mar 2026 02:07:30 +0000</pubDate>
      <link>https://dev.to/rubemfsv/building-authentication-without-collecting-any-personal-data-3c7e</link>
      <guid>https://dev.to/rubemfsv/building-authentication-without-collecting-any-personal-data-3c7e</guid>
      <description>&lt;h2&gt;
  
  
  What If Your Auth System Collected Zero Personal Data?
&lt;/h2&gt;

&lt;p&gt;Traditional authentication has a common pattern: it requires you to collect personal information before you can verify identity. Email. Phone number. Password. All of it ends up in a database, and databases get breached.&lt;/p&gt;

&lt;p&gt;But there's a different model — one borrowed from cryptocurrency wallets — where the server stores nothing that can be traced back to a real person. Let's build it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Idea
&lt;/h2&gt;

&lt;p&gt;Instead of email + password, the user gets a &lt;strong&gt;12-word seed phrase&lt;/strong&gt;. From that phrase, we derive a cryptographic key pair. The &lt;strong&gt;public key&lt;/strong&gt; becomes their anonymous identity on the server. The &lt;strong&gt;private key&lt;/strong&gt; and the &lt;strong&gt;raw mnemonic&lt;/strong&gt; never leave the browser.&lt;/p&gt;

&lt;p&gt;Login doesn't ask for the full phrase either. It challenges the user to provide 3 randomly chosen words — which are hashed client-side before comparison. The server only ever sees hashes.&lt;/p&gt;

&lt;p&gt;Here's what the server ends up storing:&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;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a1b2c3d4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"publicKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8f7a9b2c1d3e4f5a..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mnemonicHashes"&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="s2"&gt;"e3b0c44298fc1c14..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"d7a8fbb307d78094..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&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;No email. No password. No name. Nothing linkable to a person.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Generating the Seed Phrase (BIP-39)
&lt;/h2&gt;

&lt;p&gt;BIP-39 is the standard used by hardware wallets like Ledger and Trezor. It maps 128 bits of random entropy to 12 words from a fixed 2048-word English dictionary. Each word encodes roughly 11 bits of information — enough that the full phrase has ~128 bits of security.&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;generateMnemonic&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;@scure/bip39&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;wordlist&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;@scure/bip39/wordlists/english.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 128 bits of entropy → 12 words&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mnemonic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateMnemonic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wordlist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// "witch collapse practice feed shame open despair creek road again ice least"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;@scure/bip39&lt;/code&gt; library is audited and has zero dependencies — important for anything touching key material.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Deriving the Ed25519 Key Pair
&lt;/h2&gt;

&lt;p&gt;Ed25519 is a fast, secure elliptic-curve signature scheme. A 32-byte private key deterministically produces a 32-byte public key. We derive the private key from the mnemonic using HMAC-SHA-256 with a domain-separation tag — this ensures the same mnemonic produces different keys for different applications.&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;mnemonicToSeedSync&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;@scure/bip39&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ed&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;@noble/ed25519&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hmac&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;@noble/hashes/hmac&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sha256&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;@noble/hashes/sha2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deriveKeyPair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mnemonic&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="c1"&gt;// BIP-39: mnemonic → 64-byte seed&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mnemonicToSeedSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mnemonic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// HMAC with a domain tag — same seed, different keys per app&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;derived&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your-app-name-ed25519&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;seed&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;privateKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;derived&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&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="mi"&gt;32&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;publicKey&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;ed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPublicKeyAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;privateKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;bytesToHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;// goes to the server&lt;/span&gt;
    &lt;span class="na"&gt;privateKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;bytesToHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;privateKey&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// stays in the browser, forever&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;The domain tag (&lt;code&gt;'your-app-name-ed25519'&lt;/code&gt;) is what prevents key reuse across different systems using the same BIP-39 seed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Hashing the Mnemonic Words
&lt;/h2&gt;

&lt;p&gt;Before any data leaves the browser, each of the 12 words is hashed individually using SHA-256 via the native Web Crypto API. The server stores these hashes — never the words themselves.&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;hashWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&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="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&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;buffer&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;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SHA-256&lt;/span&gt;&lt;span class="dl"&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="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&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;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&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="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;padStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&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="nf"&gt;join&lt;/span&gt;&lt;span class="p"&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="c1"&gt;// Hash all 12 words in parallel&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mnemonicHashes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;mnemonic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hashWord&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A note on salting&lt;/strong&gt;: BIP-39 words are common English words, making them vulnerable to precomputed rainbow tables in theory. For production, you'd want to salt each hash with something unique per user (e.g., their public key). This demo keeps it simple for clarity.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 4: Registration
&lt;/h2&gt;

&lt;p&gt;The client sends three things to the server — and none of them are personally identifiable:&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;await&lt;/span&gt; &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;someAnonymousId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// could be anything — a hash, a UUID&lt;/span&gt;
  &lt;span class="na"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;keyPair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;mnemonicHashes&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;That's the entire registration payload. The server has no way to contact this user, no way to identify them in the real world, and nothing useful to hand over in a breach.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: The Login Challenge
&lt;/h2&gt;

&lt;p&gt;This is the most interesting part. Login doesn't ask for the full mnemonic — it picks 3 random positions and asks the user to type only those words.&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;// Pick 3 unique random positions from 0–11&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;pickRandomIndices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;indices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&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;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;indices&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;total&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="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;challengeIndices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pickRandomIndices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// e.g. [1, 6, 10] → "Enter words #2, #7, and #11"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user types those 3 words. Each one is hashed client-side, then compared against the stored hash at that position:&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;isAuthenticated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;challengeIndices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;hashWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;storedHashes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&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;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;saveSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicKey&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;The full mnemonic is never transmitted. The server never sees plaintext words. Only hashes travel over the wire — and only 3 of the 12, at random positions that change every login.&lt;/p&gt;




&lt;h2&gt;
  
  
  What About Sessions?
&lt;/h2&gt;

&lt;p&gt;Sessions store the minimum possible state: the public key and an expiry timestamp.&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;SessionData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;publicKey&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="nl"&gt;expiresAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;saveSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicKey&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="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SessionData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;expiresAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 2 days&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;session&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;session&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;No JWT with an email claim. No cookie with a user ID tied to a real person. Just a public key reference.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Trade-offs (Honestly)
&lt;/h2&gt;

&lt;p&gt;This architecture makes some things genuinely better and some things genuinely harder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A database breach exposes nothing usable — public keys and hashes only&lt;/li&gt;
&lt;li&gt;No "forgot password" attack surface exists, because there's no password&lt;/li&gt;
&lt;li&gt;No email = no phishing vector tied to this account&lt;/li&gt;
&lt;li&gt;The user owns their identity completely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Harder:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Losing the seed phrase means losing the account. Permanently. No recovery.&lt;/li&gt;
&lt;li&gt;Users unfamiliar with seed phrases need onboarding. This is real UX work.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Libraries
&lt;/h2&gt;

&lt;p&gt;Everything cryptographic uses audited, zero-dependency implementations from &lt;a href="https://paulmillr.com/" rel="noopener noreferrer"&gt;Paul Miller&lt;/a&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@scure/bip39&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mnemonic generation from entropy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@noble/ed25519&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Key pair derivation and signing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@noble/hashes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SHA-256, HMAC for key derivation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Crypto API&lt;/td&gt;
&lt;td&gt;SHA-256 word hashing (built-in)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Audited libraries matter here. Key derivation code is security-critical and not the place for unreviewed dependencies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The shift this pattern forces is conceptual more than technical: instead of asking "how do we protect our users' data?", you ask "what if we simply didn't have it?".&lt;/p&gt;

&lt;p&gt;The cryptographic primitives — BIP-39, Ed25519, SHA-256 — are battle-tested and well-understood. The challenge is UX: helping users understand that their 12 words are their account, and that means treating them with the same care as a physical key.&lt;/p&gt;

&lt;p&gt;For the right use case — privacy tools, pseudonymous platforms, self-sovereign applications — this model removes an entire category of risk from the threat surface.&lt;/p&gt;

&lt;p&gt;Full open source demo: &lt;a href="https://github.com/rubemfsv/anonymous-auth" rel="noopener noreferrer"&gt;GitHub link&lt;/a&gt;&lt;/p&gt;

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

</description>
      <category>webdev</category>
      <category>cryptography</category>
      <category>security</category>
      <category>privacy</category>
    </item>
    <item>
      <title>From Monoliths to Microservices: Rethinking the Test Pyramid</title>
      <dc:creator>Rubem Vasconcelos</dc:creator>
      <pubDate>Thu, 19 Feb 2026 13:13:29 +0000</pubDate>
      <link>https://dev.to/rubemfsv/from-monoliths-to-microservices-rethinking-the-test-pyramid-441</link>
      <guid>https://dev.to/rubemfsv/from-monoliths-to-microservices-rethinking-the-test-pyramid-441</guid>
      <description>&lt;p&gt;In the previous article, I explored the fundamentals of software testing and the classic Test Pyramid.&lt;/p&gt;

&lt;p&gt;This post builds on that foundation and is adapted from &lt;strong&gt;Chapter 4 – &lt;em&gt;Tests in Microservices&lt;/em&gt;&lt;/strong&gt; of my Master’s thesis, where I analyze how testing strategies evolve when moving from monolithic architectures to distributed microservices systems.&lt;/p&gt;

&lt;p&gt;The core idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Test Pyramid still applies — but its structure must evolve in distributed systems.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Testing in Monolithic Systems
&lt;/h2&gt;

&lt;p&gt;Monolithic systems are deployed as a single unit. All components share the same codebase, runtime, and memory space.&lt;/p&gt;

&lt;p&gt;This architectural simplicity has direct implications for testing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw7dlej5buakn91uy619s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw7dlej5buakn91uy619s.png" alt="Test Pyramid" width="466" height="246"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Image Source: Martin Fowler, The Practical Test Pyramid&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It usually includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit Tests
&lt;/li&gt;
&lt;li&gt;Integration Tests
&lt;/li&gt;
&lt;li&gt;End-to-End (or UI) Tests
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Unit Tests
&lt;/h3&gt;

&lt;p&gt;Unit testing focuses on validating small, isolated parts of the system. As Fowler (2014) emphasizes, unit tests should be fast and independent, forming the foundation of the test suite.&lt;/p&gt;

&lt;p&gt;Because everything runs within the same process, isolation is easier and execution is deterministic.&lt;/p&gt;




&lt;h3&gt;
  
  
  Integration Testing
&lt;/h3&gt;

&lt;p&gt;Integration testing combines components and verifies their interactions.&lt;/p&gt;

&lt;p&gt;According to &lt;strong&gt;ISTQB (2018)&lt;/strong&gt;, integration testing aims to expose defects in interfaces and interactions between integrated components or systems.&lt;/p&gt;

&lt;p&gt;Two common forms include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Component integration testing&lt;/strong&gt; – interaction between internal modules
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System integration testing&lt;/strong&gt; – interaction with external systems (e.g., third-party APIs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In monolithic systems, integration remains relatively predictable because communication occurs within the same application boundary.&lt;/p&gt;




&lt;h3&gt;
  
  
  End-to-End Testing
&lt;/h3&gt;

&lt;p&gt;System testing evaluates the fully integrated system against specified requirements (ISTQB, 2018).&lt;/p&gt;

&lt;p&gt;End-to-end testing simulates real user flows, validating complete business scenarios across the application stack (Fowler &amp;amp; Lewis, 2014).&lt;/p&gt;

&lt;p&gt;Since monoliths are deployed as a single artifact, validation of full flows is operationally simpler than in distributed architectures.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Changes With Microservices?
&lt;/h2&gt;

&lt;p&gt;Microservices architectures introduce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Independent deployments
&lt;/li&gt;
&lt;li&gt;Network communication
&lt;/li&gt;
&lt;li&gt;Asynchronous messaging
&lt;/li&gt;
&lt;li&gt;Distributed data management
&lt;/li&gt;
&lt;li&gt;Cross-team ownership
&lt;/li&gt;
&lt;li&gt;Runtime unpredictability
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Failures no longer occur only inside the codebase — they happen &lt;strong&gt;between services&lt;/strong&gt;, across network boundaries.&lt;/p&gt;

&lt;p&gt;Bozkurt, Harman, and Hassoun (2010) observed that distributed systems introduce higher testing complexity due to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dynamic service behavior
&lt;/li&gt;
&lt;li&gt;Simultaneous distributed transactions
&lt;/li&gt;
&lt;li&gt;Multiple implementations under shared specifications
&lt;/li&gt;
&lt;li&gt;Cross-team coordination
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This complexity forces a structural evolution of the testing strategy.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Microservices Test Pyramid
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr9fxbsmqvzs4mp8fgcdr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr9fxbsmqvzs4mp8fgcdr.png" alt="Microservices Test Pyramid" width="800" height="516"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Image Source: SOTOMAYOR et al. (2023)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In addition to traditional:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit Tests
&lt;/li&gt;
&lt;li&gt;Integration Tests
&lt;/li&gt;
&lt;li&gt;End-to-End Tests
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Microservices introduce two critical layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Component Tests&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contract Tests&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In many models, integration, component, and contract tests form the broader &lt;strong&gt;service testing layer&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Unit Testing in Microservices
&lt;/h2&gt;

&lt;p&gt;In microservices, units typically include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Domain services
&lt;/li&gt;
&lt;li&gt;Resource handlers
&lt;/li&gt;
&lt;li&gt;Gateways
&lt;/li&gt;
&lt;li&gt;Repositories
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, as Habl, Kipouridis, and Fottner (2017) note, successful isolated unit tests do not guarantee correct behavior once components interact within distributed systems.&lt;/p&gt;

&lt;p&gt;Network boundaries fundamentally change the reliability assumptions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Integration Testing in Distributed Systems
&lt;/h2&gt;

&lt;p&gt;In microservices, integration testing focuses on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database interactions
&lt;/li&gt;
&lt;li&gt;Inter-service communication
&lt;/li&gt;
&lt;li&gt;Messaging systems
&lt;/li&gt;
&lt;li&gt;External APIs
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sotomayor et al. (2023) highlight that integration testing in microservices architectures must explicitly validate communication layers and service coordination mechanisms.&lt;/p&gt;

&lt;p&gt;Common failure sources include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serialization mismatches
&lt;/li&gt;
&lt;li&gt;Version incompatibilities
&lt;/li&gt;
&lt;li&gt;Timeout configurations
&lt;/li&gt;
&lt;li&gt;Infrastructure inconsistencies
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Integration tests must validate these boundaries intentionally.&lt;/p&gt;




&lt;h2&gt;
  
  
  Component Testing
&lt;/h2&gt;

&lt;p&gt;Component testing treats each microservice as an independent system.&lt;/p&gt;

&lt;p&gt;Using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mocks
&lt;/li&gt;
&lt;li&gt;Stubs
&lt;/li&gt;
&lt;li&gt;Simulated dependencies
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;external network interactions are removed during testing.&lt;/p&gt;

&lt;p&gt;This ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deterministic execution
&lt;/li&gt;
&lt;li&gt;Faster feedback loops
&lt;/li&gt;
&lt;li&gt;Reduced flakiness
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As Fowler and Lewis (2014) argue, isolating services during testing is essential to prevent infrastructure instability from compromising test reliability.&lt;/p&gt;

&lt;p&gt;Component tests bridge the gap between isolated unit tests and full distributed integration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Contract Testing: A Structural Necessity
&lt;/h2&gt;

&lt;p&gt;One of the most important additions in microservices architectures is &lt;strong&gt;contract testing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Contract testing verifies service boundaries and interaction agreements.&lt;/p&gt;

&lt;p&gt;Sotomayor et al. (2023) define contract testing in microservices as the validation of service boundaries to ensure compatibility between independently evolving services.&lt;/p&gt;

&lt;p&gt;In distributed systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APIs define request/response contracts
&lt;/li&gt;
&lt;li&gt;Messaging schemas define event contracts
&lt;/li&gt;
&lt;li&gt;Consumers depend on explicit interface structures
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a provider changes its contract without coordination, it may break consumers in production.&lt;/p&gt;

&lt;p&gt;Contract testing ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Schema compatibility
&lt;/li&gt;
&lt;li&gt;Behavioral consistency
&lt;/li&gt;
&lt;li&gt;Safe independent deployments
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Contract testing addresses methodological complexity introduced by independently deployed microservices.&lt;/p&gt;

&lt;p&gt;Tools like Pact are widely used to operationalize this practice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why End-to-End Tests Must Be Minimized
&lt;/h2&gt;

&lt;p&gt;End-to-end testing in microservices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires multiple services running simultaneously
&lt;/li&gt;
&lt;li&gt;Depends on production-like environments
&lt;/li&gt;
&lt;li&gt;Is slower and more fragile
&lt;/li&gt;
&lt;li&gt;Is harder to debug
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Over-reliance on E2E tests can lead to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flaky CI pipelines
&lt;/li&gt;
&lt;li&gt;Slow feedback cycles
&lt;/li&gt;
&lt;li&gt;High maintenance overhead
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cohn (2010) already recommended minimizing top-layer tests in the Test Pyramid. In microservices architectures, this recommendation becomes even more critical.&lt;/p&gt;

&lt;p&gt;Confidence should be built lower in the pyramid — especially at service boundaries.&lt;/p&gt;




&lt;h2&gt;
  
  
  Structural Differences: Monolith vs Microservices
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Monolith&lt;/th&gt;
&lt;th&gt;Microservices&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Failures mostly internal&lt;/td&gt;
&lt;td&gt;Failures often at boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shared memory communication&lt;/td&gt;
&lt;td&gt;Network communication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single deployment&lt;/td&gt;
&lt;td&gt;Independent deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implicit contracts&lt;/td&gt;
&lt;td&gt;Explicit contracts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Centralized coordination&lt;/td&gt;
&lt;td&gt;Distributed coordination&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Testing evolves from validating internal correctness to validating &lt;strong&gt;communication guarantees and service contracts&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Microservices provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scalability
&lt;/li&gt;
&lt;li&gt;Deployment independence
&lt;/li&gt;
&lt;li&gt;Team autonomy
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But they introduce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Greater operational complexity
&lt;/li&gt;
&lt;li&gt;Increased testing surface area
&lt;/li&gt;
&lt;li&gt;More failure modes
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Test Pyramid still applies — but its center shifts toward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strong unit tests
&lt;/li&gt;
&lt;li&gt;Robust service-level tests
&lt;/li&gt;
&lt;li&gt;Explicit contract validation
&lt;/li&gt;
&lt;li&gt;Minimal, strategic end-to-end tests
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In distributed systems, testing strategy becomes an architectural decision.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Bozkurt, M., Harman, M., &amp;amp; Hassoun, Y. (2010). &lt;em&gt;Testing web services: A survey&lt;/em&gt;.
&lt;/li&gt;
&lt;li&gt;Cohn, M. (2010). &lt;em&gt;Succeeding with Agile: Software Development Using Scrum&lt;/em&gt;. Addison-Wesley.
&lt;/li&gt;
&lt;li&gt;Fowler, M. (2014). &lt;em&gt;The Practical Test Pyramid&lt;/em&gt;. martinfowler.com
&lt;/li&gt;
&lt;li&gt;Habl, G., Kipouridis, I., &amp;amp; Fottner, J. (2017). &lt;em&gt;Deploying microservices for a cloud-based design of system-of-systems in intralogistics&lt;/em&gt;. IEEE.&lt;/li&gt;
&lt;li&gt;ISTQB (2018). &lt;em&gt;Standard Glossary of Terms Used in Software Testing&lt;/em&gt;.
&lt;/li&gt;
&lt;li&gt;Sotomayor, B. et al. (2023). &lt;em&gt;Comparison of open-source runtime testing tools for microservices&lt;/em&gt;. Software Quality Journal, Springer.
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This article is adapted from Chapter 4 – “Tests in Microservices” of my Master’s thesis, which presents a comparative analysis of testing strategies in monolithic and microservices-based systems.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>distributedsystems</category>
      <category>microservices</category>
      <category>testing</category>
    </item>
    <item>
      <title>Software Testing Fundamentals: Black Box, White Box, and the Test Pyramid</title>
      <dc:creator>Rubem Vasconcelos</dc:creator>
      <pubDate>Thu, 29 Jan 2026 19:15:29 +0000</pubDate>
      <link>https://dev.to/rubemfsv/software-testing-fundamentals-black-box-white-box-and-the-test-pyramid-mm4</link>
      <guid>https://dev.to/rubemfsv/software-testing-fundamentals-black-box-white-box-and-the-test-pyramid-mm4</guid>
      <description>&lt;p&gt;Software testing plays a fundamental role in ensuring that systems behave as expected and meet both user and business requirements. While tools and frameworks evolve quickly, the core testing principles remain largely the same. &lt;/p&gt;

&lt;p&gt;The fundamental objective of software testing is to verify whether the system behaves according to previously defined expectations (Bourque &amp;amp; Fairley, &lt;em&gt;Guide to the Software Engineering Body of Knowledge&lt;/em&gt;, 2014).&lt;/p&gt;

&lt;p&gt;This article is adapted from &lt;strong&gt;Chapter 4 – Tests in Microservices&lt;/strong&gt; of my Master’s thesis, where I analyze software testing strategies and how they scale from traditional systems to modern architectures.&lt;br&gt;&lt;br&gt;
In this post, I’ll focus on the &lt;strong&gt;fundamentals of software testing&lt;/strong&gt; and the &lt;strong&gt;Test Pyramid&lt;/strong&gt;, which serves as the foundation for understanding more advanced testing approaches.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is the Goal of Software Testing?
&lt;/h2&gt;

&lt;p&gt;At its core, software testing aims to ensure that a system conforms to expected behavior and defined requirements.&lt;/p&gt;

&lt;p&gt;Software testing seeks to validate whether a system is in accordance with functional and non-functional requirements previously established.&lt;/p&gt;

&lt;p&gt;Beyond simply finding bugs, testing helps reduce risks in production environments and increases confidence when evolving software systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  Software Testing Techniques
&lt;/h2&gt;

&lt;p&gt;Software testing techniques can be broadly classified into three categories (Mathur, &lt;em&gt;Foundations of Software Testing&lt;/em&gt;, 2013):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Black-box testing
&lt;/li&gt;
&lt;li&gt;White-box testing
&lt;/li&gt;
&lt;li&gt;Gray-box testing
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these techniques plays a complementary role in achieving adequate test coverage.&lt;/p&gt;




&lt;h3&gt;
  
  
  Black-box Testing
&lt;/h3&gt;

&lt;p&gt;Black-box testing evaluates the &lt;strong&gt;functionality of a system without considering knowledge of its internal structure&lt;/strong&gt;. The tester focuses exclusively on inputs and outputs, without concern for implementation details or internal logic.&lt;/p&gt;

&lt;p&gt;In black-box testing, the tester concentrates on analyzing system inputs and outputs, without worrying about the underlying logic or component implementation.&lt;/p&gt;

&lt;p&gt;This technique is effective in validating user requirements and detecting failures that may not be evident through internal code analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No need for internal code knowledge
&lt;/li&gt;
&lt;li&gt;Faster execution and lower cost
&lt;/li&gt;
&lt;li&gt;Good alignment with user expectations
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cannot detect internal logic or implementation errors
&lt;/li&gt;
&lt;li&gt;Limited visibility into hidden edge cases
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this reason, black-box testing should always be combined with other testing approaches.&lt;/p&gt;




&lt;h3&gt;
  
  
  White-box Testing
&lt;/h3&gt;

&lt;p&gt;White-box testing is based on the &lt;strong&gt;analysis of the internal structure of the system&lt;/strong&gt;, requiring detailed knowledge of the source code and component logic.&lt;/p&gt;

&lt;p&gt;White-box testing evaluates not only system functionality, but also efficiency, security, and maintainability.&lt;/p&gt;

&lt;p&gt;This technique enables the identification of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logical flaws
&lt;/li&gt;
&lt;li&gt;Syntax errors
&lt;/li&gt;
&lt;li&gt;Execution path issues
&lt;/li&gt;
&lt;li&gt;Security vulnerabilities
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also supports code optimization, improving performance and scalability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Challenges:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Higher complexity
&lt;/li&gt;
&lt;li&gt;Greater time and effort required
&lt;/li&gt;
&lt;li&gt;Requires strong technical expertise
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;White-box testing is especially valuable at lower levels, such as unit and component testing.&lt;/p&gt;




&lt;h3&gt;
  
  
  Gray-box Testing
&lt;/h3&gt;

&lt;p&gt;Gray-box testing combines characteristics of black-box and white-box testing. The tester has &lt;strong&gt;limited access to the system’s internal structure&lt;/strong&gt;, allowing partial analysis of the code and logic.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Gray-box testing enables the detection of failures that would not be evident through purely external analysis, without the complexity associated with full white-box testing.”&lt;/em&gt;&lt;br&gt;&lt;br&gt;
— Mathur, &lt;em&gt;Foundations of Software Testing&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This approach offers a good balance between coverage and cost, although limited internal access can still restrict test precision.&lt;/p&gt;




&lt;h2&gt;
  
  
  Functional vs Non-functional Testing
&lt;/h2&gt;

&lt;p&gt;In addition to testing techniques, defining &lt;strong&gt;testing objectives&lt;/strong&gt; is a fundamental part of the software testing process.&lt;/p&gt;

&lt;p&gt;According to Bourque &amp;amp; Fairley, in the &lt;em&gt;Guide to the Software Engineering Body of Knowledge&lt;/em&gt; (2014), identifying testing objectives is a fundamental aspect of the software testing process.&lt;/p&gt;

&lt;p&gt;These objectives are commonly divided into two categories.&lt;/p&gt;




&lt;h3&gt;
  
  
  Functional Tests
&lt;/h3&gt;

&lt;p&gt;Functional tests aim to validate system functionalities and verify whether they comply with established requirements. This category often includes &lt;strong&gt;end-to-end testing&lt;/strong&gt;, which validates integration and interaction between different system components.&lt;/p&gt;




&lt;h3&gt;
  
  
  Non-functional Tests
&lt;/h3&gt;

&lt;p&gt;Non-functional tests focus on system qualities not directly related to functionality, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance
&lt;/li&gt;
&lt;li&gt;Scalability
&lt;/li&gt;
&lt;li&gt;Reliability
&lt;/li&gt;
&lt;li&gt;Stress tolerance
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Performance and stress tests evaluate system capacity, response time, and behavior under high load and overload conditions (Sotomayor et al., &lt;em&gt;Comparison of runtime testing tools for microservices&lt;/em&gt;, 2019).&lt;/p&gt;

&lt;p&gt;A clear definition of these objectives helps ensure adequate coverage and minimizes the risk of failures in production environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Test Pyramid
&lt;/h2&gt;

&lt;p&gt;One of the most widely adopted models for organizing software tests is the &lt;strong&gt;Test Pyramid&lt;/strong&gt;, originally proposed by Mike Cohn.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“The 'Test Pyramid' is a metaphor that tells us to group software tests into buckets of different granularity. It also gives an idea of how many tests we should have in each of these groups.”&lt;/em&gt;&lt;br&gt;&lt;br&gt;
— Martin Fowler, The Practical Test Pyramid&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The pyramid structures tests according to cost, speed, and scope.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw7dlej5buakn91uy619s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw7dlej5buakn91uy619s.png" alt="Test Pyramid" width="466" height="246"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Image Source: Martin Fowler, The Practical Test Pyramid&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Unit Tests (Base of the Pyramid)
&lt;/h3&gt;

&lt;p&gt;Unit tests focus on small, isolated parts of the system.&lt;/p&gt;

&lt;p&gt;According to Martin Fowler in &lt;em&gt;The Practical Test Pyramid&lt;/em&gt;, unit tests are expected to be significantly faster than other test categories, and each unit should be tested independently&lt;/p&gt;

&lt;p&gt;They are usually written by developers and should represent the majority of the test suite.&lt;/p&gt;




&lt;h3&gt;
  
  
  Service / Integration Tests (Middle Layer)
&lt;/h3&gt;

&lt;p&gt;Service tests validate integration between components, evaluating services in isolation from the user interface.&lt;/p&gt;

&lt;p&gt;Service-level tests fill the gap between unit tests and end-to-end tests. They test the integration of your application with all the parts that live outside of your application (Martin Fowler, &lt;em&gt;The Practical Test Pyramid&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;These tests ensure that components collaborate correctly.&lt;/p&gt;




&lt;h3&gt;
  
  
  End-to-End Tests (Top of the Pyramid)
&lt;/h3&gt;

&lt;p&gt;End-to-end tests validate complete user scenarios by testing the system as a whole.&lt;/p&gt;

&lt;p&gt;End-to-end tests aim to simulate interactions with the system in the same way the end user would.&lt;/p&gt;

&lt;p&gt;Because they are slower and more fragile, they should be used sparingly.&lt;/p&gt;




&lt;h3&gt;
  
  
  Recommended Proportions
&lt;/h3&gt;

&lt;p&gt;A commonly recommended distribution is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;More than 60%&lt;/strong&gt; unit tests
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less than 30%&lt;/strong&gt; service or integration tests
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less than 10%&lt;/strong&gt; end-to-end tests
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Following this structure helps ensure fast and stable tests, while reserving a small number of tests for validating complete user scenarios.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why the Test Pyramid Still Matters
&lt;/h2&gt;

&lt;p&gt;Although simple, the Test Pyramid remains an effective guideline for building efficient and scalable test suites.&lt;/p&gt;

&lt;p&gt;By combining different types of tests and adjusting their quantity at each level, it is possible to obtain comprehensive and efficient test coverage.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Bourque, P., &amp;amp; Fairley, R. (2014). &lt;em&gt;Guide to the Software Engineering Body of Knowledge&lt;/em&gt;. IEEE.
&lt;/li&gt;
&lt;li&gt;Mathur, A. (2013). &lt;em&gt;Foundations of Software Testing&lt;/em&gt;. Pearson.
&lt;/li&gt;
&lt;li&gt;Cohn, M. (2010). &lt;em&gt;Succeeding with Agile: Software Development Using Scrum&lt;/em&gt;. Addison-Wesley.
&lt;/li&gt;
&lt;li&gt;Fowler, M. (2014). &lt;em&gt;The Practical Test Pyramid&lt;/em&gt;. martinfowler.com
&lt;/li&gt;
&lt;li&gt;Sotomayor, B. et al. (2019). &lt;em&gt;Comparison of runtime testing tools for microservices&lt;/em&gt;.
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This article is adapted from Chapter 4 – “Tests in Microservices” of my Master’s thesis, which presents a comparative analysis of testing strategies in monolithic and microservices-based systems.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>architecture</category>
      <category>codequality</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Building a Movie App with Clean Architecture Concepts in React Native</title>
      <dc:creator>Rubem Vasconcelos</dc:creator>
      <pubDate>Tue, 25 Nov 2025 12:30:46 +0000</pubDate>
      <link>https://dev.to/rubemfsv/building-a-movie-app-with-clean-architecture-concepts-in-react-native-6md</link>
      <guid>https://dev.to/rubemfsv/building-a-movie-app-with-clean-architecture-concepts-in-react-native-6md</guid>
      <description>&lt;p&gt;I'm a huge movie fan, and I've decided to combine my passion for cinema with my love for building well-architected systems to practice my React Native skills. That's how &lt;a href="https://github.com/rubemfsv/premiere-night" rel="noopener noreferrer"&gt;Premiere Night&lt;/a&gt; was born.&lt;/p&gt;

&lt;p&gt;I've worked with Clean Architecture before. In fact, I built &lt;a href="https://github.com/rubemfsv/YOUR-OLD-PROJECT-LINK" rel="noopener noreferrer"&gt;another React Native project&lt;/a&gt; with even more layers and complexity, but it was on an older version of React Native. I wanted to challenge myself to apply these same architectural principles to a modern React Native 0.82 setup with the latest tools - in a more simple way.&lt;/p&gt;

&lt;p&gt;But this isn't just a "look at my code" post. I made mistakes, learned valuable lessons, and discovered what actually works (and what's overkill) when building production-ready React Native apps. Whether you're a solo developer or working in a team, I hope my experience helps you make better architectural decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We're Building
&lt;/h2&gt;

&lt;p&gt;Premiere Night is a movie discovery app that lets users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browse popular, top-rated, and upcoming movies&lt;/li&gt;
&lt;li&gt;Search movies in real-time with debouncing (500ms)&lt;/li&gt;
&lt;li&gt;View detailed movie information&lt;/li&gt;
&lt;li&gt;Save favorites to an offline watchlist&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React Native 0.82 (bare workflow, no Expo)&lt;/li&gt;
&lt;li&gt;TypeScript 5.8 with strict mode&lt;/li&gt;
&lt;li&gt;Redux Toolkit 2.10 for state management&lt;/li&gt;
&lt;li&gt;React Navigation 7.x&lt;/li&gt;
&lt;li&gt;AsyncStorage for local persistence&lt;/li&gt;
&lt;li&gt;Jest + Testing Library (289 passing tests)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://appetize.io/app/b_j4bhkef2vdlkq5t7g5n3ltembq" rel="noopener noreferrer"&gt;🎬 Try Android Demo&lt;/a&gt; | &lt;a href="https://appetize.io/app/b_poz4kcawgfmliw3xmutt2m7dkm" rel="noopener noreferrer"&gt;📱 Try iOS Demo&lt;/a&gt; | &lt;a href="https://github.com/rubemfsv/premiere-night" rel="noopener noreferrer"&gt;💻 View Source&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Clean Architecture: Breaking It Down
&lt;/h2&gt;

&lt;p&gt;If you've never worked with Clean Architecture before, don't worry! I'll explain it in a way that actually makes sense (I wish someone had done this for me when I started).&lt;/p&gt;

&lt;p&gt;The core idea is simple: &lt;strong&gt;separate your app into layers where inner layers don't know about outer layers&lt;/strong&gt;. Think of it like a building where the foundation doesn't care about the paint color on the walls.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to dive deeper into Clean Architecture, I've written guides on the topic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/rubemfsv/clean-architecture-the-concept-behind-the-code-52do"&gt;Clean Architecture: The Concept Behind the Code&lt;/a&gt; - Understanding the core principles&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/rubemfsv/clean-architecture-applying-with-react-40h6"&gt;Clean Architecture: Applying with React&lt;/a&gt; - Practical implementation with React&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post focuses specifically on React Native implementation, but those articles will give you the foundational knowledge if you're new to these concepts.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────┐
│       PRESENTATION LAYER                │
│  (UI, Screens, Components, Hooks)       │
└──────────────┬──────────────────────────┘
               ↓
┌─────────────────────────────────────────┐
│          DATA LAYER                     │
│  (Repositories, DTOs, Mappers)          │
└────────┬─────────────────────┬──────────┘
         ↓                     ↓
┌──────────────────┐  ┌──────────────────┐
│  DOMAIN LAYER    │  │  INFRA LAYER     │
│  (Entities,      │  │  (API, Redux,    │
│   Interfaces)    │  │   Storage)       │
└──────────────────┘  └──────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layer Responsibilities
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Domain Layer&lt;/strong&gt; (Pure TypeScript):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Defines business entities (&lt;code&gt;Movie&lt;/code&gt;, &lt;code&gt;MovieDetails&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Defines repository interfaces (&lt;code&gt;IMovieRepository&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Zero external dependencies&lt;/li&gt;
&lt;li&gt;Framework-agnostic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure Layer&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP client (TMDb API)&lt;/li&gt;
&lt;li&gt;Redux store configuration&lt;/li&gt;
&lt;li&gt;State slices (movies, search, watchlist, details)&lt;/li&gt;
&lt;li&gt;AsyncStorage integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Data Layer&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repository implementations (&lt;code&gt;MovieRepositoryImpl&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;DTOs matching API responses&lt;/li&gt;
&lt;li&gt;Mappers transforming DTOs to domain entities&lt;/li&gt;
&lt;li&gt;Data utilities (image URLs, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Presentation Layer&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React components and screens&lt;/li&gt;
&lt;li&gt;Custom hooks for business logic&lt;/li&gt;
&lt;li&gt;Navigation configuration&lt;/li&gt;
&lt;li&gt;UI styles and themes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Layer Deep Dive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Domain Layer: Pure Business Logic
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; Define what the app does, not how.&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;Movie&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;title&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="nl"&gt;overview&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="nl"&gt;posterPath&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="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;releaseDate&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="nl"&gt;voteAverage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IMovieRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;getPopularMovies&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="nx"&gt;MovieListParams&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;PaginatedResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Movie&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="nf"&gt;getMovieDetails&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="nx"&gt;MovieDetailsParams&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;MovieDetails&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;searchMovies&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="nx"&gt;MovieSearchParams&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;PaginatedResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Movie&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No React, no Redux, no external libraries&lt;/li&gt;
&lt;li&gt;Easy to understand business rules&lt;/li&gt;
&lt;li&gt;Reusable across platforms (web, mobile)&lt;/li&gt;
&lt;li&gt;Perfect for documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Infrastructure Layer: External Systems
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; Handle communication with external dependencies.&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;class&lt;/span&gt; &lt;span class="nc"&gt;ApiClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;baseURL&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="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;apiKey&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;get&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;endpoint&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;params&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;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="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="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buildUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;endpoint&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&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;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`API Error: &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="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApiClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why Redux Toolkit?&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Excellent TypeScript support with full type inference&lt;/li&gt;
&lt;li&gt;Less boilerplate (no action constants, simplified reducers)&lt;/li&gt;
&lt;li&gt;Built-in thunk middleware for async actions&lt;/li&gt;
&lt;li&gt;Immer integration for immutable updates&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3. Data Layer: Transformation Hub
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; Transform external data to domain models.&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;IMovieRepository&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;@domain/repositories/MovieRepository&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;apiClient&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;@infra/api/ApiClient&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MovieMapper&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;../mappers/MovieMapper&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MovieRepositoryImpl&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;IMovieRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getPopularMovies&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="nx"&gt;MovieListParams&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;PaginatedResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Movie&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="c1"&gt;// 1. Fetch from API&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&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;apiClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PaginatedResponseDTO&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MovieDTO&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;movie/popular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;page&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="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;language&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="nx"&gt;language&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&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;// 2. Transform DTO to Domain&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;MovieMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toPaginatedDomain&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;MovieMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toDomain&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;movieRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MovieRepositoryImpl&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Mapper Pattern:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MovieMapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;toDomain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MovieDTO&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Movie&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dto&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;overview&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;overview&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;posterPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;poster_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// snake_case → camelCase&lt;/span&gt;
      &lt;span class="na"&gt;releaseDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;release_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;voteAverage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vote_average&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;&lt;strong&gt;Why Mappers?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API contracts change, domain models don't&lt;/li&gt;
&lt;li&gt;Centralized transformation logic&lt;/li&gt;
&lt;li&gt;Easy to test in isolation&lt;/li&gt;
&lt;li&gt;Type-safe conversions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Presentation Layer: User Interface
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Custom Hooks Pattern:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useAppDispatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useAppSelector&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;@infra/store/hooks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fetchPopularMovies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;selectMoviesState&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;@infra/store/movies&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useMovies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAppDispatch&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;moviesState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAppSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectMoviesState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&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="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetchPopularMovies&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dispatch&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;loadMore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&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="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetchPopularMovies&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
      &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;moviesState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;popular&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="na"&gt;append&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moviesState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;popular&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;moviesState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;popular&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="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;moviesState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;popular&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;moviesState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;popular&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;loadMore&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;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Components focus solely on rendering&lt;/li&gt;
&lt;li&gt;Business logic is reusable and testable&lt;/li&gt;
&lt;li&gt;Easy to mock in tests&lt;/li&gt;
&lt;li&gt;Hooks can be tested independently of UI&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Implementing a Feature: Movie Search
&lt;/h2&gt;

&lt;p&gt;Let's implement the search feature step-by-step to see the architecture in action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Define Domain Contract
&lt;/h3&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;MovieSearchParams&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;query&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="nl"&gt;page&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;language&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="nl"&gt;year&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IMovieRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;searchMovies&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="nx"&gt;MovieSearchParams&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;PaginatedResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Movie&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Implement Repository
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;searchMovies&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="nx"&gt;MovieSearchParams&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;PaginatedResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Movie&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;response&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;apiClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PaginatedResponseDTO&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MovieDTO&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;search/movie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;query&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="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;page&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="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;language&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="nx"&gt;language&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;year&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="nx"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;MovieMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toPaginatedDomain&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;MovieMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toDomain&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;
  
  
  Step 3: Create Redux Slice
&lt;/h3&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;searchSlice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSlice&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="na"&gt;loading&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="na"&gt;hasSearched&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;span class="na"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;setSearchQuery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PayloadAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;clearSearch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasSearched&lt;/span&gt; &lt;span class="o"&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;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;extraReducers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;builder&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;builder&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchMovies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchMovies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fulfilled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasSearched&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="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;
  
  
  Step 4: Create Custom Hook with Debounce
&lt;/h3&gt;

&lt;p&gt;This is where the magic happens - debouncing reduces API calls by ~80%.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useMovieSearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;debounceTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&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="nx"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAppDispatch&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;searchState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAppSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectSearchState&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;localQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLocalQuery&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;debounceTimer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleQueryChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;query&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setLocalQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Immediate UI update&lt;/span&gt;
    &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setSearchQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&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;debounceTimer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;debounceTimer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&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="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;clearSearch&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="c1"&gt;// Debounced API call&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;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;debounceTimer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&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="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;searchMovies&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;debounceTime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;debounceTime&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&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;return &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;debounceTimer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;debounceTimer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;localQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;searchState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;searchState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;onQueryChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;handleQueryChange&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;&lt;strong&gt;Why Debounce?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduces API calls by ~80% during typing&lt;/li&gt;
&lt;li&gt;Better performance and UX&lt;/li&gt;
&lt;li&gt;Respects TMDb API rate limits&lt;/li&gt;
&lt;li&gt;Immediate visual feedback, delayed API call&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5: Use in Component
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HomeScreen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onQueryChange&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMovieSearch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SearchBar&lt;/span&gt; 
        &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;onChangeText&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onQueryChange&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Search movies...&lt;/span&gt;&lt;span class="dl"&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;loading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ActivityIndicator&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SearchResults&lt;/span&gt; &lt;span class="nx"&gt;movies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/View&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why This Architecture Changed My Life (Okay, My Codebase)
&lt;/h2&gt;

&lt;p&gt;Let me be honest, when I started, I thought "this is way too much work". But after finishing the project, I can clearly see the benefits. Here's what I gained:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Testing Became More Enjoyable
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Each layer can be tested independently, which means:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No more "change one thing, break everything" moments&lt;/li&gt;
&lt;li&gt;Tests run faster because you're not booting up the whole app&lt;/li&gt;
&lt;li&gt;99.77% coverage wasn't painful—it was achievable!
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Test mapper without API&lt;/span&gt;
&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MovieMapper&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should map DTO to domain&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="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="na"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MovieDTO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test Movie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;poster_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/test.jpg&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="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;MovieMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDomain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&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="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test Movie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;posterPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/test.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Test repository with mocked API&lt;/span&gt;
&lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@infra/api/ApiClient&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MovieRepositoryImpl&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should fetch popular movies&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="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="nx"&gt;mockResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="cm"&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;apiClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mockResolvedValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockResponse&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;movieRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPopularMovies&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiClient&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="nf"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;movie/popular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nf"&gt;expect&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="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Test hook without rendering UI&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;result&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderHook&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;useMovieSearch&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Provider&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;act&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onQueryChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Avatar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitFor&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="nf"&gt;expect&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="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&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;p&gt;&lt;strong&gt;Test Distribution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit tests: ~180 (Mappers, utils, reducers)&lt;/li&gt;
&lt;li&gt;Integration tests: ~60 (Redux slices with thunks)&lt;/li&gt;
&lt;li&gt;Component tests: ~49 (React components and hooks)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Maintainability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clear boundaries:&lt;/strong&gt; Each layer has a single responsibility&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy to find code:&lt;/strong&gt; Predictable folder structure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type-safe refactoring:&lt;/strong&gt; TypeScript catches breaking changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-documenting:&lt;/strong&gt; Interfaces serve as documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Flexibility
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Want to switch from Redux to Zustand?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only change the Infra layer&lt;/li&gt;
&lt;li&gt;Domain and Data layers stay the same&lt;/li&gt;
&lt;li&gt;Components use the same custom hooks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Want to add GraphQL?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create new repository implementation&lt;/li&gt;
&lt;li&gt;Keep domain entities unchanged&lt;/li&gt;
&lt;li&gt;Swap implementations via dependency injection&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Scalability
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Adding new features is straightforward:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define domain contract&lt;/li&gt;
&lt;li&gt;Implement repository&lt;/li&gt;
&lt;li&gt;Create Redux slice (if needed)&lt;/li&gt;
&lt;li&gt;Build UI components&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each step is independent and testable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Let's Talk About The Elephant in The Room
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is this overkill?&lt;/strong&gt; Sometimes, yes. Let me be brutally honest about the downsides:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. You'll Create A LOT of Files
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Real talk:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I spent 30% more time on setup than a simpler approach&lt;/li&gt;
&lt;li&gt;For every feature, I created DTOs, Mappers, Interfaces, Tests...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;But, in my opinion, here's when it's worth it:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Medium to large projects&lt;/li&gt;
&lt;li&gt;Team of 2+ developers&lt;/li&gt;
&lt;li&gt;Long-term maintenance expected&lt;/li&gt;
&lt;li&gt;High test coverage required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When It's Overkill:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quick prototypes&lt;/li&gt;
&lt;li&gt;Simple CRUD apps&lt;/li&gt;
&lt;li&gt;Solo weekend projects&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Learning Curve
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Team members need to understand:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layer boundaries and dependencies&lt;/li&gt;
&lt;li&gt;Repository pattern&lt;/li&gt;
&lt;li&gt;DTO vs Domain entities&lt;/li&gt;
&lt;li&gt;When to create new abstractions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Good documentation (like ARCHITECTURE.md)&lt;/li&gt;
&lt;li&gt;Code reviews to enforce patterns&lt;/li&gt;
&lt;li&gt;Pair programming for onboarding&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Initial Overhead
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;First feature takes longer:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up folder structure&lt;/li&gt;
&lt;li&gt;Configure TypeScript path aliases&lt;/li&gt;
&lt;li&gt;Create base interfaces&lt;/li&gt;
&lt;li&gt;Set up testing infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;But subsequent features are faster:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy-paste and adapt existing patterns&lt;/li&gt;
&lt;li&gt;Reuse mappers, utilities, components&lt;/li&gt;
&lt;li&gt;Clear guidelines reduce decision fatigue&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Concepts Explained
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Repository Pattern
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What:&lt;/strong&gt; Interface that abstracts data access logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decouple business logic from data source&lt;/li&gt;
&lt;li&gt;Easy to mock in tests&lt;/li&gt;
&lt;li&gt;Can swap implementations (REST → GraphQL)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Domain defines what we need&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IMovieRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;getPopularMovies&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;Movie&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="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Data implements how we get it&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MovieRepositoryImpl&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;IMovieRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getPopularMovies&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;Movie&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="nx"&gt;response&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;apiClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;movie/popular&lt;/span&gt;&lt;span class="dl"&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MovieMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toDomain&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;// Tests can use a fake implementation&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FakeMovieRepository&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;IMovieRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getPopularMovies&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;Movie&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;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test Movie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&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;
  
  
  DTO (Data Transfer Object)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What:&lt;/strong&gt; Object that matches external API shape.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API uses snake_case, we use camelCase&lt;/li&gt;
&lt;li&gt;API might change, our domain shouldn't&lt;/li&gt;
&lt;li&gt;Separates external contract from internal model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// DTO: Matches API response&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;MovieDTO&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;title&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="nl"&gt;poster_path&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="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// snake_case from API&lt;/span&gt;
  &lt;span class="nl"&gt;vote_average&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Domain: Our internal model&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Movie&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;title&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="nl"&gt;posterPath&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="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// camelCase for TypeScript&lt;/span&gt;
  &lt;span class="nl"&gt;voteAverage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Mapper bridges the gap&lt;/span&gt;
&lt;span class="nx"&gt;MovieMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDomain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Redux Slice Pattern
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What:&lt;/strong&gt; Co-locate all Redux logic for a feature in one place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structure:&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;store/movies/
├── slice.ts       # Reducers
├── thunks.ts      # Async actions
├── selectors.ts   # State queries
├── initialState.ts
└── tests/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All related code in one place&lt;/li&gt;
&lt;li&gt;Easy to find and modify&lt;/li&gt;
&lt;li&gt;Clear feature boundaries&lt;/li&gt;
&lt;li&gt;Simple to test&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Custom Hooks for Business Logic
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What:&lt;/strong&gt; Extract component logic into reusable hooks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Components focus on rendering&lt;/li&gt;
&lt;li&gt;Logic is reusable across components&lt;/li&gt;
&lt;li&gt;Easy to test without mounting components&lt;/li&gt;
&lt;li&gt;Better separation of concerns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Hook handles business logic&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useMovies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAppDispatch&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;movies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAppSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectPopularMovies&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&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="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetchPopularMovies&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;movies&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Component just renders&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MovieList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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;movies&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMovies&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FlatList&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="nx"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Results: By The Numbers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;99.77% Statement Coverage&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;98.41% Branch Coverage&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;100% Function Coverage&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;289 Passing Tests&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;4 Clear Layers&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0 Circular Dependencies&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Questions You Might Be Asking
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Q: "Is this overkill for my 5-screen app?"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Yes, probably. For small projects (&amp;lt; 10 screens), consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple folder structure (components, screens, utils)&lt;/li&gt;
&lt;li&gt;React Query instead of Redux&lt;/li&gt;
&lt;li&gt;Fewer abstractions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use Clean Architecture when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Team size &amp;gt; 2&lt;/li&gt;
&lt;li&gt;Expected lifetime &amp;gt; 6 months&lt;/li&gt;
&lt;li&gt;Complexity will grow&lt;/li&gt;
&lt;li&gt;High test coverage required&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Q: "Does this work with Expo?"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Absolutely! I used bare React Native, but the architecture doesn't care. Just:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Swap &lt;code&gt;@react-native-async-storage&lt;/code&gt; with &lt;code&gt;expo-secure-store&lt;/code&gt; if you want&lt;/li&gt;
&lt;li&gt;Use Expo modules wherever you need them&lt;/li&gt;
&lt;li&gt;Keep the same layer structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The beauty of Clean Architecture is that it's framework-agnostic!&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: "How do you manage API keys securely?"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; I use &lt;code&gt;react-native-dotenv&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;TMDB_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TMDB_BASE_URL&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;@env&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApiClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TMDB_BASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TMDB_API_KEY&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;
  
  
  When to Use This Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Great For:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Production apps&lt;/li&gt;
&lt;li&gt;Team projects&lt;/li&gt;
&lt;li&gt;Apps with complex business logic&lt;/li&gt;
&lt;li&gt;High test coverage requirements&lt;/li&gt;
&lt;li&gt;Long-term maintenance&lt;/li&gt;
&lt;li&gt;Multiple data sources&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Overkill For:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Quick prototypes&lt;/li&gt;
&lt;li&gt;Solo weekend projects&lt;/li&gt;
&lt;li&gt;Simple CRUD apps&lt;/li&gt;
&lt;li&gt;Learning projects&lt;/li&gt;
&lt;li&gt;MVPs that might be thrown away&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building this app showed me that Clean Architecture isn’t about achieving perfection — it’s about making deliberate, well-reasoned trade-offs. What initially felt overwhelming gradually revealed its value. The test suite reached 99.77% coverage and, instead of feeling unattainable, it became a natural part of the workflow. Adding new features turned into a predictable, almost effortless process once the foundation was in place. Most importantly, refactoring stopped being risky—the architecture and tests consistently supported every change.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Premiere Night Project:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rubemfsv/premiere-night" rel="noopener noreferrer"&gt;Source Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appetize.io/app/b_j4bhkef2vdlkq5t7g5n3ltembq" rel="noopener noreferrer"&gt;Android Demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://appetize.io/app/b_poz4kcawgfmliw3xmutt2m7dkm" rel="noopener noreferrer"&gt;iOS Demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Learn More About Clean Architecture:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/rubemfsv/clean-architecture-the-concept-behind-the-code-52do"&gt;Clean Architecture: The Concept Behind the Code&lt;/a&gt; - Core principles and theory&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/rubemfsv/clean-architecture-applying-with-react-40h6"&gt;Clean Architecture: Applying with React&lt;/a&gt; - Practical React implementation&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>reactnative</category>
      <category>typescript</category>
      <category>architecture</category>
      <category>cleancode</category>
    </item>
    <item>
      <title>PixiJS: Implementing Core Gaming Concepts</title>
      <dc:creator>Rubem Vasconcelos</dc:creator>
      <pubDate>Mon, 18 Aug 2025 01:02:59 +0000</pubDate>
      <link>https://dev.to/rubemfsv/pixijs-implementing-core-gaming-concepts-438j</link>
      <guid>https://dev.to/rubemfsv/pixijs-implementing-core-gaming-concepts-438j</guid>
      <description>&lt;p&gt;As a software engineer stepping into game development for the first time (with only some exposure to graphics programming during my bachelor’s degree using Java and OpenGL), I recently began exploring PixiJS to explore fundamental gaming concepts. In this article, I’ll walk through the implementation of three interactive systems that demonstrate sprite management, custom text rendering, and particle effects optimization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://game-hub-blue-rho.vercel.app/" rel="noopener noreferrer"&gt;Live Demo&lt;/a&gt;&lt;/strong&gt; | &lt;strong&gt;&lt;a href="https://github.com/rubemfsv/play-central/" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is PixiJS?
&lt;/h2&gt;

&lt;p&gt;PixiJS is a JavaScript library that helps you create interactive 2D graphics in web browsers. If you've worked with HTML Canvas or D3.js for data visualization, think of PixiJS as a more powerful, performance-focused alternative.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Performance Story: CPU vs GPU
&lt;/h3&gt;

&lt;p&gt;Traditional web graphics (HTML Canvas, SVG) use your computer's &lt;strong&gt;CPU&lt;/strong&gt; to draw everything. This works fine for simple graphics, but becomes slow with complex animations or many visual elements.&lt;/p&gt;

&lt;p&gt;PixiJS uses your computer's &lt;strong&gt;GPU&lt;/strong&gt; (Graphics Processing Unit) instead. The GPU is designed specifically for graphics operations and can handle thousands of calculations simultaneously. This is why video games and video editing software are so smooth - they leverage GPU power.&lt;/p&gt;

&lt;p&gt;In practical terms: Where Canvas might struggle with 100 moving elements, PixiJS can smoothly handle 1000+ elements at 60fps.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's a "Sprite"?
&lt;/h3&gt;

&lt;p&gt;In traditional web development, you think in terms of DOM elements (&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;). In PixiJS, everything visual is called a &lt;strong&gt;"sprite"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of a sprite as a lightweight visual object, like a DOM element, but optimized for graphics:&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;// Instead of: &amp;lt;img src="player.png" style="left: 100px; top: 200px;"&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// You have:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;playerSprite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sprite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;player.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;playerSprite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;playerSprite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Types of sprites you'll work with:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Image sprites: Pictures (like &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags)&lt;/li&gt;
&lt;li&gt;Text sprites: Rendered text (like &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Graphics sprites: Drawn shapes (like CSS borders or SVG shapes)&lt;/li&gt;
&lt;li&gt;Container sprites: Groups of other sprites (like &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; containers)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Display Tree for Graphics
&lt;/h3&gt;

&lt;p&gt;Just like HTML has a DOM tree structure, PixiJS has a display tree:&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;// HTML equivalent:&lt;/span&gt;
&lt;span class="c1"&gt;// &amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;//   &amp;lt;div class="game-container"&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;//     &amp;lt;img src="player.png"&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;//     &amp;lt;p&amp;gt;Score: 100&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;//   &amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// &amp;lt;/body&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// PixiJS equivalent:&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;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;           &lt;span class="c1"&gt;// Like &amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gameContainer&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;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// Like &amp;lt;div&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;playerSprite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sprite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;player.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Like &amp;lt;img&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scoreText&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;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Score: 100&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;       &lt;span class="c1"&gt;// Like &amp;lt;p&amp;gt;&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;stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gameContainer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;gameContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;playerSprite&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;gameContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key advantage&lt;/strong&gt;: Moving the container moves everything inside it, just like CSS transforms on parent elements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Choose PixiJS nowadays?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Performance first: PixiJS automatically uses WebGL when available, providing hardware-accelerated rendering that can handle thousands of sprites at 60fps.&lt;/li&gt;
&lt;li&gt;Developer experience: The API is designed to be intuitive and well-documented. Coming from web development, the learning curve is gentle because you're working with familiar concepts like containers, event listeners, and asset loading.&lt;/li&gt;
&lt;li&gt;Ecosystem and tooling:

&lt;ul&gt;
&lt;li&gt;TypeScript support: First-class TypeScript definitions&lt;/li&gt;
&lt;li&gt;Asset pipeline: Built-in asset loader with support for atlases, fonts, and various image formats&lt;/li&gt;
&lt;li&gt;Plugin system: Extensive plugin ecosystem for filters, particles, and advanced features&lt;/li&gt;
&lt;li&gt;Framework agnostic: Works with React, Vue, Angular, or vanilla JavaScript&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Cross-platform ready: PixiJS applications run anywhere modern browsers do, and no additional compilation or platform-specific code is required.&lt;/li&gt;

&lt;li&gt;Mobile optimized: Built-in support for high-DPI displays, touch events, and mobile-specific optimizations make it production-ready for mobile games and applications.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Project Architecture Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Play Central&lt;/strong&gt; demonstrates practical applications of PixiJS through three focused implementations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ace of Shadows&lt;/strong&gt; - Automated card dealing system with 144 sprites&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Magic Words&lt;/strong&gt; - Visual novel-style dialogue system with custom emoji support
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phoenix Flame&lt;/strong&gt; - Performance-constrained particle fire effect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each system addresses different aspects of game development while maintaining consistent architectural principles throughout the codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
├── scenes/
│   ├── BaseScene.ts
│   └── game/
│       ├── ace-of-shadows/
│       │   ├── AceOfShadowsScene.ts
│       │   ├── Card.ts
│       │   └── Deck.ts
│       ├── magic-words/
│       │   ├── MagicWordsScene.ts
│       │   ├── DialogueBox.ts
│       │   ├── AvatarBox.ts
│       │   ├── ResourceManager.ts
│       │   └── types.ts
│       └── phoenix-flame/
│           ├── PhoenixFlameScene.ts
│           └── Particle.ts
├── core/
├── infra/
└── ui/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Implementation 1: Ace of Shadows - Automated Card Dealing System
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Objective:&lt;/strong&gt; Create 144 sprites stacked like cards with automated movement between piles at 1-second intervals, with 2-second animation duration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scene Architecture and Separation of Concerns
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;AceOfShadowsScene&lt;/code&gt; implements clean separation using a dedicated &lt;code&gt;Deck&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AceOfShadowsScene&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseScene&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;deck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Deck&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sprite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;discardPile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;dealInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NodeJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Timeout&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ace of Shadows&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sprite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;BACKGROUND_IMAGE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChildAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Deck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getApp&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;discardPile&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;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;discardPile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startDealing&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;&lt;strong&gt;Key Design Decisions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Delegation pattern: Card logic encapsulated in &lt;code&gt;Deck&lt;/code&gt; class rather than scene management&lt;/li&gt;
&lt;li&gt;Automatic termination: Interval stops when deck is empty, preventing unnecessary processing&lt;/li&gt;
&lt;li&gt;Clean resource management: Proper interval cleanup in the destroy method&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance-First Texture Management
&lt;/h3&gt;

&lt;p&gt;The most crucial lesson was understanding sprite vs graphics performance. Instead of 144 individual graphics objects, I generated one reusable texture:&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;private&lt;/span&gt; &lt;span class="nf"&gt;createCardTexture&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Texture&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;graphics&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;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Graphics&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginFill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xffffff&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawRoundedRect&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CARD_WIDTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CARD_HEIGHT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endFill&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;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateTexture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;createDeck&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;cardTexture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCardTexture&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;NUM_CARDS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cardTexture&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;STAGGER_OFFSET&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Visual stacking effect&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;cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;card&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;
  
  
  Timer-Based Animation System with GSAP
&lt;/h3&gt;

&lt;p&gt;The implementation uses interval-based timing with GSAP (a powerful JavaScript library used for creating high-performance animations on the web) for smooth 2-second animations:&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;private&lt;/span&gt; &lt;span class="nf"&gt;startDealing&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dealInterval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&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="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasCards&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dealInterval&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dealInterval&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;moveTopCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;discardPile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 1-second intervals&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;moveTopCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;discardContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="k"&gt;return&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;card&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worldPos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;discardContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toGlobal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Point&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;localPos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLocal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;worldPos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;gsap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;localPos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;localPos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;discardPile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;STAGGER_OFFSET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 2-second animation requirement&lt;/span&gt;
    &lt;span class="na"&gt;onComplete&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="nx"&gt;discardContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;card&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;
  
  
  Implementation 2: Magic Words - Visual Novel Dialogue System
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Objective:&lt;/strong&gt; Create a system combining text and images for RPG-style dialogue using external API data with custom emoji support.&lt;/p&gt;

&lt;h3&gt;
  
  
  Asynchronous Resource Loading Architecture
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;MagicWordsScene&lt;/code&gt; implements a loading coordination:&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;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;loadGameData&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="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="nf"&gt;fetchMagicWordsData&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nx"&gt;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Assets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;BACKGROUND_IMAGE&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&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;resourceManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ResourceManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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;emojiTextures&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;avatarSprites&lt;/span&gt; &lt;span class="p"&gt;}&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;resourceManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadAssets&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emojiTextures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;emojiTextures&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avatarSprites&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;avatarSprites&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avatarSprites&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sprite&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sprite&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initializeScene&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to load game 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;error&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;&lt;strong&gt;Architecture Highlights:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parallel loading: Background and API data load simultaneously using &lt;code&gt;Promise.all&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Resource manager pattern: Dedicated class handles emoji and avatar asset loading&lt;/li&gt;
&lt;li&gt;Error handling: Proper try-catch with error reporting&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Rich Text Parsing with Inline Emojis
&lt;/h3&gt;

&lt;p&gt;The most complex challenge was parsing text with emoji placeholders like &lt;code&gt;{smile}&lt;/code&gt; and rendering them inline. Imagine you need to display text like &lt;code&gt;"Hello {smile} welcome to our game {fire}"&lt;/code&gt; where &lt;code&gt;{smile}&lt;/code&gt; and &lt;code&gt;{fire}&lt;/code&gt; should be replaced with actual emoji images, not text.&lt;/p&gt;

&lt;p&gt;In HTML, you might use something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hello &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"smile.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; welcome to our game &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"fire.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike HTML, PixiJS doesn’t provide markup-based layout. All elements must be positioned manually. To solve this, we used regex to split the text, identify each part’s type, and then applied custom layout logic to position them individually.&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;private&lt;/span&gt; &lt;span class="nf"&gt;updateContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;text&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;emojiTextures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Texture&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;wordWrapWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChildren&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentX&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentY&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FONT_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&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;LINE_HEIGHT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FONT_SIZE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.4&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;EMOJI_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FONT_SIZE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.2&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;SPACE_WIDTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&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;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(\{[^&lt;/span&gt;&lt;span class="sr"&gt;}&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\})&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&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;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;part&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="nx"&gt;emojiMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\{([^&lt;/span&gt;&lt;span class="sr"&gt;}&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)\}&lt;/span&gt;&lt;span class="sr"&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;emojiMatch&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;emojiTextures&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;emojiMatch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emojiName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;emojiMatch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&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;currentX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;EMOJI_SIZE&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;wordWrapWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;currentX&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="nx"&gt;currentY&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;LINE_HEIGHT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emojiSprite&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;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Sprite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emojiTextures&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;emojiName&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="nx"&gt;emojiSprite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EMOJI_SIZE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;emojiSprite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EMOJI_SIZE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;emojiSprite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LINE_HEIGHT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;EMOJI_SIZE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;emojiSprite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emojiSprite&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;currentX&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;emojiSprite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;SPACE_WIDTH&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Handle text with word wrapping&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;word&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;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="k"&gt;return&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;textObject&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;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FONT_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TEXT_COLOR&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="nx"&gt;currentX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;textObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;wordWrapWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;currentX&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="nx"&gt;currentY&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;LINE_HEIGHT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;textObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;currentX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;currentY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LINE_HEIGHT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;textObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;currentX&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;textObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;SPACE_WIDTH&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Implementation 3: Phoenix Flame - Performance-Optimized Particle System
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Objective:&lt;/strong&gt; Create a fire effect within a strict 10-sprite performance constraint.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's a "Particle" in Graphics Programming?
&lt;/h3&gt;

&lt;p&gt;Imagine you want to create a campfire effect. In real life, fire is made of thousands of tiny flames, sparks, and embers, each moving independently.&lt;/p&gt;

&lt;p&gt;In graphics programming, we simulate this with particles, small sprites that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spawn continuously (like sparks from a fire)&lt;/li&gt;
&lt;li&gt;Move with physics (rise up, drift sideways)&lt;/li&gt;
&lt;li&gt;Change over time (fade out, shrink, change color)&lt;/li&gt;
&lt;li&gt;Die and get recycled (disappear when their "life" expires)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Each particle is just a sprite with extra properties&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Particle&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sprite&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;vx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// Horizontal velocity&lt;/span&gt;
  &lt;span class="nl"&gt;vy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// Vertical velocity  &lt;/span&gt;
  &lt;span class="nl"&gt;life&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// How long it lives&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most challenging thing was creating a realistic fire limited to just 10 sprites maximum. But, with object pooling, we could reuse the same 10 sprites over and over, making them "die" and "respawn" so quickly that it looks like continuous fire.&lt;/p&gt;

&lt;h3&gt;
  
  
  Object Pooling Pattern Implementation
&lt;/h3&gt;

&lt;p&gt;Instead of creating and destroying particles constantly (that is expensive), we create 10 particles once and reuse them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PhoenixFlameScene&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseScene&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;particles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Particle&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="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;particleContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ParticleContainer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;flameTextures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Texture&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="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Phoenix Flame&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;particleContainer&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;PIXI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ParticleContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;scale&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;position&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;alpha&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;particleContainer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;emitParticle&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;particle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;particles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visible&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;particle&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="c1"&gt;// Pool exhausted&lt;/span&gt;

    &lt;span class="c1"&gt;// Reset particle properties&lt;/span&gt;
    &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copyFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emitter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;texture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flameTextures&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flameIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flameIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flameIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flameTextures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visible&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="c1"&gt;// Set physics properties&lt;/span&gt;
    &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;life&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;50&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;
  
  
  Physics Simulation and Lifecycle Management
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emitParticle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;particles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Particle&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visible&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="c1"&gt;// Apply velocity and gravity&lt;/span&gt;
    &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vy&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Gravity effect&lt;/span&gt;

    &lt;span class="c1"&gt;// Age the particle&lt;/span&gt;
    &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;life&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&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="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;life&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.99&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Shrink over time&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;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;life&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Return to pool&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;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;These implementations demonstrate practical application of core PixiJS concepts through real-world constraints and requirements. The Ace of Shadows system showcases efficient sprite management and timer-based animations, Magic Words illustrates complex resource loading and dynamic UI composition, while Phoenix Flame demonstrates performance optimization through object pooling.&lt;/p&gt;

&lt;p&gt;The clean separation of concerns, proper TypeScript integration, and performance-conscious design patterns provide a solid foundation for more complex interactive applications. Each system addresses different aspects of game development while maintaining consistent architectural principles throughout the codebase.&lt;/p&gt;

&lt;p&gt;PixiJS proved to be an excellent choice for learning game development concepts, offering the perfect balance of power and simplicity. The modular approach and emphasis on resource management create maintainable, scalable solutions that effectively demonstrate PixiJS capabilities for production-quality interactive applications.&lt;/p&gt;

&lt;p&gt;In game development, performance isn’t something to optimize later; it has to be a priority from the start. Unlike traditional web development, where techniques like lazy loading or bundle splitting can be introduced later, games demand smoothness and consistency at every frame. That means carefully managing memory to avoid garbage collection spikes, reusing textures and sprites whenever possible, and relying on specialized structures such as object pooling or optimized containers like ParticleContainer. These aren’t optional enhancements, they’re essential practices for ensuring a stable frame rate and delivering a responsive, enjoyable player experience.&lt;/p&gt;

&lt;p&gt;Whether you're building a simple card game, a visual novel, or an action-packed adventure, these patterns and techniques provide a strong foundation for bringing your ideas to life in the browser.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pixijs.download/dev/docs/index.html" rel="noopener noreferrer"&gt;PixiJS Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://greensock.com/gsap/" rel="noopener noreferrer"&gt;GSAP Animation Library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gameprogrammingpatterns.com/" rel="noopener noreferrer"&gt;Game Programming Patterns&lt;/a&gt; - Object pooling and other essential patterns&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.typescriptlang.org/docs/" rel="noopener noreferrer"&gt;TypeScript Handbook&lt;/a&gt; - For better PixiJS integration&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>pixijs</category>
      <category>gamedev</category>
      <category>frontend</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Configure Aplicações React Usando Arquitetura Limpa com Apenas Um Comando</title>
      <dc:creator>Rubem Vasconcelos</dc:creator>
      <pubDate>Sun, 31 Jul 2022 15:36:23 +0000</pubDate>
      <link>https://dev.to/rubemfsv/configure-aplicacoes-react-usando-arquitetura-limpa-com-apenas-um-comando-2m9k</link>
      <guid>https://dev.to/rubemfsv/configure-aplicacoes-react-usando-arquitetura-limpa-com-apenas-um-comando-2m9k</guid>
      <description>&lt;p&gt;Crie aplicações React usando &lt;a href="https://dev.to/rubemfsv/clean-architecture-the-concept-behind-the-code-52do"&gt;Arquitetura Limpa&lt;/a&gt; sem precisar configurar nada.&lt;/p&gt;

&lt;p&gt;É como usar o comando create-react-app, mas mais eficiente e mais fácil de escalar. O objetivo é ajudar a ganhar tempo durante o desenvolvimento. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/rubemfsv/arquitetura-limpa-aplicando-com-react-1eo0"&gt;Guia do usuário&lt;/a&gt; – Como desenvolver aplicativos criados com Clean React App.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clean React App funciona em macOS, Windows e Linux.&lt;br&gt;&lt;br&gt;
Se algo não funcionar, &lt;a href="https://github.com/rubemfsv/clean-react-app/issues/new" rel="noopener noreferrer"&gt;registre um problema&lt;/a&gt; no repositório.&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Visão geral rápida
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @rubemfsv/clean-react-app my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
npm start or npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feito. Tudo está configurado e pronto para ser executado!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Este boilerplate contém as seguintes configurações:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adapter para o Local storage;&lt;/li&gt;
&lt;li&gt;Axios como cliente HTTP;&lt;/li&gt;
&lt;li&gt;Webpack configurado para ambientes de desenvolvimento e produção;&lt;/li&gt;
&lt;li&gt;Testes end-to-end básicos configurados com Cypress;&lt;/li&gt;
&lt;li&gt;Testes unitários com Jest;&lt;/li&gt;
&lt;li&gt;Husky com pre-push configurado para rodar os testes unitários;&lt;/li&gt;
&lt;li&gt;Autenticação com validações;&lt;/li&gt;
&lt;li&gt;Camada de validação para reuso das validações;&lt;/li&gt;
&lt;li&gt;Alguns hooks para ajudar com as chamadas da API e submissões de formulários;&lt;/li&gt;
&lt;li&gt;Configurações para rotas privadas;&lt;/li&gt;
&lt;li&gt;Três páginas para ajudar a melhorar a produtividade:

&lt;ul&gt;
&lt;li&gt;Página de Login&lt;/li&gt;
&lt;li&gt;Página de Sign up&lt;/li&gt;
&lt;li&gt;Página inicial&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  É open source. Sintam-se livres para contribuir!
&lt;/h2&gt;

&lt;p&gt;Link: &lt;a href="https://www.npmjs.com/package/@rubemfsv/clean-react-app" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@rubemfsv/clean-react-app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>braziliandevs</category>
      <category>opensource</category>
      <category>react</category>
    </item>
    <item>
      <title>Clean Architecture: Applying with React</title>
      <dc:creator>Rubem Vasconcelos</dc:creator>
      <pubDate>Sun, 24 Jul 2022 12:54:55 +0000</pubDate>
      <link>https://dev.to/rubemfsv/clean-architecture-applying-with-react-40h6</link>
      <guid>https://dev.to/rubemfsv/clean-architecture-applying-with-react-40h6</guid>
      <description>&lt;p&gt;This text is part of a series of texts about &lt;a href="https://dev.to/rubemfsv/clean-architecture-the-concept-behind-the-code-52do"&gt;Clean Architecture&lt;/a&gt; analysis applied with different frameworks and languages.&lt;/p&gt;

&lt;p&gt;The purposes of this text are in line with those of the previous text, which are: I. Show an architectural division of a React application using Clean Architecture; II. Guide the implementation of new features in this proposed architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architectural Division
&lt;/h2&gt;

&lt;p&gt;The initial step is to analyze how the division is performed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cypress/
src/
  data/
    protocols/
    test/
    usecases/
  domain/
    errors/
    models/
    test/
    usecases/
  infra/
    cache/
    http/
    test/
  main/
    adapters/
    config/
    decorators/
    factories/
      cache/
      decorators/
      http/
      pages/
      usecases/
    routes/
    scripts/
    index.tsx
  presentation/
    assets/
    components/
    hooks/
    pages/
    protocols/
    routes/
    styles/
    test/
  requirements/
  validation/
    errors/
    protocols/
    test/
    validators/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In detail, the purpose of each file structure is the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;cypress&lt;/strong&gt;: It contains the application's end-to-end test files (for large projects, this folder is recommended to be in a separate project, so that the team responsible for e2e tests can take care of it, as they do not need to know the project code).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;src&lt;/strong&gt;: Contains all files needed for the application.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data&lt;/strong&gt;: The data folder represents the data layer of the Clean Architecture, being dependent on the domain layer. Contains the implementations of business rules that are declared in &lt;em&gt;domain&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain&lt;/strong&gt;: Represents the domain layer of the Clean Architecture, the innermost layer of the application, not having any dependency on any other layer, where it contains the business rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infra&lt;/strong&gt;: This folder contains the implementations referring to the HTTP protocol and the cache, it is also the only place where you will have access to external dependencies related to these two items mentioned. This folder also contains most of the external libraries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main&lt;/strong&gt;: It corresponds to the main layer of the application, where the interfaces developed in the presentation layer are integrated with the business rules created in the folders that represent the innermost layers of the Clean Architecture. All this is due to the use of design patterns such as Factory Method, Composite and Builder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Presentation&lt;/strong&gt;: This folder contains the visual part of the application, with its pages, components, &lt;em&gt;hooks&lt;/em&gt;, assets and styling.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Requirements&lt;/strong&gt;: Contains documented system requirements.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Validation&lt;/strong&gt;: Where it contains the implementations of the validations used in the fields.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Unlike the &lt;a href="https://dev.to/rubemfsv/clean-architecture-applying-with-flutter-487b/"&gt;approach with Flutter&lt;/a&gt; - where there was a central folder where all the tests were concentrated - in this approach the tests are located in the respective folders inside the &lt;code&gt;src&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Guide
&lt;/h2&gt;

&lt;p&gt;In this section, a recommended logical sequence will be described for a better implementation performance of React systems using this architecture.&lt;/p&gt;

&lt;p&gt;In order to simplify the explanation, unit tests will not be described in detail. However, it is strongly recommended to start with unit tests before development (TDD) of each step using the &lt;em&gt;requirements&lt;/em&gt; to support the scenarios. And after finalizing the scenarios, test the end to end flow (if it is one of the main ones, keep in mind the &lt;a href="https://martinfowler.com/articles/practical-test-pyramid.html" rel="noopener noreferrer"&gt;test pyramid&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The following demonstration is the creation of the Login flow to log into an application.&lt;/p&gt;

&lt;h3&gt;
  
  
  First step: Create business rules in the domain layer
&lt;/h3&gt;

&lt;p&gt;Inside src/domain/usecases, create &lt;code&gt;authentication.ts&lt;/code&gt;. This file will be an interface that will describe the authentication business rule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { AccountModel } from '@/domain/models/';

export interface IAuthentication {
  auth(params: Authentication.Params): Promise&amp;lt;Authentication.Model&amp;gt;;
}

export namespace Authentication {
  export type Params = {
    email: string;
    password: string;
  };

  export type Model = AccountModel;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, it is an interface that has an &lt;code&gt;auth()&lt;/code&gt; function that receives the  &lt;em&gt;Authentication.Params&lt;/em&gt; which are declared in a namespace below - containing the parameters type (email and password) and the model type (&lt;em&gt;AccountModel&lt;/em&gt;) - and expects to return an &lt;em&gt;Authentication.Model&lt;/em&gt; asynchronously.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AccountModel&lt;/em&gt; is a named export of the model created in src/domain/models that represents the token that is returned after authentication to persist the session.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export type AccountModel = {
  accessToken: string;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Second step: Implement the rules in the data layer
&lt;/h3&gt;

&lt;p&gt;In this layer, we create the use case to implement the rule created previously in the domain layer, but inside src/data/usecases.&lt;/p&gt;

&lt;p&gt;The file usually looks like the example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { IHttpClient, HttpStatusCode } from '@/data/protocols/http';
import { UnexpectedError, InvalidCredentialsError } from '@/domain/errors';
import { IAuthentication, Authentication } from '@/domain/usecases';

export class RemoteAuthentication implements IAuthentication {
  constructor(
    private readonly url: string,
    private readonly httpClient: IHttpClient&amp;lt;RemoteAuthenticationamespace.Model&amp;gt;
  ) {}

  async auth(
    params: Authentication.Params
  ): Promise&amp;lt;RemoteAuthenticationamespace.Model&amp;gt; {
    const httpResponse = await this.httpClient.request({
      url: this.url,
      method: 'post',
      body: params,
    });

    switch (httpResponse.statusCode) {
      case HttpStatusCode.ok:
        return httpResponse.body;
      case HttpStatusCode.unauthorized:
        throw new InvalidCredentialsError();
      default:
        throw new UnexpectedError();
    }
  }
}

export namespace RemoteAuthenticationamespace {
  export type Model = Authentication.Model;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, the &lt;em&gt;RemoteAuthentication&lt;/em&gt; class implements the &lt;em&gt;IAuthentication&lt;/em&gt; interface, receiving the HTTP client and the url for the request. In the &lt;code&gt;auth()&lt;/code&gt; function it receives the parameters, and calls the httpClient passing the url, the method (in this case it is &lt;em&gt;post&lt;/em&gt;) and the body (which are the parameters). This return is a &lt;em&gt;httpResponse&lt;/em&gt; of the type referring to the &lt;em&gt;Authentication.Model&lt;/em&gt; that has a response status code, and which, depending on its result, gives the respective return - and may return the value expected by the request or an error.&lt;/p&gt;

&lt;p&gt;The status codes are the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" rel="noopener noreferrer"&gt;HTTP&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export enum HttpStatusCode {
  ok = 200,
  created = 201,
  noContent = 204,
  badRequest = 400,
  unauthorized = 401,
  forbidden = 403,
  notFound = 404,
  serverError = 500,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Third step: Implement the pages in the &lt;em&gt;presentation&lt;/em&gt; layer
&lt;/h3&gt;

&lt;p&gt;To simplify the understanding, only code snippets referring to the authentication function call will be presented. The Login screen contains more actions and details that go beyond authentication. Consider the page prototype below for easier visualization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://camo.githubusercontent.com/0d99b7013bf6f71b20c1cf31c66b26795e83668722f05055c58e895df67da5ce/68747470733a2f2f6465762d746f2d75706c6f6164732e73332e616d617a6f6e6177732e636f6d2f75706c6f6164732f61727469636c65732f76797275763565726f633165623570376665726a2e706e67" class="article-body-image-wrapper"&gt;&lt;img src="https://camo.githubusercontent.com/0d99b7013bf6f71b20c1cf31c66b26795e83668722f05055c58e895df67da5ce/68747470733a2f2f6465762d746f2d75706c6f6164732e73332e616d617a6f6e6177732e636f6d2f75706c6f6164732f61727469636c65732f76797275763565726f633165623570376665726a2e706e67" alt="Login page" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In src/presentation/pages/ the Login page will be created, which is composed by components, methods and functions. The component that calls the authentication function is the &lt;code&gt;&amp;lt;Button/&amp;gt;&lt;/code&gt; that is contained in the form to get the input values, as shown in the following code snippet:&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;form
  data-testid="loginForm"
  className={Styles.form}
  onSubmit={handleSubmit}
&amp;gt; 
  &amp;lt;Input
    autoComplete="off"
    title="Enter your e-mail"
    type="email"
    name="email"
  /&amp;gt;
  &amp;lt;Input
    autoComplete="off"
    title="Enter your password"
    type="password"
    name="password"
    minLength={6}
  /&amp;gt;
  &amp;lt;Button
    className={Styles.loginBtn}
    type="submit"
    disabled={state.isFormInvalid}
    title="Login"
    data-testid="loginButton"
  /&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When clicking on the &lt;code&gt;Button&lt;/code&gt;, the &lt;code&gt;handleSubmit()&lt;/code&gt; that is in the &lt;code&gt;onSubmit&lt;/code&gt; of the &lt;code&gt;form&lt;/code&gt; is called.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const handleSubmit = async (
    event: React.FormEvent&amp;lt;HTMLFormElement&amp;gt;
  ): Promise&amp;lt;void&amp;gt; =&amp;gt; {
    event.preventDefault();
    try {
      const account = await authentication.auth({
        email: state.email,
        password: state.password,
      });

      setCurrentAccount(account);
      history.replace('/');
    } catch (error) {
      // Error handling here
    }
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the &lt;code&gt;authentication.auth()&lt;/code&gt; on click will call a &lt;em&gt;factory&lt;/em&gt; (we'll see later) to do the authentication. In this case, it is passing the parameters captured by the input and the value returned from the request is saved in the cache through &lt;code&gt;setCurrentAccount(account)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fourth step: Connect all layers for requests to work
&lt;/h3&gt;

&lt;p&gt;After everything is implemented, now just connect all the parts. For this, the design pattern &lt;a href="https://refactoring.guru/design-patterns/factory-method" rel="noopener noreferrer"&gt;Factory Method&lt;/a&gt; is used.&lt;/p&gt;

&lt;p&gt;Inside src/main/factories/usecases we create the &lt;em&gt;factory&lt;/em&gt; of the use case being implemented. In the case of this example, it is related to authentication.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;makeRemoteAuthentication&lt;/em&gt; is created, which returns the &lt;em&gt;RemoteAuthentication&lt;/em&gt; that receives as a parameter the &lt;em&gt;factory&lt;/em&gt; of the &lt;em&gt;Http Client&lt;/em&gt; and the &lt;em&gt;factory&lt;/em&gt; that creates the URL. The URL of the API you want to request is passed as a parameter along with the &lt;em&gt;factory&lt;/em&gt; that creates the URL. In the example it is the URL that ends with &lt;em&gt;/login&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { RemoteAuthentication } from '@/data/usecases/';
import { IAuthentication } from '@/domain/usecases';
import { makeAxiosHttpClient, makeApiUrl } from '@/main/factories/http';

export const makeRemoteAuthentication = (): IAuthentication =&amp;gt; {
  const remoteAuthentication = new RemoteAuthentication(
    makeApiUrl('/login'),
    makeAxiosHttpClient()
  );

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

&lt;/div&gt;



&lt;p&gt;After that, in src/main/factories/pages, the folder for the Login factories is created. In pages with forms, form validations are also injected, but as the focus of this text is on integrations, we will leave this point out of the explanation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import { Login } from '@/presentation/pages';
import { makeRemoteAuthentication } from '@/main/factories/usecases/';

const makeLogin: React.FC = () =&amp;gt; {
  const remoteAuthentication = makeRemoteAuthentication();

  return (
    &amp;lt;Login
      authentication={remoteAuthentication}
    /&amp;gt;
  );
};

export default makeLogin;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;makeLogin&lt;/code&gt; const representing the &lt;em&gt;factory&lt;/em&gt; is created. It has &lt;code&gt;makeRemoteAuthentication&lt;/code&gt; which is injected inside the Login page created in the &lt;em&gt;presentation&lt;/em&gt; layer so that the page has access to these requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fifth step: Apply the page created in the application
&lt;/h3&gt;

&lt;p&gt;Finally, it is necessary to call the Login &lt;em&gt;factory&lt;/em&gt; in the application, so that it can be accessed by the user.&lt;/p&gt;

&lt;p&gt;In the &lt;em&gt;router.tsx&lt;/em&gt; file located in src/main/routes, add the &lt;em&gt;factory&lt;/em&gt; page created into the &lt;em&gt;Switch&lt;/em&gt; inside &lt;em&gt;BrowserRouter&lt;/em&gt;. The route is passed in the &lt;em&gt;path&lt;/em&gt;, in this case it is &lt;em&gt;/login&lt;/em&gt;, and the page in the &lt;em&gt;component&lt;/em&gt;, which in this case is the pointer to the &lt;code&gt;makeLoginPage&lt;/code&gt; &lt;em&gt;factory&lt;/em&gt; . This logic is used with all other pages, only changing from Route to PrivateRoute if the route is authenticated. The code looks like this below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Router: React.FC = () =&amp;gt; {
  return (
    &amp;lt;ApiContext.Provider
      value={{
        setCurrentAccount: setCurrentAccountAdapter,
        getCurrentAccount: getCurrentAccountAdapter,
      }}
    &amp;gt;
      &amp;lt;BrowserRouter&amp;gt;
        &amp;lt;Switch&amp;gt;
          &amp;lt;Route exact path="/login" component={makeLogin} /&amp;gt;
          &amp;lt;PrivateRoute exact path="/" component={makeDashboard} /&amp;gt;
        &amp;lt;/Switch&amp;gt;
      &amp;lt;/BrowserRouter&amp;gt;
    &amp;lt;/ApiContext.Provider&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Clean Architecture despite being a bit complex to understand and implement at the beginning - and even seem redundant -, abstractions are necessary. Several design patterns are applied to ensure the quality and independence of the code, facilitating the evolution and independent maintenance of the framework. In cases like this, if you want to change the framework from &lt;em&gt;React&lt;/em&gt; to &lt;em&gt;Angular&lt;/em&gt; or any other &lt;em&gt;Typescript&lt;/em&gt; based framework, just change the presentation layer and make adjustments to the dependencies.&lt;/p&gt;

&lt;p&gt;Following the development process and understanding why you are doing it in such a way makes code production easier. After a while it ends up being done naturally, as it has a linear development process: I. Use case in the domain layer; II. Use case in the data layer; III. Creation of UI in the presentation layer; IV. Creation of &lt;em&gt;factories&lt;/em&gt; to integrate all layers into the main layer; V. And the call of the main &lt;em&gt;factory&lt;/em&gt; in the application routes.&lt;/p&gt;

&lt;p&gt;As the example has many abstracted parts, it is recommended that you read the code for the hidden parts for a better understanding. &lt;a href="https://github.com/rubemfsv/clean-react-app" rel="noopener noreferrer"&gt;In this repository&lt;/a&gt; you can access abstracted code similar to the one given in this example.&lt;/p&gt;

&lt;p&gt;You can also access this architecture just by running the &lt;code&gt;npx @rubemfsv/clean-react-app my app&lt;/code&gt; command, similar to create-react-app, but in a cleaner and more scalable way. Find out how to do it reading &lt;a href="https://dev.to/rubemfsv/set-up-a-react-app-using-clean-architecture-by-running-one-command-133c"&gt;this post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Rodrigo Manguinho &lt;a href="https://github.com/rmanguinho/clean-react" rel="noopener noreferrer"&gt;https://github.com/rmanguinho/clean-react&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MARTIN, Robert C. Clean Architecture: A Craftsman’s Guide to Software Structure and Design. 1st. ed. USA: Prentice Hall Press, 2017. ISBN 0134494164.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>cleanarchitecture</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Set Up a React App Using Clean Architecture by Running One Command</title>
      <dc:creator>Rubem Vasconcelos</dc:creator>
      <pubDate>Mon, 18 Jul 2022 17:20:00 +0000</pubDate>
      <link>https://dev.to/rubemfsv/set-up-a-react-app-using-clean-architecture-by-running-one-command-133c</link>
      <guid>https://dev.to/rubemfsv/set-up-a-react-app-using-clean-architecture-by-running-one-command-133c</guid>
      <description>&lt;p&gt;Create React apps using &lt;a href="https://dev.to/rubemfsv/clean-architecture-the-concept-behind-the-code-52do"&gt;Clean Architecture&lt;/a&gt; with no build configuration.&lt;/p&gt;

&lt;p&gt;It's like create-react-app, but more efficient and easier to scale. The purpose is to help improve time during development.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/rubemfsv/clean-architecture-applying-with-react-40h6"&gt;User Guide&lt;/a&gt; – How to develop apps bootstrapped with Clean React App.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clean React App works on macOS, Windows, and Linux.&lt;br&gt;&lt;br&gt;
If something doesn’t work, please &lt;a href="https://github.com/rubemfsv/clean-react-app/issues/new" rel="noopener noreferrer"&gt;file an issue&lt;/a&gt;.&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Overview
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @rubemfsv/clean-react-app my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
npm start or npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done. Everything is set up and ready to run.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;This boilerplate contains the following settings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Local storage adapter;&lt;/li&gt;
&lt;li&gt;Axios as HTTP Client;&lt;/li&gt;
&lt;li&gt;Webpack configured for development and production environments;&lt;/li&gt;
&lt;li&gt;Basic end-to-end test settings with Cypress;&lt;/li&gt;
&lt;li&gt;Unit tests with Jest;&lt;/li&gt;
&lt;li&gt;Husky with pre-push to run unit tests;&lt;/li&gt;
&lt;li&gt;Authentication with validations;&lt;/li&gt;
&lt;li&gt;Validation layer for reuse of validations;&lt;/li&gt;
&lt;li&gt;Some hooks to help with API calls and form submissions;&lt;/li&gt;
&lt;li&gt;Private route configured;&lt;/li&gt;
&lt;li&gt;Three pages to help improve productivity:

&lt;ul&gt;
&lt;li&gt;Login page&lt;/li&gt;
&lt;li&gt;Sign up page&lt;/li&gt;
&lt;li&gt;Dashboard&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  It's open source. Feel free to contribute!
&lt;/h1&gt;

&lt;p&gt;Link: &lt;a href="https://www.npmjs.com/package/@rubemfsv/clean-react-app" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@rubemfsv/clean-react-app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>typescript</category>
      <category>react</category>
      <category>cleanarchitecture</category>
    </item>
    <item>
      <title>Arquitetura Limpa: Aplicando com React</title>
      <dc:creator>Rubem Vasconcelos</dc:creator>
      <pubDate>Fri, 15 Jul 2022 17:18:00 +0000</pubDate>
      <link>https://dev.to/rubemfsv/arquitetura-limpa-aplicando-com-react-1eo0</link>
      <guid>https://dev.to/rubemfsv/arquitetura-limpa-aplicando-com-react-1eo0</guid>
      <description>&lt;p&gt;Esse texto faz parte de uma série de textos sobre análise da &lt;a href="https://dev.to/rubemfsv/arquitetura-limpa-o-conceito-por-tras-do-codigo-4kdf"&gt;Arquitetura Limpa&lt;/a&gt; aplicada com frameworks e linguagens distintas.&lt;/p&gt;

&lt;p&gt;Os propósitos deste texto seguem alinhados com os do texto anterior, sendo eles: I. Mostrar uma divisão arquitetural de uma aplicação React usando Arquitetura Limpa; II. Guiar a implementação de novas features nesta arquitetura proposta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Divisão Arquitetural
&lt;/h2&gt;

&lt;p&gt;O passo inicial é analisar como é feita a divisão.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cypress/
src/
  data/
    protocols/
    test/
    usecases/
  domain/
    errors/
    models/
    test/
    usecases/
  infra/
    cache/
    http/
    test/
  main/
    adapters/
    config/
    decorators/
    factories/
      cache/
      decorators/
      http/
      pages/
      usecases/
    routes/
    scripts/
    index.tsx
  presentation/
    assets/
    components/
    hooks/
    pages/
    protocols/
    routes/
    styles/
    test/
  requirements/
  validation/
    errors/
    protocols/
    test/
    validators/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Em detalhes, o propósito de cada estrutura de arquivos é o seguinte:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;cypress&lt;/strong&gt;: Contém os arquivos de teste end to end da aplicação (para projetos grandes, essa pasta é recomendável que seja em um projeto a parte, para que o time responsável pelos testes e2e cuide dele, uma vez que não precisa conhecer o código do projeto).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;src&lt;/strong&gt;: Contém todos os arquivos necessários para o sistema.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data&lt;/strong&gt;: A pasta data representa a camada de dados da Arquitetura Limpa, sendo dependente da camada de domínio. Contém as implementações das regras de negócio que são declaradas no &lt;em&gt;domain&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain&lt;/strong&gt;: Representa a camada de domínio da Arquitetura Limpa, a camada mais interna da aplicação, não apresentando dependência com nenhuma outra camada, onde contém as regras de negócio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infra&lt;/strong&gt;: Essa pasta contém as implementações referentes ao protocolo HTTP e ao cache, também é único local onde terá acesso a dependências externas relacionadas para esses dois itens citados. Aqui também é onde está contido a maioria das bibliotecas externas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main&lt;/strong&gt;: Corresponde a camada principal da aplicação, ponto que ocorre a integração das interfaces desenvolvidas na camada de UI, com as regras de negócio criadas nas pastas que representam as camadas mais internas da Arquitetura Limpa. Tudo isso se dá devido ao uso de padrões de projetos como Factory Method, Composite e Builder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Presentation&lt;/strong&gt;: Nesta pasta contém a parte visual da aplicação, com suas páginas, componentes, &lt;em&gt;hooks&lt;/em&gt;, assets e estilizações.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Requirements&lt;/strong&gt;: Contém os requisitos do sistema documentados.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Validation&lt;/strong&gt;: Onde contém as implementações das validações utilizadas nos campos.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Diferente da &lt;a href="https://dev.to/rubemfsv/arquitetura-limpa-uso-com-flutter-2i68"&gt;abordagem com Flutter&lt;/a&gt; onde havia uma pasta central que se concentravam todos os testes, nessa abordagem os testes se encontram nas respectivas pastas dentro da &lt;code&gt;src&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guia de Implementação
&lt;/h2&gt;

&lt;p&gt;Nesta seção será descrita uma sequência lógica recomendada para um melhor desempenho de implementação de sistemas React utilizando esta arquitetura. &lt;/p&gt;

&lt;p&gt;Para finalidade de simplificar a explicação, não será descrito em detalhes os testes unitários. No entanto, é fortemente recomendado começar pelos testes unitários antes do desenvolvimento (TDD) de cada passo utilizando os &lt;em&gt;requirements&lt;/em&gt; para embasar os cenários. E após finalizar os cenários, fazer o teste end to end do fluxo (se for um dos principais, ter em mente a &lt;a href="https://martinfowler.com/articles/practical-test-pyramid.html" rel="noopener noreferrer"&gt;pirâmide de testes&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;A demonstração a seguir é da criação do fluxo de Login para entrar em uma aplicação.&lt;/p&gt;

&lt;h3&gt;
  
  
  Primeiro passo: Criar as regras de negócio na camada de domínio
&lt;/h3&gt;

&lt;p&gt;Dentro de src/domain/usecases, criar o authentication.ts. Esse arquivo será uma interface que vai descrever a regra de negócio da autenticação.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { AccountModel } from '@/domain/models/';

export interface IAuthentication {
  auth(params: Authentication.Params): Promise&amp;lt;Authentication.Model&amp;gt;;
}

export namespace Authentication {
  export type Params = {
    email: string;
    password: string;
  };

  export type Model = AccountModel;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como vemos, essa interface tem um método &lt;code&gt;auth()&lt;/code&gt; que recebe os parâmetros &lt;em&gt;Authentication.Params&lt;/em&gt; que são declarados num namespace abaixo - contendo o tipo dos parâmetros (email e password) e o tipo do model (&lt;em&gt;AccountModel&lt;/em&gt;) - e espera retornar um &lt;em&gt;Authentication.Model&lt;/em&gt; de maneira assíncrona. &lt;/p&gt;

&lt;p&gt;O &lt;em&gt;AccountModel&lt;/em&gt; é uma exportação nomeada do model criado em src/domain/models que representa o token que é retornado após a autenticação para persistir a sessão.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export type AccountModel = {
  accessToken: string;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Segundo passo: Implementar as regras na camada de dados
&lt;/h3&gt;

&lt;p&gt;Nessa camada, criamos o caso de uso para implementar a interface criada anteriormente na camada de domínio, porém dentro de src/data/usecases.&lt;/p&gt;

&lt;p&gt;O arquivo tende a ficar como o exemplo abaixo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { IHttpClient, HttpStatusCode } from '@/data/protocols/http';
import { UnexpectedError, InvalidCredentialsError } from '@/domain/errors';
import { IAuthentication, Authentication } from '@/domain/usecases';

export class RemoteAuthentication implements IAuthentication {
  constructor(
    private readonly url: string,
    private readonly httpClient: IHttpClient&amp;lt;RemoteAuthenticationamespace.Model&amp;gt;
  ) {}

  async auth(
    params: Authentication.Params
  ): Promise&amp;lt;RemoteAuthenticationamespace.Model&amp;gt; {
    const httpResponse = await this.httpClient.request({
      url: this.url,
      method: 'post',
      body: params,
    });

    switch (httpResponse.statusCode) {
      case HttpStatusCode.ok:
        return httpResponse.body;
      case HttpStatusCode.unauthorized:
        throw new InvalidCredentialsError();
      default:
        throw new UnexpectedError();
    }
  }
}

export namespace RemoteAuthenticationamespace {
  export type Model = Authentication.Model;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como podemos observar, a classe &lt;em&gt;RemoteAuthentication&lt;/em&gt; implementa a interface &lt;em&gt;IAuthentication&lt;/em&gt;, recebendo o cliente HTTP e a url para a requisição. No método &lt;code&gt;auth()&lt;/code&gt; ele recebe os parâmetros, e chama o httpClient passando a url, o método (nesse caso é o &lt;em&gt;post&lt;/em&gt;) e o body (que são os parâmetros). Esse retorno é uma &lt;em&gt;httpResponse&lt;/em&gt; do tipo referente ao &lt;em&gt;Authentication.Model&lt;/em&gt; que tem um código de status de resposta, e que a depender do seu resultado, dá o respectivo retorno - podendo retornar o valor esperado pela requisição ou um erro. &lt;/p&gt;

&lt;p&gt;Os códigos de status são os &lt;a href="https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Status" rel="noopener noreferrer"&gt;HTTP&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export enum HttpStatusCode {
  ok = 200,
  created = 201,
  noContent = 204,
  badRequest = 400,
  unauthorized = 401,
  forbidden = 403,
  notFound = 404,
  serverError = 500,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Terceiro passo: Implementar as páginas na camada de &lt;em&gt;presentation&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Para simplificar o entendimento, será apresentado apenas trechos de códigos referentes a chamada do método de autenticação. A página de Login contém mais ações e detalhes que vão além da autenticação. Levar em consideração o protótipo da página abaixo para facilitar a visualização.&lt;/p&gt;

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

&lt;p&gt;Em src/presentation/pages/ será criada a página de Login que é composta por componentes, métodos e funções. O componente que chama o método de autenticação é o &lt;code&gt;&amp;lt;Button/&amp;gt;&lt;/code&gt; que está contido no formulário para pegar os valores dos inputs, conforme o trecho de código a seguir:&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;form
  data-testid="loginForm"
  className={Styles.form}
  onSubmit={handleSubmit}
&amp;gt; 
  &amp;lt;Input
    autoComplete="off"
    title="Digite seu e-mail"
    type="email"
    name="email"
  /&amp;gt;
  &amp;lt;Input
    autoComplete="off"
    title="Digite sua senha"
    type="password"
    name="password"
    minLength={6}
  /&amp;gt;
  &amp;lt;Button
    className={Styles.loginBtn}
    type="submit"
    disabled={state.isFormInvalid}
    title="Entrar"
    data-testid="loginButton"
  /&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ao clicar no &lt;code&gt;Button&lt;/code&gt;, é chamado o &lt;code&gt;handleSubmit()&lt;/code&gt; que está no &lt;code&gt;onSubmit&lt;/code&gt; do &lt;code&gt;form&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const handleSubmit = async (
    event: React.FormEvent&amp;lt;HTMLFormElement&amp;gt;
  ): Promise&amp;lt;void&amp;gt; =&amp;gt; {
    event.preventDefault();
    try {
      const account = await authentication.auth({
        email: state.email,
        password: state.password,
      });

      setCurrentAccount(account);
      history.replace('/');
    } catch (error) {
      // Error handling here
    }
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Onde o &lt;code&gt;authentication.auth()&lt;/code&gt; clicado chamará uma &lt;em&gt;factory&lt;/em&gt; (veremos mais a seguir) para fazer a autenticação. Nesse caso está passando os parâmetros capturados pelo input e o valor retornado da requisição é salvo no cache através do &lt;code&gt;setCurrentAccount(account);&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quarto passo: Conectar todas as camadas para que as requisições funcionem
&lt;/h3&gt;

&lt;p&gt;Após tudo implementado, agora basta conectar todas as partes. Para isso, é utilizado o padrão de projeto &lt;a href="https://refactoring.guru/pt-br/design-patterns/factory-method" rel="noopener noreferrer"&gt;Factory Method&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Dentro de src/main/factories/usecases, criamos a &lt;em&gt;factory&lt;/em&gt; do caso de uso que está sendo implementado. No caso desse exemplo, é o relacionado a autenticação. &lt;/p&gt;

&lt;p&gt;É criado o &lt;em&gt;makeRemoteAuthentication&lt;/em&gt;, que retorna o &lt;em&gt;RemoteAuthentication&lt;/em&gt; que recebe como parâmetro a &lt;em&gt;factory&lt;/em&gt; que cria a URL e a &lt;em&gt;factory&lt;/em&gt; do &lt;em&gt;Http Client&lt;/em&gt;. É passado como parâmetro a URL da API que deseja requisitar junto da &lt;em&gt;factory&lt;/em&gt; que cria a URL. No exemplo é a URL que finaliza com &lt;em&gt;/login&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { RemoteAuthentication } from '@/data/usecases/';
import { IAuthentication } from '@/domain/usecases';
import { makeAxiosHttpClient, makeApiUrl } from '@/main/factories/http';

export const makeRemoteAuthentication = (): IAuthentication =&amp;gt; {
  const remoteAuthentication = new RemoteAuthentication(
    makeApiUrl('/login'),
    makeAxiosHttpClient()
  );

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

&lt;/div&gt;



&lt;p&gt;Após isso, em src/main/factories/pages, é criada a pasta para as factories do Login. Em páginas com formulário também são injetadas validações, porém como o foco desse texto são as integrações, deixaremos esse ponto de fora da explicação.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import { Login } from '@/presentation/pages';
import { makeRemoteAuthentication } from '@/main/factories/usecases/';

const makeLogin: React.FC = () =&amp;gt; {
  const remoteAuthentication = makeRemoteAuthentication();

  return (
    &amp;lt;Login
      authentication={remoteAuthentication}
    /&amp;gt;
  );
};

export default makeLogin;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;É criada uma const &lt;code&gt;makeLogin&lt;/code&gt; que representa a &lt;em&gt;factory&lt;/em&gt;. Ela possui o &lt;code&gt;makeRemoteAuthentication&lt;/code&gt; que é injetado dentro da página de Login criada na camada de &lt;em&gt;presentation&lt;/em&gt; para que a página tenha acesso a essas requisições.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quinto passo: Aplicar a página criada na aplicação
&lt;/h3&gt;

&lt;p&gt;Por fim, é necessário adicionar a &lt;em&gt;factory&lt;/em&gt; do Login nas rotas da aplicação para que ela seja acessada pelos usuários. &lt;/p&gt;

&lt;p&gt;No arquivo &lt;em&gt;router.tsx&lt;/em&gt; que fica localizado em src/main/routes, adicionar a &lt;em&gt;factory&lt;/em&gt; da página criada dentro do &lt;em&gt;Switch&lt;/em&gt; do &lt;em&gt;BrowserRouter&lt;/em&gt;. É passado no &lt;em&gt;path&lt;/em&gt; a rota, no caso é a &lt;em&gt;/login&lt;/em&gt;, e a página no &lt;em&gt;component&lt;/em&gt;, que no caso é o ponteiro para a &lt;em&gt;factory&lt;/em&gt; &lt;code&gt;makeLoginPage&lt;/code&gt;. Essa lógica é utilizada com todas as outras páginas, alterando apenas de Route para PrivateRoute caso a rota seja autenticada. O código parecido com este abaixo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Router: React.FC = () =&amp;gt; {
  return (
    &amp;lt;ApiContext.Provider
      value={{
        setCurrentAccount: setCurrentAccountAdapter,
        getCurrentAccount: getCurrentAccountAdapter,
      }}
    &amp;gt;
      &amp;lt;BrowserRouter&amp;gt;
        &amp;lt;Switch&amp;gt;
          &amp;lt;Route exact path="/login" component={makeLogin} /&amp;gt;
          &amp;lt;PrivateRoute exact path="/" component={makeDashboard} /&amp;gt;
        &amp;lt;/Switch&amp;gt;
      &amp;lt;/BrowserRouter&amp;gt;
    &amp;lt;/ApiContext.Provider&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;A Arquitetura Limpa apesar de ser um pouco complexa de se entender e implementar no começo - e até parecer redundante -, as abstrações são necessárias. São aplicados diversos padrões de projetos para garantir a qualidade e independência do código, facilitando a evolução e manutenção independente de framework. Em casos como esse, se desejar mudar o framework de &lt;em&gt;React&lt;/em&gt; para &lt;em&gt;Angular&lt;/em&gt; ou qualquer outro baseado em &lt;em&gt;Typescript&lt;/em&gt;, basta mudar a camada de apresentação e fazer ajustes nas dependências.&lt;/p&gt;

&lt;p&gt;Seguir o processo de desenvolvimento e entender o porquê está fazendo de tal maneira facilita a produção do código. Após um tempo acaba sendo feito de maneira natural, pois tem um processo linear de desenvolvimento: I. Caso de uso na camada de domínio; II. Caso de uso na camada de dados; III. Criação das interfaces na camada de presentation; IV. Criação das &lt;em&gt;factories&lt;/em&gt; para integrar todas as camadas na camada principal; V. E a chamada da &lt;em&gt;factory&lt;/em&gt; principal nas rotas da aplicação. &lt;/p&gt;

&lt;p&gt;Pelo exemplo ter muitas partes abstraídas, é recomendável a leitura do código das partes ocultas para uma maior compreensão. &lt;a href="https://github.com/rubemfsv/clean-react-app" rel="noopener noreferrer"&gt;Nesse repositório&lt;/a&gt; você consegue ter acesso a códigos abstraídos similares ao desse exemplo dado. &lt;/p&gt;

&lt;p&gt;Você também pode ter acesso a essa arquitetura rodando apenas o comando &lt;code&gt;npx @rubemfsv/clean-react-app my app&lt;/code&gt;, similar ao create-react-app, mas de uma maneira mais limpa e escalável. &lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Rodrigo Manguinho &lt;a href="https://github.com/rmanguinho/clean-react" rel="noopener noreferrer"&gt;https://github.com/rmanguinho/clean-react&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MARTIN, Robert C. Clean Architecture: A Craftsman’s Guide to Software Structure and Design. 1st. ed. USA: Prentice Hall Press, 2017. ISBN 0134494164.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>cleanarchitecture</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Clean Architecture: Applying with Flutter</title>
      <dc:creator>Rubem Vasconcelos</dc:creator>
      <pubDate>Sat, 09 Jul 2022 18:45:20 +0000</pubDate>
      <link>https://dev.to/rubemfsv/clean-architecture-applying-with-flutter-487b</link>
      <guid>https://dev.to/rubemfsv/clean-architecture-applying-with-flutter-487b</guid>
      <description>&lt;p&gt;Before starting to read this text, it is recommended to have notions of some basic concepts such as &lt;a href="https://dev.to/rubemfsv/clean-architecture-the-concept-behind-the-code-52do"&gt;Clean Architecture&lt;/a&gt; and &lt;a href="https://dev.to/rubemfsv/solid-the-concept-behind-the-code-3h4o"&gt;SOLID&lt;/a&gt;, as they make it easier to understand what will be presented.&lt;/p&gt;

&lt;p&gt;This text has two purposes: I. Show an architectural division of a Flutter application using Clean Architecture; II. Guide the implementation of new features in this proposed architecture.&lt;/p&gt;

&lt;p&gt;The analyzed code is based on the Clean Architecture approach proposed by &lt;a href="https://www.linkedin.com/in/rmanguinho/" rel="noopener noreferrer"&gt;Rodrigo Manguinho&lt;/a&gt; in his &lt;a href="https://rmanguinho.github.io" rel="noopener noreferrer"&gt;Flutter course&lt;/a&gt;. His approach is aligned with the original proposal by &lt;a href="http://cleancoder.com/products" rel="noopener noreferrer"&gt;Robert Martin&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architectural Division
&lt;/h2&gt;

&lt;p&gt;The first step is to analyze how the division is done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;android/
ios/
lib/
  data/
    cache/
    http/
    models/
    usecases/
  domain/
    entities/
    helpers/
    usecases/
  infra/
    cache/
    http/
  main/
    builders/
    composites/
    decorators/
    factories/
      cache/
      http/
      pages/
      usecases/
    main.dart
  presentation/
    mixins/
    presenters/
    protocols/
  ui/
    assets/
    components/
    helpers/
    mixins/
    pages/
  validation/
    protocols/
    validators/
requirements/
    bdd_specs/
    checklists/
    usecases/
test/
    data/
    domain/
    infra/
    main/
    mocks/
    presentation/
    ui/
    validation/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And from there, we can make associations with the &lt;a href="https://dev.to/rubemfsv/clean-architecture-the-concept-behind-the-code-52do"&gt;Clean Architecture theory&lt;/a&gt; for an easier understanding of &lt;a href="https://dev.to/rubemfsv/clean-architecture-layers-and-boundaries-13ed"&gt;division of responsibilities&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, let's take a closer look at the purpose of each file structure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Android&lt;/strong&gt;: Contains the files needed to build the application on android systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iOS&lt;/strong&gt;: Contains the files needed to build the application on iOS systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lib&lt;/strong&gt;: Contains all files needed for the application.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data&lt;/strong&gt;: The data folder represents the data layer of the Clean Architecture, being dependent on the domain layer. Contains the implementations of business rules that are declared in &lt;em&gt;domain&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain&lt;/strong&gt;: Represents the domain layer of the Clean Architecture, the innermost layer of the application, not having any dependency on any other layer, where it contains the business rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infra&lt;/strong&gt;: This folder contains the implementations referring to the HTTP protocol and the cache, it is also the only place where you will have access to external dependencies related to these two items mentioned.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main&lt;/strong&gt;: It corresponds to the main layer of the application, where the interfaces developed in the UI layer are integrated with the business rules created in the folders that represent the innermost layers of the Clean Architecture. All this is due to the use of design patterns such as Factory Method, Composite and Builder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Presentation&lt;/strong&gt;: This layer is where the data is prepared to be consumed in the UI, handling the logic behind the screens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI&lt;/strong&gt;: Contains the components and visual interfaces that are seen in the system, this is where the screens are created.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation&lt;/strong&gt;: Where it contains the implementations of the validations used in the fields (ex: minimum amount of characters, required field, valid email, among others).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Requirements&lt;/strong&gt;: Contains documented system requirements, this folder may or may not have all of the following subfolders, it depends a lot on how the team works.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bdd_specs&lt;/strong&gt;: Contains files written in Gherkin language to describe the expected behavior of the system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checklist&lt;/strong&gt;: It contains the description of the behavior of the pages, in order to facilitate during the unit tests, to know what to validate and what to expect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usecases&lt;/strong&gt;: It contains the expected behavior of the use cases of the system, where it describes the variations of the business rules to facilitate the unit tests and implementation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Test&lt;/strong&gt;: It contains all the unit tests of the application, each internal folder represents the layer that the tests belong to, and the &lt;em&gt;mocks&lt;/em&gt; folder contains the mocks used in the tests.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation Guide
&lt;/h2&gt;

&lt;p&gt;After understanding the reason for the division and what responsibilities are contained in each folder of the structure, a recommended logical sequence for a better implementation performance using this architecture will be described.&lt;/p&gt;

&lt;p&gt;In order to simplify the explanation, unit tests will not be described in detail. However, it is strongly recommended to start with unit tests before development (TDD) of each step using the &lt;em&gt;requirements&lt;/em&gt; to support the scenarios.&lt;/p&gt;

&lt;p&gt;The following demonstration is the creation of the Login flow to log into an application.&lt;/p&gt;

&lt;h3&gt;
  
  
  First step: Create business rules in the domain layer
&lt;/h3&gt;

&lt;p&gt;Inside lib/domain/usecases, create &lt;code&gt;authentication.dart&lt;/code&gt;. This file will be an abstract class that will describe the authentication business rule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import '../entities/entities.dart';

abstract class Authentication {
  Future&amp;lt;AccountEntity&amp;gt; auth(AuthenticationParams params);
}

class AuthenticationParams {
  final String email;
  final String password;

  AuthenticationParams({required this.email, required this.password});
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, it is an abstract class that has an &lt;code&gt;auth()&lt;/code&gt; method that receives the &lt;em&gt;AuthenticationParams&lt;/em&gt; parameters that are declared below (email and password), and expects to return an &lt;code&gt;AccountEntity&lt;/code&gt; asynchronously through the &lt;em&gt;Future&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AccountEntity&lt;/em&gt; is a class created in lib/domain/entities which represents the token that is returned after authentication to persist the session.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class AccountEntity {
  final String token;

  AccountEntity({required this.token});
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Second step: Implement the rules in the data layer
&lt;/h3&gt;

&lt;p&gt;In this layer, we create the use case to implement the rule created previously in the domain layer, but inside lib/data/usecases.&lt;/p&gt;

&lt;p&gt;The file usually looks like the example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import '../../../domain/entities/entities.dart';
import '../../../domain/helpers/helpers.dart';
import '../../../domain/usecases/usecases.dart';

import '../../http/http.dart';
import '../../models/models.dart';

class RemoteAuthentication implements Authentication {
  final HttpClient httpClient;
  final String url;

  RemoteAuthentication({required this.httpClient, required this.url});

  Future&amp;lt;AccountEntity&amp;gt; auth(AuthenticationParams params) async {
    final body = RemoteAuthenticationParams.fromDomain(params).toJson();
    try {
      final hpptResponse =
          await httpClient.request(url: url, method: 'post', body: body);

      return RemoteAccountModel.fromJson(hpptResponse).toEntity();
    } on HttpError catch (error) {
      throw error == HttpError.unauthorized
          ? DomainError.invalidCredentials
          : DomainError.unexpected;
    }
  }
}

class RemoteAuthenticationParams {
  final String email;
  final String password;

  RemoteAuthenticationParams({required this.email, required this.password});

  factory RemoteAuthenticationParams.fromDomain(AuthenticationParams params) =&amp;gt;
      RemoteAuthenticationParams(
          email: params.email, password: params.password);

  Map toJson() =&amp;gt; {'email': email, 'password': password};
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, the &lt;em&gt;RemoteAuthentication&lt;/em&gt; class implements the &lt;em&gt;Authentication&lt;/em&gt; abstract class, receiving the HTTP client and the url for the request. In the &lt;code&gt;auth()&lt;/code&gt; method it receives the parameters, and calls the &lt;code&gt;RemoteAuthenticationParams.fromDomain(params)&lt;/code&gt; factory created below with the purpose of converting what comes in the standard format to the json format to be sent in the HTTP request inside the body. After that, the request is made and the value returned in &lt;em&gt;httpResponse&lt;/em&gt; is stored, and this &lt;em&gt;httpResponse&lt;/em&gt; is returned in the method inside a model in order to convert the result to the standard format to work (entity) through &lt;code&gt;RemoteAccountModel.fromJson( hpptResponse).toEntity()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This factory and model are created in this layer with the purpose of not polluting the domain layer, because what happens in the data layer should not influence what happens in the domain layer.&lt;/p&gt;

&lt;p&gt;For the sake of curiosity, the implementation of &lt;em&gt;RemoteAccountModel&lt;/em&gt; is below. It takes an accessToken in json format and converts it to an Entity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import '../../domain/entities/entities.dart';
import '../http/http.dart';

class RemoteAccountModel {
  final String accessToken;

  RemoteAccountModel(this.accessToken);

  factory RemoteAccountModel.fromJson(Map json) {
    if (!json.containsKey('accessToken')) {
      throw HttpError.invalidData;
    }
    return RemoteAccountModel(json['accessToken']);
  }

  AccountEntity toEntity() =&amp;gt; AccountEntity(token: accessToken);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Third step: Implement the screens in the UI layer
&lt;/h3&gt;

&lt;p&gt;To simplify the understanding, only code snippets referring to the authentication method call will be presented. The Login screen contains more actions and details that go beyond authentication. Consider the screen prototype below for easier visualization.&lt;/p&gt;

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

&lt;p&gt;In lib/ui/pages/ you will need at least two files: I. &lt;code&gt;login_page.dart&lt;/code&gt;, which will be the Login page; II. &lt;code&gt;login_presenter.dart&lt;/code&gt; which will contain the abstract class with the methods and streams that are used on the page, and the implementation of this abstract class takes place in the presentation layer.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;login_presenter.dart&lt;/code&gt; file is similar to the example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';

abstract class LoginPresenter implements Listenable {
  void validateEmail(String email);
  void validatePassword(String password);

  Future&amp;lt;void&amp;gt; auth();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the code below is for the Login button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class LoginButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final presenter = Provider.of&amp;lt;LoginPresenter&amp;gt;(context);

    return StreamBuilder&amp;lt;bool&amp;gt;(
      stream: presenter.isFormValidStream,
      builder: (context, snapshot) {
        return ElevatedButton(
          onPressed: snapshot.data == true ? presenter.auth : null,
          child: Text(R.translations.enterButtonText.toUpperCase()),
        );
      },
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;auth()&lt;/code&gt; method when clicked on &lt;code&gt;onPressed&lt;/code&gt; calls the presenter to do the authentication. In this case, you are not passing parameters in &lt;code&gt;auth()&lt;/code&gt; because the parameters are taken from the presenter when interacting with the screen (the &lt;code&gt;validateEmail()&lt;/code&gt; and &lt;code&gt;validatePassword()&lt;/code&gt; declared above in the page presenter are used). We will see more details in the next step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fourth step: Implement the UI's abstract presenter class in the presentation layer
&lt;/h3&gt;

&lt;p&gt;To make it easier to work with Streams, it is recommended to use the &lt;a href="https://pub.dev/packages/get" rel="noopener noreferrer"&gt;GetX&lt;/a&gt; library (or any other one of your choice) to make it less verbose. The choice for &lt;em&gt;GetX&lt;/em&gt; is due to its great support and being constantly updated.&lt;/p&gt;

&lt;p&gt;In lib/presentation/presenters, &lt;code&gt;getx_login_presenter.dart&lt;/code&gt; is created. It is a &lt;em&gt;GetxLoginPresenter&lt;/em&gt; class that extends &lt;em&gt;GetxController&lt;/em&gt; and implements &lt;em&gt;LoginPresenter&lt;/em&gt;. Although the example below has &lt;em&gt;Validation&lt;/em&gt; and &lt;em&gt;SaveCurrentAccount&lt;/em&gt;, we will focus only on &lt;em&gt;Authentication&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'dart:async';
import 'package:get/get.dart';

import '../../ui/pages/login/login_presenter.dart';
import '../../domain/helpers/domain_error.dart';
import '../../domain/usecases/usecases.dart';
import '../protocols/protocols.dart';

class GetxLoginPresenter extends GetxController
  implements LoginPresenter {
  final Validation validation;
  final Authentication authentication;
  final SaveCurrentAccount saveCurrentAccount;

  String? _email;
  String? _password;

  GetxLoginPresenter({
    required this.validation,
    required this.authentication,
    required this.saveCurrentAccount,
  });

  void validateEmail(String email) {
    _email = email;
    // Validation code here
  }

  void validatePassword(String password) {
    _password = password;
    // Validation code here
  }

  Future&amp;lt;void&amp;gt; auth() async {
    try {
      final account = await authentication.auth(AuthenticationParams(
        email: _email!,
        password: _password!,
      ));
      await saveCurrentAccount.save(account);
    } on DomainError catch (error) {
      // Handle errors here
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;validateEmail(String email)&lt;/code&gt; and &lt;code&gt;validatePassword(String password)&lt;/code&gt; methods, the user's email and password are captured when typing in the &lt;em&gt;Inputs&lt;/em&gt; of the Login screen. In &lt;code&gt;auth()&lt;/code&gt;, this is where there is a call to the previously implemented authentication method, which receives the email and password captured by the previous validates.&lt;/p&gt;

&lt;p&gt;The call &lt;code&gt;authentication.auth(AuthenticationParams(email: _email!, password: _password!))&lt;/code&gt; returns a token (as explained earlier), is assigned to a variable called &lt;em&gt;account&lt;/em&gt; and then cached via &lt;code&gt;saveCurrentAccount.save( account)&lt;/code&gt; (this point was not explained in this text, but it is through it that the user session persists on the device).&lt;/p&gt;

&lt;h3&gt;
  
  
  Fifth step: Connect all layers for requests to work
&lt;/h3&gt;

&lt;p&gt;After everything is implemented, now just connect all the parts. For this, the design pattern &lt;a href="https://refactoring.guru/design-patterns/factory-method" rel="noopener noreferrer"&gt;Factory Method&lt;/a&gt; is used.&lt;/p&gt;

&lt;p&gt;Inside lib/main/factories/usecases we create the &lt;em&gt;factory&lt;/em&gt; of the use case being implemented. In the case of this example, it is related to authentication.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;authentication_factory.dart&lt;/em&gt; is created, which returns the &lt;em&gt;RemoteAuthentication&lt;/em&gt; that receives as a parameter the &lt;em&gt;factory&lt;/em&gt; of the &lt;em&gt;Http Client&lt;/em&gt; and the &lt;em&gt;factory&lt;/em&gt; that creates the URL. The URL of the API you want to request is passed as a parameter along with the &lt;em&gt;factory&lt;/em&gt; that creates the URL. In the example it is the URL that ends with &lt;em&gt;/login&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import '../../../domain/usecases/usecases.dart';
import '../../../data/usecases/usecases.dart';
import '../factories.dart';

Authentication makeRemoteAuthentication() {
  return RemoteAuthentication(
    httpClient: makeHttpAdapter(),
    url: makeApiUrl('login'),
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, in lib/main/factories/pages, the folder for the Login factories is created. For this explanation, we will focus on &lt;em&gt;login_page_factory.dart&lt;/em&gt; and &lt;em&gt;login_presenter_factory.dart&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;First, the &lt;em&gt;login_presenter_factory.dart&lt;/em&gt; is made, which is a Widget that returns the &lt;em&gt;GetxLoginPresenter&lt;/em&gt;. This &lt;em&gt;presenter&lt;/em&gt; was created previously, and inside it is injecting the authentication &lt;em&gt;factories&lt;/em&gt; (which was created just above) and the validation and saving token in the cache (which were not covered in this text, but follow the same premises of _ makeRemoteAuthentication_) .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import '../../factories.dart';
import '../../../../presentation/presenters/presenters.dart';
import '../../../../ui/pages/pages.dart';

LoginPresenter makeGetxLoginPresenter() {
  return GetxLoginPresenter(
    authentication: makeRemoteAuthentication(),
    validation: makeLoginValidation(),
    saveCurrentAccount: makeLocalSaveCurrentAccount(),
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, following the same line of thought, the &lt;em&gt;factory&lt;/em&gt; of the Login page is made. Like the &lt;em&gt;factory&lt;/em&gt; of the &lt;em&gt;presenter&lt;/em&gt;, it's a Widget, but in this case it returns the &lt;em&gt;LoginPage&lt;/em&gt; with the &lt;em&gt;factory&lt;/em&gt; of the &lt;em&gt;presenter&lt;/em&gt; created earlier being injected as a parameter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';
import '../../../../ui/pages/pages.dart';
import '../../factories.dart';

Widget makeLoginPage() {
  return LoginPage(makeGetxLoginPresenter());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sixth step: Apply the screen created in the application
&lt;/h3&gt;

&lt;p&gt;Finally, it is necessary to call the Login &lt;em&gt;factory&lt;/em&gt; in the application, so that it can be accessed by the user.&lt;/p&gt;

&lt;p&gt;In the &lt;em&gt;main.dart&lt;/em&gt; file located in lib/main, add the &lt;em&gt;factory&lt;/em&gt; page created into the page array (&lt;em&gt;getPages&lt;/em&gt;). In the route, is passed the &lt;em&gt;name&lt;/em&gt; of the rout - in this case it is &lt;em&gt;/login&lt;/em&gt; - and the page, which in this case is the pointer to the &lt;em&gt;factory&lt;/em&gt; &lt;code&gt;makeLoginPage&lt;/code&gt;. This logic is used with all other pages. The code looks like it is below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';

import '../ui/components/components.dart';
import 'factories/factories.dart';

void main() {
  runApp(App());
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
    final routeObserver = Get.put&amp;lt;RouteObserver&amp;gt;(RouteObserver&amp;lt;PageRoute&amp;gt;());

    return GetMaterialApp(
      title: 'Flutter Clean App',
      debugShowCheckedModeBanner: false,
      theme: makeAppTheme(),
      navigatorObservers: [routeObserver],
      initialRoute: '/',
      getPages: [
        GetPage(
            name: '/login', page: makeLoginPage, transition: Transition.fadeIn),
      ],
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Although it is a little complex to understand at first and seems a bit redundant, abstractions are necessary. Various design patterns are applied to ensure code quality and independence, facilitating evolution and maintenance.&lt;/p&gt;

&lt;p&gt;Following the development process and understanding why you are doing it in such a way makes code production easier. After a while it ends up being done naturally, as it has a linear development process: I. Use case in the domain layer; II. Use case in the data layer; III. UI creation; IV. Creation of logics to call the request in the presentation layer; V. Creation of &lt;em&gt;factories&lt;/em&gt; to integrate all layers into the main layer; VI. And then the call of the main &lt;em&gt;factory&lt;/em&gt; in the application routes so that it is available to the user.&lt;/p&gt;

&lt;p&gt;Despite having many abstracted parts, it is recommended to read the code of the hidden parts for a better understanding. &lt;a href="https://github.com/rmanguinho/clean-flutter-app" rel="noopener noreferrer"&gt;In this repository&lt;/a&gt; from Rodrigo Manguinho's course you can access these abstracted codes.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Rodrigo Manguinho &lt;a href="https://github.com/rmanguinho/clean-flutter-app" rel="noopener noreferrer"&gt;https://github.com/rmanguinho/clean-flutter-app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MARTIN, Robert C. Clean Architecture: A Craftsman’s Guide to Software Structure and Design. 1st. ed. USA: Prentice Hall Press, 2017. ISBN 0134494164.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>cleanarchitecture</category>
      <category>dart</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Arquitetura Limpa: Aplicando com Flutter</title>
      <dc:creator>Rubem Vasconcelos</dc:creator>
      <pubDate>Fri, 01 Jul 2022 21:21:07 +0000</pubDate>
      <link>https://dev.to/rubemfsv/arquitetura-limpa-uso-com-flutter-2i68</link>
      <guid>https://dev.to/rubemfsv/arquitetura-limpa-uso-com-flutter-2i68</guid>
      <description>&lt;p&gt;Antes de iniciar a leitura desse texto, é recomendável ter noções de alguns conceitos básicos como &lt;a href="https://dev.to/rubemfsv/arquitetura-limpa-o-conceito-por-tras-do-codigo-4kdf"&gt;Arquitetura Limpa&lt;/a&gt; e &lt;a href="https://dev.to/rubemfsv/solid-o-conceito-por-tras-do-codigo-5fd1"&gt;SOLID&lt;/a&gt;, pois eles facilitam o entendimento do que será apresentado.&lt;/p&gt;

&lt;p&gt;Este texto tem dois propósitos: I. Mostrar uma divisão arquitetural de uma aplicação Flutter usando Arquitetura Limpa; II. Guiar a implementação de novas features nesta arquitetura proposta.&lt;/p&gt;

&lt;p&gt;O código analisado é baseado na abordagem de Arquitetura Limpa proposta por &lt;a href="https://www.linkedin.com/in/rmanguinho/" rel="noopener noreferrer"&gt;Rodrigo Manguinho&lt;/a&gt; em seu &lt;a href="https://rmanguinho.github.io" rel="noopener noreferrer"&gt;curso de Flutter&lt;/a&gt;. Sua abordagem segue alinhada com a proposta original de &lt;a href="http://cleancoder.com/products" rel="noopener noreferrer"&gt;Robert Martin&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Divisão Arquitetural
&lt;/h2&gt;

&lt;p&gt;O primeiro passo é analisar como é feita a divisão.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;android/
ios/
lib/
  data/
    cache/
    http/
    models/
    usecases/
  domain/
    entities/
    helpers/
    usecases/
  infra/
    cache/
    http/
  main/
    builders/
    composites/
    decorators/
    factories/
      cache/
      http/
      pages/
      usecases/
    main.dart
  presentation/
    mixins/
    presenters/
    protocols/
  ui/
    assets/
    components/
    helpers/
    mixins/
    pages/
  validation/
    protocols/
    validators/
requirements/
    bdd_specs/
    checklists/
    usecases/
test/
    data/
    domain/
    infra/
    main/
    mocks/
    presentation/
    ui/
    validation/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E a partir daí, podemos fazer associações com a &lt;a href="https://dev.to/rubemfsv/arquitetura-limpa-o-conceito-por-tras-do-codigo-4kdf"&gt;teoria da Arquitetura Limpa&lt;/a&gt; para uma compreensão mais fácil da &lt;a href="https://dev.to/rubemfsv/arquitetura-limpa-camadas-e-limites-22p9"&gt;divisão de responsabilidades&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A seguir, vamos ver em detalhes o propósito de cada estrutura de arquivos.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Android&lt;/strong&gt;: Contém os arquivos necessários para buildar a aplicação em sistemas android.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iOS&lt;/strong&gt;: Contém os arquivos necessários para buildar a aplicação em sistemas iOS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lib&lt;/strong&gt;: Contém todos os arquivos necessários para a aplicação.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data&lt;/strong&gt;: A pasta data representa a camada de dados da Arquitetura Limpa, sendo dependente da camada de domínio. Contém as implementações das regras de negócio que são declaradas no &lt;em&gt;domain&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain&lt;/strong&gt;: Representa a camada de domínio da Arquitetura Limpa, a camada mais interna da aplicação, não apresentando dependência com nenhuma outra camada, onde contém as regras de negócio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infra&lt;/strong&gt;: Essa pasta contém as implementações referentes ao protocolo HTTP e ao cache, também é único local onde terá acesso a dependências externas relacionadas para esses dois itens citados.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main&lt;/strong&gt;: Corresponde a camada principal da aplicação, ponto que ocorre a integração das interfaces desenvolvidas na camada de UI, com as regras de negócio criadas nas pastas que representam as camadas mais internas da Arquitetura Limpa. Tudo isso se dá devido ao uso de padrões de projetos como Factory Method, Composite e Builder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Presentation&lt;/strong&gt;: Nessa camada é onde os dados são preparados para serem consumidos na UI, tratando as lógicas por trás das telas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI&lt;/strong&gt;: Contém os componentes e interfaces visuais que são vistas no sistema, é aqui que propriamente são criadas as telas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation&lt;/strong&gt;: Onde contém as implementações das validações utilizadas nos campos (ex: quantidade mínima de carácteres, campo obrigatório, email válido, dentre outros).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Requirements&lt;/strong&gt;: Contém os requisitos do sistema documentados, essa pasta pode ou não ter todas as subpastas a seguir, depende muito de como o time trabalha.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bdd_specs&lt;/strong&gt;: Contém os arquivos escritos em linguagem Gherkin para descrever o comportamento esperado do sistema.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checklist&lt;/strong&gt;: Contém a descrição do comportamento das páginas, com o intuito de facilitar durante os testes unitários, para saber o que validar e o que esperar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usecases&lt;/strong&gt;: Contém o comportamento esperado dos casos de uso do sistema, onde descreve as variações das regras de negócio para facilitar os testes unitários e implementação.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Test&lt;/strong&gt;: Contém todos os testes unitários da aplicação, cada pasta interna representa a camada que os testes pertencem, e a pasta de &lt;em&gt;mocks&lt;/em&gt; contém os mocks utilizados nos testes.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Guia de Implementação
&lt;/h2&gt;

&lt;p&gt;Após compreender a razão da divisão e quais responsabilidades estão contidas em cada pasta da estrutura, será descrita uma sequência lógica recomendada para um melhor desempenho de implementação utilizando esta arquitetura. &lt;/p&gt;

&lt;p&gt;Para finalidade de simplificar a explicação, não será descrito em detalhes os testes unitários. No entanto, é fortemente recomendado começar pelos testes unitários antes do desenvolvimento (TDD) de cada passo utilizando os &lt;em&gt;requirements&lt;/em&gt; para embasar os cenários.&lt;/p&gt;

&lt;p&gt;A demonstração a seguir é da criação do fluxo de Login para entrar em uma aplicação.&lt;/p&gt;

&lt;h3&gt;
  
  
  Primeiro passo: Criar as regras de negócio na camada de domínio
&lt;/h3&gt;

&lt;p&gt;Dentro de lib/domain/usecases, criar o authentication.dart. Esse arquivo vai ser uma classe abstrata que vai descrever a regra de negócio da autenticação.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import '../entities/entities.dart';

abstract class Authentication {
  Future&amp;lt;AccountEntity&amp;gt; auth(AuthenticationParams params);
}

class AuthenticationParams {
  final String email;
  final String password;

  AuthenticationParams({required this.email, required this.password});
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como vemos, é uma classe abstrata que tem um método &lt;code&gt;auth()&lt;/code&gt; que recebe os parâmetros &lt;em&gt;AuthenticationParams&lt;/em&gt; que são declarados abaixo (email e password), e espera retornar um &lt;em&gt;AccountEntity&lt;/em&gt; de maneira assíncrona através do &lt;em&gt;Future&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;O &lt;em&gt;AccountEntity&lt;/em&gt; é uma classe criada em lib/domain/entities que representa o token que é retornado após a autenticação para persistir a sessão.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class AccountEntity {
  final String token;

  AccountEntity({required this.token});
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Segundo passo: Implementar as regras na camada de dados
&lt;/h3&gt;

&lt;p&gt;Nessa camada, criamos o caso de uso para implementar a regra criada anteriormente na camada de domínio, porém dentro de lib/data/usecases.&lt;/p&gt;

&lt;p&gt;O arquivo costuma ficar como o exemplo abaixo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import '../../../domain/entities/entities.dart';
import '../../../domain/helpers/helpers.dart';
import '../../../domain/usecases/usecases.dart';

import '../../http/http.dart';
import '../../models/models.dart';

class RemoteAuthentication implements Authentication {
  final HttpClient httpClient;
  final String url;

  RemoteAuthentication({required this.httpClient, required this.url});

  Future&amp;lt;AccountEntity&amp;gt; auth(AuthenticationParams params) async {
    final body = RemoteAuthenticationParams.fromDomain(params).toJson();
    try {
      final hpptResponse =
          await httpClient.request(url: url, method: 'post', body: body);

      return RemoteAccountModel.fromJson(hpptResponse).toEntity();
    } on HttpError catch (error) {
      throw error == HttpError.unauthorized
          ? DomainError.invalidCredentials
          : DomainError.unexpected;
    }
  }
}

class RemoteAuthenticationParams {
  final String email;
  final String password;

  RemoteAuthenticationParams({required this.email, required this.password});

  factory RemoteAuthenticationParams.fromDomain(AuthenticationParams params) =&amp;gt;
      RemoteAuthenticationParams(
          email: params.email, password: params.password);

  Map toJson() =&amp;gt; {'email': email, 'password': password};
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como podemos observar, a classe &lt;em&gt;RemoteAuthentication&lt;/em&gt; implementa a classe abstrata &lt;em&gt;Authentication&lt;/em&gt;, recebendo o cliente HTTP e a url para a requisição. No método &lt;code&gt;auth()&lt;/code&gt; ele recebe os parâmetros, e chama a factory &lt;code&gt;RemoteAuthenticationParams.fromDomain(params)&lt;/code&gt; criada abaixo com o propósito de converter o que vem no formato padrão para o formato json para ser enviado na requisição HTTP dentro do body. Após isso, é feita a requisição e armazenado o valor retornado em &lt;em&gt;httpResponse&lt;/em&gt;, e essa &lt;em&gt;httpResponse&lt;/em&gt; é retornada no método dentro de um model com o intuito de converter o resultado para o formato padrão de se trabalhar (entity) através de &lt;code&gt;RemoteAccountModel.fromJson(hpptResponse).toEntity()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Essa factory e model são criadas nessa camada com o propósito de não poluir a camada de domínio, pois o que acontece na camada de dados não deve influenciar o que acontece na de domínio.&lt;/p&gt;

&lt;p&gt;Para fim de curiosidade, a implementação do &lt;em&gt;RemoteAccountModel&lt;/em&gt; está abaixo. Ele recebe um accessToken no formato de json e o converte para um Entity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import '../../domain/entities/entities.dart';
import '../http/http.dart';

class RemoteAccountModel {
  final String accessToken;

  RemoteAccountModel(this.accessToken);

  factory RemoteAccountModel.fromJson(Map json) {
    if (!json.containsKey('accessToken')) {
      throw HttpError.invalidData;
    }
    return RemoteAccountModel(json['accessToken']);
  }

  AccountEntity toEntity() =&amp;gt; AccountEntity(token: accessToken);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Terceiro passo: Implementar as telas na camada de UI
&lt;/h3&gt;

&lt;p&gt;Para simplificar o entendimento, será apresentado apenas trechos de códigos referentes a chamada do método de autenticação. A tela de Login contém mais ações e detalhes que vão além da autenticação. Levar em consideração o protótipo da tela abaixo para facilitar a visualização.&lt;/p&gt;

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

&lt;p&gt;Em lib/ui/pages/ será necessário ao menos dois arquivos: I. &lt;code&gt;login_page.dart&lt;/code&gt;, que será a página de Login; II. &lt;code&gt;login_presenter.dart&lt;/code&gt; que conterá a classe abstrata com os métodos e streams que são utilizados na página, e a implementação dessa classe abstrata ocorre na camada de presentation.&lt;/p&gt;

&lt;p&gt;O arquivo &lt;code&gt;login_presenter.dart&lt;/code&gt; é similar ao exemplo abaixo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';

abstract class LoginPresenter implements Listenable {
  void validateEmail(String email);
  void validatePassword(String password);

  Future&amp;lt;void&amp;gt; auth();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E o código abaixo é do botão de Login.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class LoginButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final presenter = Provider.of&amp;lt;LoginPresenter&amp;gt;(context);

    return StreamBuilder&amp;lt;bool&amp;gt;(
      stream: presenter.isFormValidStream,
      builder: (context, snapshot) {
        return ElevatedButton(
          onPressed: snapshot.data == true ? presenter.auth : null,
          child: Text(R.translations.enterButtonText.toUpperCase()),
        );
      },
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Onde o &lt;code&gt;auth()&lt;/code&gt; clicado no &lt;code&gt;onPressed&lt;/code&gt; chama o presenter para fazer a autenticação. Nesse caso não está passando parâmetros no &lt;code&gt;auth()&lt;/code&gt; por os parâmetros serem pegos no presenter durante a interação com a tela (são usados os &lt;code&gt;validateEmail()&lt;/code&gt; e &lt;code&gt;validatePassword()&lt;/code&gt; declarados acima no presenter da página). Veremos mais detalhes no próximo passo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quarto passo: Implementar a classe abstrata do presenter da UI na camada de presentation
&lt;/h3&gt;

&lt;p&gt;Para facilitar o trabalho com Streams, é recomendado o uso da biblioteca &lt;a href="https://pub.dev/packages/get" rel="noopener noreferrer"&gt;GetX&lt;/a&gt; (ou alguma outra da sua preferência) para deixar menos verboso. A escolha pela &lt;em&gt;GetX&lt;/em&gt; é devido ao seu grande suporte e estar em constante atualização.&lt;/p&gt;

&lt;p&gt;Em lib/presentation/presenters é criado o &lt;code&gt;getx_login_presenter.dart&lt;/code&gt;. É uma classe &lt;em&gt;GetxLoginPresenter&lt;/em&gt; que extende o &lt;em&gt;GetxController&lt;/em&gt; e implementa o &lt;em&gt;LoginPresenter&lt;/em&gt;. Apesar do exemplo abaixo ter &lt;em&gt;Validation&lt;/em&gt; e &lt;em&gt;SaveCurrentAccount&lt;/em&gt;, focaremos no &lt;em&gt;Authentication&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'dart:async';
import 'package:get/get.dart';

import '../../ui/pages/login/login_presenter.dart';
import '../../domain/helpers/domain_error.dart';
import '../../domain/usecases/usecases.dart';
import '../protocols/protocols.dart';

class GetxLoginPresenter extends GetxController
  implements LoginPresenter {
  final Validation validation;
  final Authentication authentication;
  final SaveCurrentAccount saveCurrentAccount;

  String? _email;
  String? _password;

  GetxLoginPresenter({
    required this.validation,
    required this.authentication,
    required this.saveCurrentAccount,
  });

  void validateEmail(String email) {
    _email = email;
    // Validation code here
  }

  void validatePassword(String password) {
    _password = password;
    // Validation code here
  }

  Future&amp;lt;void&amp;gt; auth() async {
    try {
      final account = await authentication.auth(AuthenticationParams(
        email: _email!,
        password: _password!,
      ));
      await saveCurrentAccount.save(account);
    } on DomainError catch (error) {
      // Handle errors here
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nos métodos &lt;code&gt;validateEmail(String email)&lt;/code&gt; e &lt;code&gt;validatePassword(String password)&lt;/code&gt; são capturados o email e senha do usuário ao digitar nos &lt;em&gt;Inputs&lt;/em&gt; da tela de Login. Em &lt;code&gt;auth()&lt;/code&gt;, é onde há a chamada ao método de autenticação implementados anteriormente, que recebem o email e senha capturados pelos validates anteriores. &lt;/p&gt;

&lt;p&gt;A chamada &lt;code&gt;authentication.auth(AuthenticationParams(email: _email!, password: _password!))&lt;/code&gt; retorna um token (como explicado anteriormente), é atribuído a uma variável chamada &lt;em&gt;account&lt;/em&gt; e em seguida salva em cache através do &lt;code&gt;saveCurrentAccount.save(account)&lt;/code&gt; (não foi explicado sobre esse ponto nesse texto, porém é através dele que há a persistência da sessão do usuário no aparelho).&lt;/p&gt;

&lt;h3&gt;
  
  
  Quinto passo: Conectar todas as camadas para que as requisições funcionem
&lt;/h3&gt;

&lt;p&gt;Após tudo implementado, agora basta conectar todas as partes. Para isso, é utilizado o padrão de projeto &lt;a href="https://refactoring.guru/pt-br/design-patterns/factory-method" rel="noopener noreferrer"&gt;Factory Method&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Dentro de lib/main/factories/usecases, criamos a &lt;em&gt;factory&lt;/em&gt; do caso de uso que está sendo implementado. No caso desse exemplo, é o relacionado a autenticação. &lt;/p&gt;

&lt;p&gt;É criado o &lt;em&gt;authentication_factory.dart&lt;/em&gt;, que retorna o &lt;em&gt;RemoteAuthentication&lt;/em&gt; que recebe como parâmetro a &lt;em&gt;factory&lt;/em&gt; do &lt;em&gt;Http Client&lt;/em&gt; e a &lt;em&gt;factory&lt;/em&gt; que cria a URL. É passado como parâmetro a URL da API que deseja requisitar junto da &lt;em&gt;factory&lt;/em&gt; que cria a URL. No exemplo é a URL que finaliza com &lt;em&gt;/login&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import '../../../domain/usecases/usecases.dart';
import '../../../data/usecases/usecases.dart';
import '../factories.dart';

Authentication makeRemoteAuthentication() {
  return RemoteAuthentication(
    httpClient: makeHttpAdapter(),
    url: makeApiUrl('login'),
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após isso, em lib/main/factories/pages, é criada a pasta para as factories do Login. Para essa explicação, focaremos no &lt;em&gt;login_page_factory.dart&lt;/em&gt; e &lt;em&gt;login_presenter_factory.dart&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Primeiro, é feito a &lt;em&gt;login_presenter_factory.dart&lt;/em&gt;, que é um Widget que retorna o &lt;em&gt;GetxLoginPresenter&lt;/em&gt;. Esse &lt;em&gt;presenter&lt;/em&gt; foi criado anteriormente, e dentro dele é injetando as &lt;em&gt;factories&lt;/em&gt; de autenticação (que foi criado logo a cima) e as de validação e salvar token no cache (que não foram abordadas nesse texto, porém seguem as mesmas premissas da &lt;em&gt;factory&lt;/em&gt; de autenticação).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import '../../factories.dart';
import '../../../../presentation/presenters/presenters.dart';
import '../../../../ui/pages/pages.dart';

LoginPresenter makeGetxLoginPresenter() {
  return GetxLoginPresenter(
    authentication: makeRemoteAuthentication(),
    validation: makeLoginValidation(),
    saveCurrentAccount: makeLocalSaveCurrentAccount(),
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Em seguida, seguindo a mesma linha de pensamento, é feita a &lt;em&gt;factory&lt;/em&gt; da página de Login. Como a &lt;em&gt;factory&lt;/em&gt; do &lt;em&gt;presenter&lt;/em&gt;, é um Widget, mas nesse caso retorna a &lt;em&gt;LoginPage&lt;/em&gt; com a &lt;em&gt;factory&lt;/em&gt; do &lt;em&gt;presenter&lt;/em&gt;  criado anteriormente sendo injetado como parâmetro.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';
import '../../../../ui/pages/pages.dart';
import '../../factories.dart';

Widget makeLoginPage() {
  return LoginPage(makeGetxLoginPresenter());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sexto passo: Aplicar a tela criada na aplicação
&lt;/h3&gt;

&lt;p&gt;Por fim, é necessário chamar a &lt;em&gt;factory&lt;/em&gt; do Login na aplicação para que ela consiga ser acessada pelo usuário. &lt;/p&gt;

&lt;p&gt;No arquivo &lt;em&gt;main.dart&lt;/em&gt; que fica localizado em lib/main, adicionar dentro do array de páginas (&lt;em&gt;getPages&lt;/em&gt;) a &lt;em&gt;factory&lt;/em&gt; página criada. É passado no &lt;em&gt;name&lt;/em&gt; a rota, no caso é a &lt;em&gt;/login&lt;/em&gt;, e a página, que no caso é o ponteiro para a &lt;em&gt;factory&lt;/em&gt; &lt;code&gt;makeLoginPage&lt;/code&gt;. Essa lógica é utilizada com todas as outras páginas. O código fica como está abaixo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';

import '../ui/components/components.dart';
import 'factories/factories.dart';

void main() {
  runApp(App());
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
    final routeObserver = Get.put&amp;lt;RouteObserver&amp;gt;(RouteObserver&amp;lt;PageRoute&amp;gt;());

    return GetMaterialApp(
      title: 'Flutter Clean App',
      debugShowCheckedModeBanner: false,
      theme: makeAppTheme(),
      navigatorObservers: [routeObserver],
      initialRoute: '/',
      getPages: [
        GetPage(
            name: '/login', page: makeLoginPage, transition: Transition.fadeIn),
      ],
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Apesar de ser um pouco complexo de se entender no começo e parecer um pouco redundante, as abstrações são necessárias. São aplicados diversos padrões de projetos para garantir a qualidade e independência do código, facilitando a evolução e manutenção.&lt;/p&gt;

&lt;p&gt;Seguir o processo de desenvolvimento e entender o porquê está fazendo de tal maneira facilita a produção do código. Após um tempo acaba sendo feito de maneira natural, pois tem um processo linear de desenvolvimento: I. Caso de uso na camada de domínio; II. Caso de uso na camada de dados; III. Criação da UI; IV. Criação das lógicas para chamada da requisição na camada de presentation; V. Criação das &lt;em&gt;factories&lt;/em&gt; para integrar todas as camadas na camada principal; VI. E a chamada da &lt;em&gt;factory&lt;/em&gt; principal nas rotas da aplicação para que seja disponível para o usuário. &lt;/p&gt;

&lt;p&gt;Apesar de ter muitas partes abstraídas, é recomendável a leitura do código das partes ocultas para uma maior compreensão. &lt;a href="https://github.com/rmanguinho/clean-flutter-app" rel="noopener noreferrer"&gt;Nesse repositório&lt;/a&gt; do curso do Rodrigo Manguinho você consegue ter acesso a esses códigos abstraídos. &lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Rodrigo Manguinho &lt;a href="https://github.com/rmanguinho/clean-flutter-app" rel="noopener noreferrer"&gt;https://github.com/rmanguinho/clean-flutter-app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MARTIN, Robert C. Clean Architecture: A Craftsman’s Guide to Software Structure and Design. 1st. ed. USA: Prentice Hall Press, 2017. ISBN 0134494164.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>braziliandevs</category>
      <category>flutter</category>
      <category>cleanarchitecture</category>
      <category>dart</category>
    </item>
    <item>
      <title>Clean Architecture: Layers and Boundaries</title>
      <dc:creator>Rubem Vasconcelos</dc:creator>
      <pubDate>Thu, 23 Jun 2022 17:26:20 +0000</pubDate>
      <link>https://dev.to/rubemfsv/clean-architecture-layers-and-boundaries-13ed</link>
      <guid>https://dev.to/rubemfsv/clean-architecture-layers-and-boundaries-13ed</guid>
      <description>&lt;p&gt;After understanding about the key concepts of &lt;a href="https://dev.to/rubemfsv/clean-architecture-the-concept-behind-the-code-52do"&gt;Clean Architecture&lt;/a&gt; and some important concepts that surround it such as &lt;a href="https://dev.to/rubemfsv/solid-the-concept-behind-the-code-3h4o"&gt;SOLID&lt;/a&gt; and &lt;a href="https://dev.to/rubemfsv/component-principles-the-concept-behind-the-code-dn6"&gt;component principles&lt;/a&gt;, it is easier to understand about its division into layers and its boundaries.&lt;/p&gt;

&lt;p&gt;A system can be defined by three basic components: the UI, the business rules and the database - and in larger systems there are many more components. To explain the concept of layers and boundaries, the example made by MARTIN (2017) will be used, which describes the game of the 1970s called Hunt the Wumpus.&lt;/p&gt;

&lt;p&gt;The rules of the game are irrelevant for this example, as the focus will be on the implementation itself.&lt;/p&gt;

&lt;p&gt;Considering that a text-based UI is used, it is necessary to decouple the game rules so it can have different languages. The rules will communicate with the UI using an API, regardless of language, and the UI will translate to the requested language. In this way all UI components will be able to make use of the same rules of the game. Assuming that the game state is kept in a persistent store, there is no need for the game rules to know the details, so an API will be created to communicate these rules with the data store component.&lt;/p&gt;

&lt;p&gt;The game rules must also not know about the different types of data storage, so the dependencies must be correctly addressed following the Dependency Rule, as seen in the game's basic structure figure (source: MARTIN (2017)).&lt;/p&gt;

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

&lt;p&gt;After this brief description and based on &lt;a href="https://dev.to/rubemfsv/clean-architecture-the-concept-behind-the-code-52do"&gt;Clean Architecture concepts&lt;/a&gt;, it is imagined that clean architecture would be easily applied in this example. However, not all architectural boundaries are clear.&lt;/p&gt;

&lt;p&gt;In this example, the language is not the only UI change axis, the communication mechanism can also be varied, such as a chat application, a normal window or text messages. There are countless possibilities for change, which shows that there is an architectural limit defined by these axes of change. Therefore, it is necessary to create an API to cross this limit and isolate the language from the communication mechanism.&lt;/p&gt;

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

&lt;p&gt;The figure above reflects the reformulated game structure (source: MARTIN (2017)), the dotted rectangles are the APIs and the rectangle written "Game Rules" contains the game rules. The Rules of the Game component is positioned at the top of the diagram, as it contains the highest level policies. This organization divides the data flow, being the left side to communicate with the user and the right side for data persistence. As systems grow, the component structure can be divided into several flows with different hierarchies, which makes the architecture increasingly complex.&lt;/p&gt;

&lt;p&gt;MARTIN (2017) concludes this issue by stating that the role of the software architect is to assess costs, determine architectural limits and which of these limits should be fully implemented, which should be partially implemented and which should be ignored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This simple example is meant to show that there are architectural boundaries everywhere, and it's up to software architects to recognize the needs.&lt;/p&gt;

&lt;p&gt;It is also necessary to know that implementing these boundaries is something expensive, but at the same time, if not implemented early in development, they can have even greater costs over time.&lt;/p&gt;

&lt;p&gt;Martin reinforces that these decisions are not made just once, that it is necessary to observe the evolution of the system, analyze where boundaries may be necessary and what can happen if they do not have well-defined boundaries. He also reinforces the importance of comparing the costs of implementing these boundaries with the cost of ignoring them, always looking to implement the right boundaries at the sweet spot where the cost of implementation is less than the cost of ignoring them.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;MARTIN, Robert C. Clean Architecture: A Craftsman’s Guide to Software Structure and Design. 1st. ed. USA: Prentice Hall Press, 2017. ISBN 0134494164.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>cleanarchitecture</category>
      <category>tutorial</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
