<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Birkaran Sachdev - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Birkaran Sachdev - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 14 May 2026 11:46:41 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/birkssachdev/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Scalable URL Shortener with Distributed Caching Using Redis ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, we'll build a scalable URL shortening service using Node.js and Redis. This service will leverage distributed caching to handle high traffic efficiently, reduce latency, and scale seamlessly. We'll explore key concepts such as consi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-scalable-url-shortener-with-distributed-caching-using-redis/</link>
                <guid isPermaLink="false">673cab723502f2a00d61ece1</guid>
                
                    <category>
                        <![CDATA[ Redis ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ consistent hashing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cache Invalidation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ sharding ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Url Shortener ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Birkaran Sachdev ]]>
                </dc:creator>
                <pubDate>Tue, 19 Nov 2024 15:14:58 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/b9-odQi5oDo/upload/934c8b697ce5ce612a217d47ddf5430d.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, we'll build a scalable URL shortening service using Node.js and Redis. This service will leverage distributed caching to handle high traffic efficiently, reduce latency, and scale seamlessly. We'll explore key concepts such as consistent hashing, cache invalidation strategies, and sharding to ensure the system remains fast and reliable.</p>
<p>By the end of this guide, you'll have a fully functional URL shortener service that uses distributed caching to optimize performance. We'll also create an interactive demo where users can input URLs and see real-time metrics like cache hits and misses.</p>
<h3 id="heading-what-you-will-learn">What You Will Learn</h3>
<ul>
<li><p>How to build a URL shortener service using <strong>Node.js</strong> and <strong>Redis</strong>.</p>
</li>
<li><p>How to implement <strong>distributed caching</strong> to optimize performance.</p>
</li>
<li><p>Understanding <strong>consistent hashing</strong> and <strong>cache invalidation strategies</strong>.</p>
</li>
<li><p>Using <strong>Docker</strong> to simulate multiple Redis instances for sharding and scaling.</p>
</li>
</ul>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>Before starting, make sure you have the following installed:</p>
<ul>
<li><p>Node.js (v14 or higher)</p>
</li>
<li><p>Redis</p>
</li>
<li><p>Docker</p>
</li>
<li><p>Basic knowledge of JavaScript, Node.js, and Redis.</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-project-overview">Project Overview</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-1-setting-up-the-project">Step 1: Setting Up the Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-setting-up-redis-instances">Step 2: Setting Up Redis Instances</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-implementing-the-url-shortener-service">Step 3: Implementing the URL Shortener Service</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-implementing-cache-invalidation">Step 4: Implementing Cache Invalidation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-monitoring-cache-metrics">Step 5: Monitoring Cache Metrics</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-6-testing-the-application">Step 6: Testing the Application</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion-what-youve-learned">Conclusion: What You’ve Learned</a></p>
</li>
</ul>
<h2 id="heading-project-overview">Project Overview</h2>
<p>We'll build a URL shortener service where:</p>
<ol>
<li><p>Users can shorten long URLs and retrieve the original URLs.</p>
</li>
<li><p>The service uses <strong>Redis caching</strong> to store mappings between shortened URLs and original URLs.</p>
</li>
<li><p>The cache is distributed across multiple Redis instances to handle high traffic.</p>
</li>
<li><p>The system will demonstrate <strong>cache hits</strong> and <strong>misses</strong> in real-time.</p>
</li>
</ol>
<h3 id="heading-system-architecture">System Architecture</h3>
<p>To ensure scalability and performance, we'll divide our service into the following components:</p>
<ol>
<li><p><strong>API Server</strong>: Handles requests for shortening and retrieving URLs.</p>
</li>
<li><p><strong>Redis Caching Layer</strong>: Uses multiple Redis instances for distributed caching.</p>
</li>
<li><p><strong>Docker</strong>: Simulates a distributed environment with multiple Redis containers.</p>
</li>
</ol>
<h2 id="heading-step-1-setting-up-the-project">Step 1: Setting Up the Project</h2>
<p>Let's set up our project by initializing a Node.js application:</p>
<pre><code class="lang-javascript">mkdir scalable-url-shortener
cd scalable-url-shortener
npm init -y
</code></pre>
<p>Now, install the necessary dependencies:</p>
<pre><code class="lang-javascript">npm install express redis shortid dotenv
</code></pre>
<ul>
<li><p><code>express</code>: A lightweight web server framework.</p>
</li>
<li><p><code>redis</code>: To handle caching.</p>
</li>
<li><p><code>shortid</code>: For generating short, unique IDs.</p>
</li>
<li><p><code>dotenv</code>: For managing environment variables.</p>
</li>
</ul>
<p>Create a <strong>.env</strong> file in the root of your project:</p>
<pre><code class="lang-javascript">PORT=<span class="hljs-number">3000</span>
REDIS_HOST_1=localhost
REDIS_PORT_1=<span class="hljs-number">6379</span>
REDIS_HOST_2=localhost
REDIS_PORT_2=<span class="hljs-number">6380</span>
REDIS_HOST_3=localhost
REDIS_PORT_3=<span class="hljs-number">6381</span>
</code></pre>
<p>These variables define the Redis hosts and ports we'll be using.</p>
<h2 id="heading-step-2-setting-up-redis-instances">Step 2: Setting Up Redis Instances</h2>
<p>We'll use Docker to simulate a distributed environment with multiple Redis instances.</p>
<p>Run the following commands to start three Redis containers:</p>
<pre><code class="lang-javascript">docker run -p <span class="hljs-number">6379</span>:<span class="hljs-number">6379</span> --name redis1 -d redis
docker run -p <span class="hljs-number">6380</span>:<span class="hljs-number">6379</span> --name redis2 -d redis
docker run -p <span class="hljs-number">6381</span>:<span class="hljs-number">6379</span> --name redis3 -d redis
</code></pre>
<p>This will set up three Redis instances running on different ports. We'll use these instances to implement <strong>consistent hashing</strong> and sharding.</p>
<h2 id="heading-step-3-implementing-the-url-shortener-service">Step 3: Implementing the URL Shortener Service</h2>
<p>Let's create our main application file, <strong>index.js</strong>:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();
<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> redis = <span class="hljs-built_in">require</span>(<span class="hljs-string">'redis'</span>);
<span class="hljs-keyword">const</span> shortid = <span class="hljs-built_in">require</span>(<span class="hljs-string">'shortid'</span>);

<span class="hljs-keyword">const</span> app = express();
app.use(express.json());

<span class="hljs-keyword">const</span> redisClients = [
  redis.createClient({ <span class="hljs-attr">host</span>: process.env.REDIS_HOST_1, <span class="hljs-attr">port</span>: process.env.REDIS_PORT_1 }),
  redis.createClient({ <span class="hljs-attr">host</span>: process.env.REDIS_HOST_2, <span class="hljs-attr">port</span>: process.env.REDIS_PORT_2 }),
  redis.createClient({ <span class="hljs-attr">host</span>: process.env.REDIS_HOST_3, <span class="hljs-attr">port</span>: process.env.REDIS_PORT_3 })
];

<span class="hljs-comment">// Hash function to distribute keys among Redis clients</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRedisClient</span>(<span class="hljs-params">key</span>) </span>{
  <span class="hljs-keyword">const</span> hash = key.split(<span class="hljs-string">''</span>).reduce(<span class="hljs-function">(<span class="hljs-params">acc, char</span>) =&gt;</span> acc + char.charCodeAt(<span class="hljs-number">0</span>), <span class="hljs-number">0</span>);
  <span class="hljs-keyword">return</span> redisClients[hash % redisClients.length];
}

<span class="hljs-comment">// Endpoint to shorten a URL</span>
app.post(<span class="hljs-string">'/shorten'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { url } = req.body;
  <span class="hljs-keyword">if</span> (!url) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).send(<span class="hljs-string">'URL is required'</span>);

  <span class="hljs-keyword">const</span> shortId = shortid.generate();
  <span class="hljs-keyword">const</span> redisClient = getRedisClient(shortId);

  <span class="hljs-keyword">await</span> redisClient.set(shortId, url);
  res.json({ <span class="hljs-attr">shortUrl</span>: <span class="hljs-string">`http://localhost:<span class="hljs-subst">${process.env.PORT}</span>/<span class="hljs-subst">${shortId}</span>`</span> });
});

<span class="hljs-comment">// Endpoint to retrieve the original URL</span>
app.get(<span class="hljs-string">'/:shortId'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { shortId } = req.params;
  <span class="hljs-keyword">const</span> redisClient = getRedisClient(shortId);

  redisClient.get(shortId, <span class="hljs-function">(<span class="hljs-params">err, url</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (err || !url) {
      <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">404</span>).send(<span class="hljs-string">'URL not found'</span>);
    }
    res.redirect(url);
  });
});

app.listen(process.env.PORT, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server running on port <span class="hljs-subst">${process.env.PORT}</span>`</span>);
});
</code></pre>
<p>As you can see in this code, we have:</p>
<ol>
<li><p><strong>Consistent Hashing</strong>:</p>
<ul>
<li><p>We distribute keys (shortened URLs) across multiple Redis clients using a simple hash function.</p>
</li>
<li><p>The hash function ensures that URLs are distributed evenly across the Redis instances.</p>
</li>
</ul>
</li>
<li><p><strong>URL Shortening</strong>:</p>
<ul>
<li><p>The <strong>/shorten</strong> endpoint accepts a long URL and generates a short ID using the <strong>shortid</strong> library.</p>
</li>
<li><p>The shortened URL is stored in one of the Redis instances using our hash function.</p>
</li>
</ul>
</li>
<li><p><strong>URL Redirection</strong>:</p>
<ul>
<li><p>The <strong>/:shortId</strong> endpoint retrieves the original URL from the cache and redirects the user.</p>
</li>
<li><p>If the URL is not found in the cache, a <strong>404</strong> response is returned.</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-step-4-implementing-cache-invalidation">Step 4: Implementing Cache Invalidation</h2>
<p>In a real-world application, URLs may expire or change over time. To handle this, we need to implement <strong>cache invalidation</strong>.</p>
<h3 id="heading-adding-expiry-to-cached-urls">Adding Expiry to Cached URLs</h3>
<p>Let's modify our <strong>index.js</strong> file to set an expiration time for each cached entry:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Endpoint to shorten a URL with expiration</span>
app.post(<span class="hljs-string">'/shorten'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { url, ttl } = req.body; <span class="hljs-comment">// ttl (time-to-live) is optional</span>
  <span class="hljs-keyword">if</span> (!url) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).send(<span class="hljs-string">'URL is required'</span>);

  <span class="hljs-keyword">const</span> shortId = shortid.generate();
  <span class="hljs-keyword">const</span> redisClient = getRedisClient(shortId);

  <span class="hljs-keyword">await</span> redisClient.set(shortId, url, <span class="hljs-string">'EX'</span>, ttl || <span class="hljs-number">3600</span>); <span class="hljs-comment">// Default TTL of 1 hour</span>
  res.json({ <span class="hljs-attr">shortUrl</span>: <span class="hljs-string">`http://localhost:<span class="hljs-subst">${process.env.PORT}</span>/<span class="hljs-subst">${shortId}</span>`</span> });
});
</code></pre>
<ul>
<li><p><strong>TTL (Time-To-Live)</strong>: We set a default expiration time of <strong>1 hour</strong> for each shortened URL. You can customize the TTL for each URL if needed.</p>
</li>
<li><p><strong>Cache Invalidation</strong>: When the TTL expires, the entry is automatically removed from the cache.</p>
</li>
</ul>
<h2 id="heading-step-5-monitoring-cache-metrics">Step 5: Monitoring Cache Metrics</h2>
<p>To monitor <strong>cache hits</strong> and <strong>misses</strong>, we’ll add some logging to our endpoints in <strong>index.js</strong>:</p>
<pre><code class="lang-javascript">app.get(<span class="hljs-string">'/:shortId'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { shortId } = req.params;
  <span class="hljs-keyword">const</span> redisClient = getRedisClient(shortId);

  redisClient.get(shortId, <span class="hljs-function">(<span class="hljs-params">err, url</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (err || !url) {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Cache miss for key: <span class="hljs-subst">${shortId}</span>`</span>);
      <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">404</span>).send(<span class="hljs-string">'URL not found'</span>);
    }
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Cache hit for key: <span class="hljs-subst">${shortId}</span>`</span>);
    res.redirect(url);
  });
});
</code></pre>
<p>Here’s what’s going on in this code:</p>
<ul>
<li><p><strong>Cache Hits</strong>: If a URL is found in the cache, it’s a cache hit.</p>
</li>
<li><p><strong>Cache Misses</strong>: If a URL is not found, it’s a cache miss.</p>
</li>
<li><p>This logging will help you monitor the performance of your distributed cache.</p>
</li>
</ul>
<h2 id="heading-step-6-testing-the-application">Step 6: Testing the Application</h2>
<ol>
<li><strong>Start your Redis instances</strong>:</li>
</ol>
<pre><code class="lang-javascript">docker start redis1 redis2 redis3
</code></pre>
<ol start="2">
<li><strong>Run the Node.js server</strong>:</li>
</ol>
<pre><code class="lang-javascript">node index.js
</code></pre>
<ol start="3">
<li><p><strong>Test the endpoints</strong> using <code>curl</code> or Postman:</p>
<ul>
<li><p>Shorten a URL:</p>
<pre><code class="lang-javascript">  POST http:<span class="hljs-comment">//localhost:3000/shorten</span>
  Body: { <span class="hljs-string">"url"</span>: <span class="hljs-string">"https://example.com"</span> }
</code></pre>
</li>
<li><p>Access the shortened URL:</p>
<pre><code class="lang-javascript">  GET http:<span class="hljs-comment">//localhost:3000/{shortId}</span>
</code></pre>
</li>
</ul>
</li>
</ol>
<h2 id="heading-conclusion-what-youve-learned">Conclusion: What You’ve Learned</h2>
<p>Congratulations! You’ve successfully built a scalable <strong>URL shortener service</strong> with <strong>distributed caching</strong> using Node.js and Redis. Throughout this tutorial, you’ve learned how to:</p>
<ol>
<li><p>Implement <strong>consistent hashing</strong> to distribute cache entries across multiple Redis instances.</p>
</li>
<li><p>Optimize your application with <strong>cache invalidation strategies</strong> to keep data up-to-date.</p>
</li>
<li><p>Use <strong>Docker</strong> to simulate a distributed environment with multiple Redis nodes.</p>
</li>
<li><p>Monitor <strong>cache hits and misses</strong> to optimize performance.</p>
</li>
</ol>
<h3 id="heading-next-steps">Next Steps:</h3>
<ul>
<li><p><strong>Add a Database</strong>: Store URLs in a database for persistence beyond the cache.</p>
</li>
<li><p><strong>Implement Analytics</strong>: Track click counts and analytics for shortened URLs.</p>
</li>
<li><p><strong>Deploy to the Cloud</strong>: Deploy your application using Kubernetes for auto-scaling and resilience.</p>
</li>
</ul>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Distributed Rate Limiting System Using Redis and Lua Scripts ]]>
                </title>
                <description>
                    <![CDATA[ In this comprehensive guide, you’ll build a distributed rate limiter using Redis and Lua scripting to control user requests in a high-traffic environment. Rate limiting is crucial in any system to prevent abuse, manage traffic, and protect your resou... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-rate-limiting-system-using-redis-and-lua/</link>
                <guid isPermaLink="false">673ca31d6c7f56d7a860bbdf</guid>
                
                    <category>
                        <![CDATA[ Lua ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Redis ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Birkaran Sachdev ]]>
                </dc:creator>
                <pubDate>Tue, 19 Nov 2024 14:39:25 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/CGWK6k2RduY/upload/a5ac857cec1d18720a060fc5e3462cf3.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this comprehensive guide, you’ll build a distributed rate limiter using Redis and Lua scripting to control user requests in a high-traffic environment.</p>
<p>Rate limiting is crucial in any system to prevent abuse, manage traffic, and protect your resources. By leveraging Redis and Lua, you'll build an efficient, scalable rate limiting system that can handle a large number of requests while keeping your backend services safe.</p>
<p>We will also include an interactive demo where users can simulate traffic, observe rate limits being enforced, and view logs of blocked requests.</p>
<h2 id="heading-what-you-will-learn">What You Will Learn</h2>
<ul>
<li><p>How to build a rate limiting system using Redis.</p>
</li>
<li><p>How to use Lua scripts with Redis to achieve atomic operations.</p>
</li>
<li><p>Understanding Redis data structures for efficient request tracking.</p>
</li>
<li><p>Techniques for handling high traffic in a distributed system.</p>
</li>
<li><p>Using Docker to simulate and scale a distributed rate limiter.</p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before starting, ensure you have the following installed:</p>
<ul>
<li><p>Node.js (v14 or higher)</p>
</li>
<li><p>Redis</p>
</li>
<li><p>Docker (for simulating a distributed environment)</p>
</li>
<li><p>Basic understanding of Node.js, Redis, and Lua scripting.</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-you-will-learn">What You Will Learn</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-project-overview">Project Overview</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-1-how-to-set-up-the-project">Step 1: How to Set Up the Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-how-to-set-up-redis">Step 2: How to Set Up Redis</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-how-to-implement-the-rate-limiter-with-redis-and-lua">Step 3: How to Implement the Rate Limiter with Redis and Lua</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-how-to-create-the-nodejs-api-server">Step 4: How to Create the Node.js API Server</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-how-to-test-the-rate-limiter">Step 5: How to Test the Rate Limiter</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-6-how-to-visualize-rate-limiting-metrics">Step 6: How to Visualize Rate Limiting Metrics</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-7-how-to-deploy-with-docker">Step 7: How to Deploy with Docker</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion-what-youve-learned">Conclusion: What You’ve Learned</a></p>
</li>
</ul>
<h2 id="heading-project-overview">Project Overview</h2>
<p>In this tutorial, you will:</p>
<ol>
<li><p>Build a rate limiter using Redis and Lua to enforce request quotas.</p>
</li>
<li><p>Use Lua scripts to ensure atomic operations, avoiding race conditions.</p>
</li>
<li><p>Implement a token bucket algorithm for rate limiting.</p>
</li>
<li><p>Create an interactive demo to simulate high traffic and visualize rate limiting in action.</p>
</li>
</ol>
<h3 id="heading-system-architecture">System Architecture</h3>
<p>You'll build the system with the following components:</p>
<ol>
<li><p><strong>API Server</strong>: Handles incoming user requests.</p>
</li>
<li><p><strong>Redis</strong>: Stores request data and enforces rate limits.</p>
</li>
<li><p><strong>Lua Scripts</strong>: Ensures atomic updates to Redis for rate limiting.</p>
</li>
<li><p><strong>Docker</strong>: Simulates a distributed environment with multiple instances.</p>
</li>
</ol>
<h2 id="heading-step-1-how-to-set-up-the-project">Step 1: How to Set Up the Project</h2>
<p>Let's start by setting up our Node.js project:</p>
<pre><code class="lang-javascript">mkdir distributed-rate-limiter
cd distributed-rate-limiter
npm init -y
</code></pre>
<p>Next, install the required dependencies:</p>
<pre><code class="lang-javascript">npm install express redis dotenv
</code></pre>
<ul>
<li><p><strong>express</strong>: A lightweight web server framework.</p>
</li>
<li><p><strong>redis</strong>: For interacting with Redis.</p>
</li>
<li><p><strong>dotenv</strong>: For managing environment variables.</p>
</li>
</ul>
<p>Create a <strong>.env</strong> file with the following content:</p>
<pre><code class="lang-javascript">REDIS_HOST=localhost
REDIS_PORT=<span class="hljs-number">6379</span>
PORT=<span class="hljs-number">3000</span>
RATE_LIMIT=<span class="hljs-number">5</span>
TIME_WINDOW=<span class="hljs-number">60</span>
</code></pre>
<p>These variables define the Redis host, port, rate limit (number of allowed requests), and the time window (in seconds).</p>
<h2 id="heading-step-2-how-to-set-up-redis">Step 2: How to Set Up Redis</h2>
<p>Before we dive into the code, ensure that Redis is installed and running on your system. If you don’t have Redis installed, you can use Docker to quickly set it up:</p>
<pre><code class="lang-javascript">docker run -p <span class="hljs-number">6379</span>:<span class="hljs-number">6379</span> --name redis-rate-limiter -d redis
</code></pre>
<h2 id="heading-step-3-how-to-implement-the-rate-limiter-with-redis-and-lua">Step 3: How to Implement the Rate Limiter with Redis and Lua</h2>
<p>To efficiently handle rate limiting, we'll use a token bucket algorithm. In this algorithm:</p>
<ol>
<li><p>Each user has a “bucket” of tokens.</p>
</li>
<li><p>Each request consumes a token.</p>
</li>
<li><p>Tokens refill periodically at a set rate.</p>
</li>
</ol>
<p>To ensure atomicity and avoid race conditions, we'll use Lua scripting with Redis. Lua scripts in Redis execute atomically, which means they can’t be interrupted by other operations while running.</p>
<h3 id="heading-how-to-create-a-lua-script-for-rate-limiting">How to Create a Lua Script for Rate Limiting</h3>
<p>Create a file called <strong>rate_limiter.lua</strong>:</p>
<pre><code class="lang-javascript">local key = KEYS[<span class="hljs-number">1</span>]
local limit = tonumber(ARGV[<span class="hljs-number">1</span>])
local <span class="hljs-built_in">window</span> = tonumber(ARGV[<span class="hljs-number">2</span>])
local current = redis.call(<span class="hljs-string">"get"</span>, key)

<span class="hljs-keyword">if</span> current and tonumber(current) &gt;= limit then
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>
<span class="hljs-keyword">else</span>
    <span class="hljs-keyword">if</span> current then
        redis.call(<span class="hljs-string">"incr"</span>, key)
    <span class="hljs-keyword">else</span>
        redis.call(<span class="hljs-string">"set"</span>, key, <span class="hljs-number">1</span>, <span class="hljs-string">"EX"</span>, <span class="hljs-built_in">window</span>)
    end
    <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
end
</code></pre>
<ol>
<li><p><strong>Inputs</strong>:</p>
<ul>
<li><p><strong>KEYS[1]</strong>: The Redis key representing the user’s request count.</p>
</li>
<li><p><strong>ARGV[1]</strong>: The rate limit (maximum number of allowed requests).</p>
</li>
<li><p><strong>ARGV[2]</strong>: The time window (in seconds) for the rate limit.</p>
</li>
</ul>
</li>
<li><p><strong>Logic</strong>:</p>
<ul>
<li><p>If the user has reached the rate limit, return <code>0</code> (request blocked).</p>
</li>
<li><p>If the user is within the limit, increment their request count or set a new count with an expiration if it's the first request.</p>
</li>
<li><p>Return 1 (request allowed).</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-step-4-how-to-create-the-nodejs-api-server">Step 4: How to Create the Node.js API Server</h2>
<p>Create a file called <strong>server.js</strong>:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();
<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> redis = <span class="hljs-built_in">require</span>(<span class="hljs-string">'redis'</span>);
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);
<span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>);

<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> client = redis.createClient({
  <span class="hljs-attr">host</span>: process.env.REDIS_HOST,
  <span class="hljs-attr">port</span>: process.env.REDIS_PORT
});

<span class="hljs-keyword">const</span> rateLimitScript = fs.readFileSync(path.join(__dirname, <span class="hljs-string">'rate_limiter.lua'</span>), <span class="hljs-string">'utf8'</span>);

<span class="hljs-keyword">const</span> RATE_LIMIT = <span class="hljs-built_in">parseInt</span>(process.env.RATE_LIMIT);
<span class="hljs-keyword">const</span> TIME_WINDOW = <span class="hljs-built_in">parseInt</span>(process.env.TIME_WINDOW);

<span class="hljs-comment">// Middleware for rate limiting</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rateLimiter</span>(<span class="hljs-params">req, res, next</span>) </span>{
  <span class="hljs-keyword">const</span> ip = req.ip;
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> allowed = <span class="hljs-keyword">await</span> client.eval(rateLimitScript, <span class="hljs-number">1</span>, ip, RATE_LIMIT, TIME_WINDOW);
    <span class="hljs-keyword">if</span> (allowed === <span class="hljs-number">1</span>) {
      next();
    } <span class="hljs-keyword">else</span> {
      res.status(<span class="hljs-number">429</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Too many requests. Please try again later.'</span> });
    }
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error in rate limiter:'</span>, err);
    res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Internal server error'</span> });
  }
}

app.use(rateLimiter);

app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  res.send(<span class="hljs-string">'Welcome to the Rate Limited API!'</span>);
});

<span class="hljs-keyword">const</span> PORT = process.env.PORT;
app.listen(PORT, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server running on port <span class="hljs-subst">${PORT}</span>`</span>);
});
</code></pre>
<ol>
<li><p><strong>Rate Limiter Middleware</strong>:</p>
<ul>
<li><p>Retrieves the client's IP address and checks if they are within the rate limit using the Lua script.</p>
</li>
<li><p>If the user exceeds the limit, a <code>429</code> response is sent.</p>
</li>
</ul>
</li>
<li><p><strong>API Endpoint</strong>:</p>
<ul>
<li>The root endpoint is rate-limited, so users can only access it a limited number of times within the specified window.</li>
</ul>
</li>
</ol>
<h2 id="heading-step-5-how-to-test-the-rate-limiter">Step 5: How to Test the Rate Limiter</h2>
<ol>
<li><p><strong>Start Redis</strong>:</p>
<pre><code class="lang-basic"> docker start redis-rate-limiter
</code></pre>
</li>
<li><p><strong>Run the Node.js Server</strong>:</p>
<pre><code class="lang-bash"> node server.js
</code></pre>
</li>
<li><p><strong>Simulate Requests</strong>:</p>
<ul>
<li><p>Use <code>curl</code> or Postman to test the rate limiter:</p>
<pre><code class="lang-bash">  curl http://localhost:3000
</code></pre>
</li>
<li><p>Send multiple requests rapidly to see rate limiting in action.</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-step-6-how-to-visualize-rate-limiting-metrics">Step 6: How to Visualize Rate Limiting Metrics</h2>
<p>To monitor rate limiting metrics like cache hits and blocked requests, we'll add logging to the middleware in <strong>server.js</strong>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rateLimiter</span>(<span class="hljs-params">req, res, next</span>) </span>{
  <span class="hljs-keyword">const</span> ip = req.ip;
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> allowed = <span class="hljs-keyword">await</span> client.eval(rateLimitScript, <span class="hljs-number">1</span>, ip, RATE_LIMIT, TIME_WINDOW);
    <span class="hljs-keyword">if</span> (allowed === <span class="hljs-number">1</span>) {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Allowed request from <span class="hljs-subst">${ip}</span>`</span>);
      next();
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Blocked request from <span class="hljs-subst">${ip}</span>`</span>);
      res.status(<span class="hljs-number">429</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Too many requests. Please try again later.'</span> });
    }
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error in rate limiter:'</span>, err);
    res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Internal server error'</span> });
  }
}
</code></pre>
<h2 id="heading-step-7-how-to-deploy-with-docker">Step 7: How to Deploy with Docker</h2>
<p>Let’s containerize the application to run it in a distributed environment.</p>
<p>Create a <code>Dockerfile</code>:</p>
<pre><code class="lang-dockerfile"><span class="hljs-keyword">FROM</span> node:<span class="hljs-number">14</span>
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm install</span>
<span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">3000</span>
<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"node"</span>, <span class="hljs-string">"server.js"</span>]</span>
</code></pre>
<p>Build and Run the Docker Container:</p>
<pre><code class="lang-bash">docker build -t rate-limiter .
docker run -p 3000:3000 rate-limiter
</code></pre>
<p>Now you can scale the rate limiter by running multiple instances.</p>
<h2 id="heading-conclusion-what-youve-learned">Conclusion: What You’ve Learned</h2>
<p>Congratulations! You’ve successfully built a distributed rate limiter using Redis and Lua scripts. Throughout this tutorial, you’ve learned how to:</p>
<ol>
<li><p>Implement rate limiting to control user requests in a distributed system.</p>
</li>
<li><p>Use Lua scripts in Redis to perform atomic operations.</p>
</li>
<li><p>Apply a token bucket algorithm to manage request quotas.</p>
</li>
<li><p>Monitor rate limiting metrics to optimize performance.</p>
</li>
<li><p>Use Docker to simulate a scalable distributed environment.</p>
</li>
</ol>
<h3 id="heading-next-steps">Next Steps:</h3>
<ol>
<li><p><strong>Add Rate Limiting by User ID</strong>: Extend the system to support rate limits per user.</p>
</li>
<li><p><strong>Integrate with Nginx</strong>: Use Nginx as a reverse proxy with Redis-backed rate limiting.</p>
</li>
<li><p><strong>Deploy with Kubernetes</strong>: Scale your rate limiter using Kubernetes for high availability.</p>
</li>
</ol>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Build a Real-Time Multiplayer Tic-Tac-Toe Game Using WebSockets and Microservices ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, we’ll build a real-time multiplayer Tic-Tac-Toe game using Node.js, Socket.IO, and Redis. This game allows two players to connect from different browser tabs, take turns playing, and see real-time updates as they play. We'll use Red... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-real-time-multiplayer-tic-tac-toe-game-using-websockets-and-microservices/</link>
                <guid isPermaLink="false">673bb7ca83cab2eb3eafb7af</guid>
                
                    <category>
                        <![CDATA[ SocketIO ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websockets ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Redis ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Birkaran Sachdev ]]>
                </dc:creator>
                <pubDate>Mon, 18 Nov 2024 21:55:22 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731400068976/3c951db9-929a-4d13-ba77-759932833a9a.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, we’ll build a <strong>real-time multiplayer Tic-Tac-Toe game</strong> using <strong>Node.js</strong>, <strong>Socket.IO</strong>, and <strong>Redis</strong>. This game allows two players to connect from different browser tabs, take turns playing, and see real-time updates as they play. We'll use <strong>Redis</strong> to manage game state synchronization across multiple WebSocket servers, making our application scalable.</p>
<p>By the end, you'll have a fully functional game with real-time capabilities and a solid understanding of how to use WebSockets and Redis to build scalable real-time applications.</p>
<h3 id="heading-what-you-will-learn">What You Will Learn</h3>
<ul>
<li><p>How to use <strong>Socket.IO</strong> for real-time communication.</p>
</li>
<li><p>How to use <strong>Redis Pub/Sub</strong> to synchronize game state across multiple clients.</p>
</li>
<li><p>How to set up a scalable WebSocket server architecture.</p>
</li>
</ul>
<h3 id="heading-prerequisites"><strong>Prerequisites</strong></h3>
<p>Before we start, make sure you have the following installed:</p>
<ul>
<li><p>Node.js (v16 or higher)</p>
</li>
<li><p>Redis</p>
</li>
<li><p>Docker (optional, for running Redis in a container)</p>
</li>
<li><p>Basic knowledge of JavaScript, Node.js, and WebSockets.</p>
</li>
</ul>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-project-overview">Project Overview</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-1-setting-up-your-development-environment">Step 1: Setting Up Your Development Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-setting-up-the-project">Step 2: Setting Up the Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-implementing-the-websocket-server-with-redis">Step 3: Implementing the WebSocket Server with Redis</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-implement-the-react-frontend-interface">Step 4: Implement the React Frontend interface</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-running-the-application">Step 5: Running the Application</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-6-viewing-redis-messages-in-real-time">Step 6: Viewing Redis Messages in Real-Time</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-demo">Demo</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-project-overview"><strong>Project Overview</strong></h2>
<p>We'll build a real-time Tic-Tac-Toe game with the following features:</p>
<ul>
<li><p><strong>Two players</strong> can connect and play a game.</p>
</li>
<li><p>The game board updates in real-time across different browser tabs.</p>
</li>
<li><p>The game announces a winner or declares a draw when the board is full.</p>
</li>
</ul>
<p>We’ll use:</p>
<ul>
<li><p><strong>Node.js</strong> with <strong>Socket.IO</strong> for handling WebSocket connections.</p>
</li>
<li><p><strong>Redis</strong> Pub/Sub to manage game state synchronization across clients.</p>
</li>
</ul>
<h2 id="heading-step-1-setting-up-your-development-environment"><strong>Step 1: Setting Up Your Development Environment</strong></h2>
<h3 id="heading-installing-nodejs"><strong>Installing Node.js</strong></h3>
<p>Ensure you have Node.js installed on your system:</p>
<pre><code class="lang-bash">node -v
</code></pre>
<p>If you don’t have it installed, download it from <a target="_blank" href="https://nodejs.org/en">Node.js.</a></p>
<h3 id="heading-installing-redis"><strong>Installing Redis</strong></h3>
<p>You can install Redis locally or run it in a Docker container.</p>
<h4 id="heading-macos-using-homebrew"><strong>macOS (Using Homebrew)</strong></h4>
<p>First, ensure that you have <a target="_blank" href="https://brew.sh/">Homebrew</a> installed on your system before running the commands below:</p>
<pre><code class="lang-bash">brew install redis
brew services start redis
</code></pre>
<p>Verify that the Redis container is running with the following command:</p>
<pre><code class="lang-bash">redis-cli ping
</code></pre>
<p>You should see:</p>
<pre><code class="lang-bash">PONG
</code></pre>
<h4 id="heading-using-docker-to-run-redis"><strong>Using Docker to Run Redis</strong></h4>
<pre><code class="lang-bash">docker run --name redis-server -p 6379:6379 -d redis
</code></pre>
<p>Check if Redis is running using:</p>
<pre><code class="lang-bash">docker <span class="hljs-built_in">exec</span> -it redis-server redis-cli ping
</code></pre>
<h2 id="heading-step-2-setting-up-the-project"><strong>Step 2: Setting Up the Project</strong></h2>
<h3 id="heading-1-create-the-project-directory"><strong>1. Create the Project Directory</strong></h3>
<pre><code class="lang-bash">mkdir tic-tac-toe
<span class="hljs-built_in">cd</span> tic-tac-toe
npm init -y
</code></pre>
<h3 id="heading-2-install-dependencies"><strong>2. Install Dependencies</strong></h3>
<pre><code class="lang-bash">npm install express socket.io redis dotenv
</code></pre>
<h3 id="heading-3-create-environment-variables"><strong>3. Create Environment Variables</strong></h3>
<p>Create a <code>.env</code> file in your project root with the following contents:</p>
<pre><code class="lang-bash">PORT=3000
REDIS_HOST=localhost
REDIS_PORT=6379
</code></pre>
<h2 id="heading-step-3-implementing-the-websocket-server-with-redis"><strong>Step 3: Implementing the WebSocket Server with Redis</strong></h2>
<p>In this step, we'll set up a WebSocket server that handles real-time game interactions using <strong>Node.js</strong>, <strong>Socket.IO</strong>, and <strong>Redis</strong>. This server will manage the game state, handle player moves, and ensure synchronization across multiple clients using Redis Pub/Sub.</p>
<p>We'll break down each section of the code so you understand exactly how everything fits together.<strong>Server Code Explanation</strong></p>
<p>Create a file named <code>server.js</code> and add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> dotenv <span class="hljs-keyword">from</span> <span class="hljs-string">'dotenv'</span>;
<span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;
<span class="hljs-keyword">import</span> http <span class="hljs-keyword">from</span> <span class="hljs-string">'http'</span>;
<span class="hljs-keyword">import</span> { Server } <span class="hljs-keyword">from</span> <span class="hljs-string">'socket.io'</span>;
<span class="hljs-keyword">import</span> { createClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'redis'</span>;

dotenv.config(); <span class="hljs-comment">// Load environment variables from .env file</span>

<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> server = http.createServer(app);
<span class="hljs-keyword">const</span> io = <span class="hljs-keyword">new</span> Server(server, {
  <span class="hljs-attr">cors</span>: {
    <span class="hljs-attr">origin</span>: <span class="hljs-string">"http://localhost:5173"</span>,
    <span class="hljs-attr">methods</span>: [<span class="hljs-string">"GET"</span>, <span class="hljs-string">"POST"</span>],
  }
});
</code></pre>
<ul>
<li><p><strong>dotenv</strong>: Loads environment variables from a <code>.env</code> file to keep sensitive information like ports and keys secure.</p>
</li>
<li><p><strong>express</strong>: Sets up a basic Express server to handle HTTP requests.</p>
</li>
<li><p><strong>http</strong>: We create an HTTP server using Node's built-in <code>http</code> module, which we'll use with <strong>Socket.IO</strong> for WebSocket communication.</p>
</li>
<li><p><strong>Socket.IO</strong>: This library enables real-time, bidirectional communication between the server and clients.</p>
</li>
<li><p><strong>CORS Configuration</strong>: Allows cross-origin requests from our frontend running on <code>localhost:5173</code>.</p>
</li>
</ul>
<p>Then, to create Redis publisher and subscriber clients, we’ll add the following code to <code>server.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Initialize Redis clients</span>
<span class="hljs-keyword">const</span> pubClient = createClient();
<span class="hljs-keyword">const</span> subClient = createClient();
<span class="hljs-keyword">await</span> pubClient.connect();
<span class="hljs-keyword">await</span> subClient.connect();
</code></pre>
<p>We use <strong>Redis</strong> to handle real-time data synchronization between connected clients.</p>
<ul>
<li><p><strong>pubClient</strong>: Used to publish messages (like game state updates).</p>
</li>
<li><p><strong>subClient</strong>: Subscribes to messages (listens for updates).</p>
</li>
</ul>
<ul>
<li><strong>connect()</strong>: Establishes a connection to the Redis server.</li>
</ul>
<p>In this paradigm, one client is used to publish updates, and the other one subscribes to updates. This helps avoid blocking behavior, since Redis clients in <strong>subscribe</strong> mode can only receive messages.</p>
<p>To subscribe to Redis channels for game updates, we’ll add the following code to <code>server.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Subscribe to the Redis channel for game updates</span>
<span class="hljs-keyword">await</span> subClient.subscribe(<span class="hljs-string">'game-moves'</span>, <span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> {
  gameState = <span class="hljs-built_in">JSON</span>.parse(message);
  io.emit(<span class="hljs-string">'gameState'</span>, gameState);
});
</code></pre>
<ul>
<li><p><strong>subClient.subscribe</strong>: Listens for messages on the <code>game-moves</code> channel.</p>
</li>
<li><p>Whenever a new move is made by a player, the game state is updated in Redis, and all connected clients are informed of the new state.</p>
</li>
<li><p>The <code>message</code> parameter contains the game state as a string. We parse it into a JavaScript object and broadcast the updated state using <strong>Socket.IO</strong>.</p>
</li>
</ul>
<p>Next, to define the game state and functions, we’ll add the following code to <code>server.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Define initial game state</span>
<span class="hljs-keyword">let</span> gameState = {
  <span class="hljs-attr">board</span>: <span class="hljs-built_in">Array</span>(<span class="hljs-number">9</span>).fill(<span class="hljs-literal">null</span>),
  <span class="hljs-attr">xIsNext</span>: <span class="hljs-literal">true</span>,
};

<span class="hljs-comment">// Function to reset the game</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">resetGame</span>(<span class="hljs-params"></span>) </span>{
  gameState = {
    <span class="hljs-attr">board</span>: <span class="hljs-built_in">Array</span>(<span class="hljs-number">9</span>).fill(<span class="hljs-literal">null</span>),
    <span class="hljs-attr">xIsNext</span>: <span class="hljs-literal">true</span>,
  };
}
</code></pre>
<ul>
<li><p><strong>gameState</strong>: Keeps track of the current state of the board and whose turn it is (<code>xIsNext</code>).</p>
<ul>
<li><p>The board is represented as an array of 9 cells (each can be 'X', 'O', or <code>null</code>).</p>
</li>
<li><p>The <code>xIsNext</code> flag determines which player's turn it is.</p>
</li>
</ul>
</li>
<li><p><strong>resetGame()</strong>: Resets the board and turn indicator to their initial state, allowing for a new game to start.</p>
</li>
</ul>
<p>Next, to handle WebSocket connections, let’s add the following code to <code>server.js</code>:</p>
<pre><code class="lang-javascript">io.on(<span class="hljs-string">'connection'</span>, <span class="hljs-function">(<span class="hljs-params">socket</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'New client connected:'</span>, socket.id);

  <span class="hljs-comment">// Send the current game state to the newly connected client</span>
  socket.emit(<span class="hljs-string">'gameState'</span>, gameState);
</code></pre>
<ul>
<li><p>The <code>io.on('connection')</code> event is triggered when a new client connects.</p>
</li>
<li><p><strong>socket.id</strong>: A unique identifier for each connected client.</p>
</li>
<li><p>We immediately send the current <code>gameState</code> to the new client so they can see the current board.</p>
</li>
</ul>
<p>To handle player moves, we’ll add the following code to <code>server.js</code>:</p>
<pre><code class="lang-javascript">  <span class="hljs-comment">// Handle player moves</span>
  socket.on(<span class="hljs-string">'makeMove'</span>, <span class="hljs-function">(<span class="hljs-params">index</span>) =&gt;</span> {
    <span class="hljs-comment">// Prevent making a move if cell is already taken or game is over</span>
    <span class="hljs-keyword">if</span> (gameState.board[index] || calculateWinner(gameState.board)) <span class="hljs-keyword">return</span>;

    <span class="hljs-comment">// Update the board and switch turns</span>
    gameState.board[index] = gameState.xIsNext ? <span class="hljs-string">'X'</span> : <span class="hljs-string">'O'</span>;
    gameState.xIsNext = !gameState.xIsNext;

    <span class="hljs-comment">// Publish the updated game state to Redis</span>
    pubClient.publish(<span class="hljs-string">'game-moves'</span>, <span class="hljs-built_in">JSON</span>.stringify(gameState));
    io.emit(<span class="hljs-string">'gameState'</span>, gameState);
  });
</code></pre>
<ul>
<li><p><strong>makeMove</strong>: This event is triggered when a player clicks on a cell.</p>
<ul>
<li><p><strong>Validation</strong>: We check if the cell is already occupied or if the game has ended before making a move.</p>
</li>
<li><p><strong>Updating Game State</strong>: If the move is valid, we update the board and switch turns.</p>
</li>
</ul>
</li>
<li><p>The updated game state is then:</p>
<ol>
<li><p><strong>Published to Redis</strong>: This ensures that all instances of the server stay in sync.</p>
</li>
<li><p><strong>Broadcasted to all clients</strong>: This immediately updates the game board for all players.</p>
</li>
</ol>
</li>
</ul>
<p>To handle game restarts, we’ll add the following code to <code>server.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Handle game restarts</span>
socket.on(<span class="hljs-string">'restartGame'</span>, <span class="hljs-function">() =&gt;</span> {
  resetGame();
  io.emit(<span class="hljs-string">'gameState'</span>, gameState);
});
</code></pre>
<p>To handle client disconnection handling, we’ll add the following code to <code>server.js</code>:</p>
<pre><code class="lang-javascript"> socket.on(<span class="hljs-string">'disconnect'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Client disconnected:'</span>, socket.id);
  });
});
</code></pre>
<p>Finally, to process the logic of the game, we’ll add the following functions to <code>server.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Function to check if there's a winner</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateWinner</span>(<span class="hljs-params">board</span>) </span>{
  <span class="hljs-keyword">const</span> lines = [
    [<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>], [<span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>], [<span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>],
    [<span class="hljs-number">0</span>, <span class="hljs-number">3</span>, <span class="hljs-number">6</span>], [<span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">7</span>], [<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">8</span>],
    [<span class="hljs-number">0</span>, <span class="hljs-number">4</span>, <span class="hljs-number">8</span>], [<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">6</span>]
  ];
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> [a, b, c] <span class="hljs-keyword">of</span> lines) {
    <span class="hljs-keyword">if</span> (board[a] &amp;&amp; board[a] === board[b] &amp;&amp; board[a] === board[c]) {
      <span class="hljs-keyword">return</span> board[a];
    }
  }
  <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isBoardFull</span>(<span class="hljs-params">board</span>) </span>{
  <span class="hljs-keyword">return</span> board.every(<span class="hljs-function">(<span class="hljs-params">cell</span>) =&gt;</span> cell !== <span class="hljs-literal">null</span>);
}
</code></pre>
<ul>
<li><p><strong>calculateWinner()</strong>: Checks if there’s a winning combination on the board.</p>
</li>
<li><p><strong>isBoardFull()</strong>: Checks if all cells are filled, indicating a draw.</p>
</li>
</ul>
<h2 id="heading-step-4-implement-the-react-frontend-interface"><strong>Step 4: Implement the React Frontend interface</strong></h2>
<p>In this step, we build a simple and interactive React frontend for our Tic-Tac-Toe game. This frontend allows players to connect to the WebSocket server, make moves, and see the game board update in real-time.</p>
<p>In <code>App.jsx</code>, add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> io <span class="hljs-keyword">from</span> <span class="hljs-string">'socket.io-client'</span>;

<span class="hljs-keyword">const</span> socket = io(<span class="hljs-string">'http://localhost:3000'</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [gameState, setGameState] = useState({
    <span class="hljs-attr">board</span>: <span class="hljs-built_in">Array</span>(<span class="hljs-number">9</span>).fill(<span class="hljs-literal">null</span>),
    <span class="hljs-attr">xIsNext</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">winner</span>: <span class="hljs-literal">null</span>
  });

  useEffect(<span class="hljs-function">() =&gt;</span> {
    socket.on(<span class="hljs-string">'gameState'</span>, <span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> {
      setGameState(state);
    });

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> socket.off(<span class="hljs-string">'gameState'</span>);
  }, []);

  <span class="hljs-keyword">const</span> handleClick = <span class="hljs-function">(<span class="hljs-params">index</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (gameState.board[index] || gameState.winner) <span class="hljs-keyword">return</span>;
    socket.emit(<span class="hljs-string">'makeMove'</span>, index);
  };

  <span class="hljs-keyword">const</span> renderCell = <span class="hljs-function">(<span class="hljs-params">index</span>) =&gt;</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> handleClick(index)}&gt;{gameState.board[index]}<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
  );

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Multiplayer Tic-Tac-Toe<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"board"</span>&gt;</span>
        {[...Array(9)].map((_, i) =&gt; renderCell(i))}
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> socket.emit('restartGame')}&gt;Restart Game<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Here is a summary of how the React app is broken down:</p>
<ul>
<li><p><strong>WebSocket Connection</strong>:</p>
<ul>
<li>The frontend establishes a connection to the server using <code>socket.io-client</code>.</li>
</ul>
</li>
</ul>
<ul>
<li><p><strong>State Management</strong>:</p>
<ul>
<li><p>The game state (<code>gameState</code>) is managed with React's <code>useState</code> and includes:</p>
<ul>
<li><p>The <strong>board</strong> (9 cells).</p>
</li>
<li><p>The flag <strong>xIsNext</strong> to indicate the current player's turn.</p>
</li>
<li><p>The <strong>winner</strong> status.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Real-Time Updates</strong>:</p>
<ul>
<li><p>The <code>useEffect</code> hook:</p>
<ul>
<li><p>Listens for <code>gameState</code> updates from the server.</p>
</li>
<li><p>Updates the local game state when changes are detected.</p>
</li>
<li><p>Cleans up the WebSocket listener when the component is unmounted.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Handling Player Moves</strong>:</p>
<ul>
<li><p>The <code>handleClick</code> function:</p>
<ul>
<li><p>Checks if a cell is already occupied or if the game has a winner before allowing a move.</p>
</li>
<li><p>Sends a <code>makeMove</code> event to the server with the clicked cell index.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Game Board Rendering</strong>:</p>
<ul>
<li><p>The <code>renderCell</code> function creates a button for each cell on the board.</p>
</li>
<li><p>The board is displayed using a 3x3 grid.</p>
</li>
</ul>
</li>
<li><p><strong>Restart Game</strong>:</p>
<ul>
<li>The "Restart Game" button emits a <code>restartGame</code> event to reset the game board for all players.</li>
</ul>
</li>
<li><p><strong>User Interface</strong>:</p>
<ul>
<li>A simple and interactive layout that allows players to take turns and see updates in real-time.</li>
</ul>
</li>
</ul>
<h2 id="heading-step-5-running-the-application"><strong>Step 5: Running the Application</strong></h2>
<h3 id="heading-starting-the-backend"><strong>Starting the Backend</strong></h3>
<p>To start the backend server, open a new terminal window and run the following commands:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> tic-tac-toe
npm start
</code></pre>
<h3 id="heading-starting-the-frontend"><strong>Starting the Frontend</strong></h3>
<p>To start the React frontend server, open a new terminal window and run the commands below (do not use the same one which the backend server is running on, as you need both running simultaneously to run the game).</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> tic-tac-toe-client
npm run dev
</code></pre>
<h3 id="heading-accessing-the-game"><strong>Accessing the Game</strong></h3>
<p>Open your browser and navigate to:</p>
<pre><code class="lang-bash">http://localhost:5173
</code></pre>
<h2 id="heading-step-6-viewing-redis-messages-in-real-time"><strong>Step 6: Viewing Redis Messages in Real-Time</strong></h2>
<p>While the game is running, you can view Redis messages to see real-time game state updates.</p>
<p>Open a terminal and run:</p>
<pre><code class="lang-bash">redis-cli
SUBSCRIBE game-moves
</code></pre>
<p>This will display game updates:</p>
<pre><code class="lang-bash">1) <span class="hljs-string">"message"</span>
2) <span class="hljs-string">"game-moves"</span>
3) <span class="hljs-string">"{\"board\":[\"X\",null,\"O\",null,\"X\",null,null,null,null],\"xIsNext\":false}"</span>
</code></pre>
<p>Every time a move is made or the game state changes, the server publishes the updated game state to the <code>game-moves</code> channel. Using <code>redis-cli</code>, you can monitor these updates in real-time, as the game is being played.</p>
<h2 id="heading-demo"><strong>Demo</strong></h2>
<p>In this demo, you'll see the Tic Tac Toe game running locally, demonstrating real-time updates as players take turns.</p>
<p>The gameplay showcases features such as turn switching, board updates, and game state announcements (winner or draw). This highlights how the game leverages WebSocket communication to provide a smooth, interactive experience.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/2aCllaBR6Xg" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Congratulations, you’ve successfully built a real-time multiplayer Tic-Tac-Toe game using Node.js, Socket.IO, and Redis. Here’s what you’ve learned:</p>
<ul>
<li><p>Real-time WebSocket communication using <strong>Socket.IO</strong>.</p>
</li>
<li><p>Game state management using <strong>Redis Pub/Sub</strong>.</p>
</li>
<li><p>Building a responsive front-end with <strong>React</strong>.</p>
</li>
</ul>
<h3 id="heading-next-steps"><strong>Next Steps</strong></h3>
<ul>
<li><p>Add player authentication.</p>
</li>
<li><p>Implement a chat feature.</p>
</li>
<li><p>Deploy your application to a cloud provider for scalability.</p>
</li>
</ul>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Design Patterns in Java with Spring Boot – Explained with Code Examples ]]>
                </title>
                <description>
                    <![CDATA[ As software projects grow, it becomes increasingly important to keep your code organized, maintainable, and scalable. This is where design patterns come into play. Design patterns provide proven, reusable solutions to common software design challenge... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-design-patterns-in-java-with-spring-boot/</link>
                <guid isPermaLink="false">673616b2ea38d82d84002cda</guid>
                
                    <category>
                        <![CDATA[ Java ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Springboot ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Birkaran Sachdev ]]>
                </dc:creator>
                <pubDate>Thu, 14 Nov 2024 15:26:42 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/1td5Iq5IvNc/upload/adaeb0229ea4ed1cd3c985d8eb92d23e.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>As software projects grow, it becomes increasingly important to keep your code organized, maintainable, and scalable. This is where design patterns come into play. Design patterns provide proven, reusable solutions to common software design challenges, making your code more efficient and easier to manage.</p>
<p>In this guide, we'll dive deep into some of the most popular design patterns and show you how to implement them in Spring Boot. By the end, you'll not only understand these patterns conceptually but also be able to apply them in your own projects with confidence.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-introduction-to-design-patterns">Introduction to Design Patterns</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-your-spring-boot-project">How to Set Up Your Spring Boot Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-the-singleton-pattern">What is the Singleton Pattern?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-the-factory-pattern">What is the Factory Pattern?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-the-strategy-pattern">What is the Strategy Pattern?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-the-observer-pattern">What is the Observer Pattern?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-spring-boots-dependency-injection">How to Use Spring Boot’s Dependency Injection</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-best-practices-and-optimization-tips">Best Practices and Optimization Tips</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion-and-key-takeaways">Conclusion and Key Takeaways</a></p>
</li>
</ul>
<h2 id="heading-introduction-to-design-patterns">Introduction to Design Patterns</h2>
<p>Design patterns are reusable solutions to common software design problems. Think of them as best practices distilled into templates that can be applied to solve specific challenges in your code. They are not specific to any language, but they can be particularly powerful in Java due to its object-oriented nature.</p>
<p>In this guide, we'll cover:</p>
<ul>
<li><p><strong>Singleton Pattern</strong>: Ensuring a class has only one instance.</p>
</li>
<li><p><strong>Factory Pattern</strong>: Creating objects without specifying the exact class.</p>
</li>
<li><p><strong>Strategy Pattern</strong>: Allowing algorithms to be selected at runtime.</p>
</li>
<li><p><strong>Observer Pattern</strong>: Setting up a publish-subscribe relationship.</p>
</li>
</ul>
<p>We'll not only cover how these patterns work but also explore how they can be applied in Spring Boot for real-world applications.</p>
<h2 id="heading-how-to-set-up-your-spring-boot-project">How to Set Up Your Spring Boot Project</h2>
<p>Before we dive into the patterns, let’s set up a Spring Boot project:</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>Make sure you have:</p>
<ul>
<li><p>Java 11+</p>
</li>
<li><p>Maven</p>
</li>
<li><p>Spring Boot CLI (optional)</p>
</li>
<li><p>Postman or curl (for testing)</p>
</li>
</ul>
<h3 id="heading-project-initialization">Project Initialization</h3>
<p>You can quickly create a Spring Boot project using Spring Initializr:</p>
<pre><code class="lang-bash">curl https://start.spring.io/starter.zip \
-d dependencies=web \
-d name=DesignPatternsDemo \
-d javaVersion=11 -o design-patterns-demo.zip
unzip design-patterns-demo.zip
<span class="hljs-built_in">cd</span> design-patterns-demo
</code></pre>
<h2 id="heading-what-is-the-singleton-pattern">What is the Singleton Pattern?</h2>
<p>The Singleton pattern ensures that a class has only one instance and provides a global access point to it. This pattern is commonly used for services like logging, configuration management, or database connections.</p>
<h3 id="heading-how-to-implement-the-singleton-pattern-in-spring-boot">How to Implement the Singleton Pattern in Spring Boot</h3>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Spring_Framework#:~:text=Creating%20and%20managing%20beans">Spring Boot beans</a> are singletons by default, meaning that Spring automatically manages the lifecycle of these beans to ensure only one instance exists. However, it's important to understand how the Singleton pattern works under the hood, especially when you're not using Spring-managed beans or need more control over instance management.</p>
<p>Let’s walk through a manual implementation of the Singleton pattern to demonstrate how you can control the creation of a single instance within your application.</p>
<h3 id="heading-step-1-create-a-loggerservice-class">Step 1: Create a <code>LoggerService</code> Class</h3>
<p>In this example, we’ll create a simple logging service using the Singleton pattern. The goal is to ensure that all parts of the application use the same logging instance.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LoggerService</span> </span>{
    <span class="hljs-comment">// The static variable to hold the single instance</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> LoggerService instance;

    <span class="hljs-comment">// Private constructor to prevent instantiation from outside</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">LoggerService</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// This constructor is intentionally empty to prevent other classes from creating instances</span>
    }

    <span class="hljs-comment">// Public method to provide access to the single instance</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">synchronized</span> LoggerService <span class="hljs-title">getInstance</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">if</span> (instance == <span class="hljs-keyword">null</span>) {
            instance = <span class="hljs-keyword">new</span> LoggerService();
        }
        <span class="hljs-keyword">return</span> instance;
    }

    <span class="hljs-comment">// Example logging method</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">log</span><span class="hljs-params">(String message)</span> </span>{
        System.out.println(<span class="hljs-string">"[LOG] "</span> + message);
    }
}
</code></pre>
<ul>
<li><p><strong>Static Variable</strong> (<code>instance</code>): This holds the single instance of <code>LoggerService</code>.</p>
</li>
<li><p><strong>Private Constructor</strong>: The constructor is marked private to prevent other classes from creating new instances directly.</p>
</li>
<li><p><strong>Synchronized</strong> <code>getInstance()</code> Method: The method is synchronized to make it thread-safe, ensuring that only one instance is created even if multiple threads try to access it simultaneously.</p>
</li>
<li><p><strong>Lazy Initialization</strong>: The instance is created only when it's first requested (<code>lazy initialization</code>), which is efficient in terms of memory usage.</p>
</li>
</ul>
<p><strong>Real-World Usage</strong>: This pattern is commonly used for shared resources, such as logging, configuration settings, or managing database connections, where you want to control access and ensure that only one instance is used throughout your application.</p>
<h3 id="heading-step-2-use-the-singleton-in-a-spring-boot-controller">Step 2: Use the Singleton in a Spring Boot Controller</h3>
<p>Now, let's see how we can use our <code>LoggerService</code> Singleton within a Spring Boot controller. This controller will expose an endpoint that logs a message whenever it's accessed.</p>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> org.springframework.http.ResponseEntity;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

<span class="hljs-meta">@RestController</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LogController</span> </span>{

    <span class="hljs-meta">@GetMapping("/log")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> ResponseEntity&lt;String&gt; <span class="hljs-title">logMessage</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// Accessing the Singleton instance of LoggerService</span>
        LoggerService logger = LoggerService.getInstance();
        logger.log(<span class="hljs-string">"This is a log message!"</span>);
        <span class="hljs-keyword">return</span> ResponseEntity.ok(<span class="hljs-string">"Message logged successfully"</span>);
    }
}
</code></pre>
<ul>
<li><p><strong>GET Endpoint</strong>: We’ve created a <code>/log</code> endpoint that, when accessed, logs a message using the <code>LoggerService</code>.</p>
</li>
<li><p><strong>Singleton Usage</strong>: Instead of creating a new instance of <code>LoggerService</code>, we call <code>getInstance()</code> to ensure we’re using the same instance every time.</p>
</li>
<li><p><strong>Response</strong>: After logging, the endpoint returns a response indicating success.</p>
</li>
</ul>
<h3 id="heading-step-3-testing-the-singleton-pattern">Step 3: Testing the Singleton Pattern</h3>
<p>Now, let's test this endpoint using Postman or your browser:</p>
<pre><code class="lang-bash">GET http://localhost:8080/<span class="hljs-built_in">log</span>
</code></pre>
<p><strong>Expected Output</strong>:</p>
<ul>
<li><p>Console log: <code>[LOG] This is a log message!</code></p>
</li>
<li><p>HTTP Response: <code>Message logged successfully</code></p>
</li>
</ul>
<p>You can call the endpoint multiple times, and you'll see that the same instance of <code>LoggerService</code> is used, as indicated by the consistent log output.</p>
<h3 id="heading-real-world-use-cases-for-the-singleton-pattern">Real-World Use Cases for the Singleton Pattern</h3>
<p>Here’s when you might want to use the Singleton pattern in real-world applications:</p>
<ol>
<li><p><strong>Configuration Management</strong>: Ensure that your application uses a consistent set of configuration settings, especially when those settings are loaded from files or databases.</p>
</li>
<li><p><strong>Database Connection Pools</strong>: Control access to a limited number of database connections, ensuring that the same pool is shared across the application.</p>
</li>
<li><p><strong>Caching</strong>: Maintain a single cache instance to avoid inconsistent data.</p>
</li>
<li><p><strong>Logging Services</strong>: As shown in this example, use a single logging service to centralize log outputs across different modules of your application.</p>
</li>
</ol>
<h3 id="heading-key-takeaways">Key Takeaways</h3>
<ul>
<li><p>The Singleton pattern is an easy way to ensure that only one instance of a class is created.</p>
</li>
<li><p>Thread safety is crucial if multiple threads are accessing the Singleton, which is why we used <code>synchronized</code> in our example.</p>
</li>
<li><p>Spring Boot beans are already singletons by default, but understanding how to implement it manually helps you gain more control when needed.</p>
</li>
</ul>
<p>This covers the implementation and usage of the Singleton pattern. Next, we’ll explore the Factory pattern to see how it can help streamline object creation.</p>
<h2 id="heading-what-is-the-factory-pattern">What is the Factory Pattern?</h2>
<p>The Factory pattern allows you to create objects without specifying the exact class. This pattern is useful when you have different types of objects that need to be instantiated based on some input.</p>
<h3 id="heading-how-to-implementing-a-factory-in-spring-boot">How to Implementing a Factory in Spring Boot</h3>
<p>The Factory pattern is incredibly useful when you need to create objects based on certain criteria but want to decouple the object creation process from your main application logic.</p>
<p>In this section, we’ll walk through building a <code>NotificationFactory</code> to send notifications via Email or SMS. This is especially useful if you anticipate adding more notification types in the future, such as push notifications or in-app alerts, without changing your existing code.</p>
<h3 id="heading-step-1-create-the-notification-interface">Step 1: Create the <code>Notification</code> Interface</h3>
<p>The first step is to define a common interface that all notification types will implement. This ensures that each type of notification (Email, SMS, and so on) will have a consistent <code>send()</code> method.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Notification</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">send</span><span class="hljs-params">(String message)</span></span>;
}
</code></pre>
<ul>
<li><p><strong>Purpose</strong>: The <code>Notification</code> interface defines the contract for sending notifications. Any class that implements this interface must provide an implementation for the <code>send()</code> method.</p>
</li>
<li><p><strong>Scalability</strong>: By using an interface, you can easily extend your application in the future to include other types of notifications without modifying existing code.</p>
</li>
</ul>
<h3 id="heading-step-2-implement-emailnotification-and-smsnotification">Step 2: Implement <code>EmailNotification</code> and <code>SMSNotification</code></h3>
<p>Now, let's implement two concrete classes, one for sending emails and another for sending SMS messages.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EmailNotification</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Notification</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">send</span><span class="hljs-params">(String message)</span> </span>{
        System.out.println(<span class="hljs-string">"Sending Email: "</span> + message);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SMSNotification</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Notification</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">send</span><span class="hljs-params">(String message)</span> </span>{
        System.out.println(<span class="hljs-string">"Sending SMS: "</span> + message);
    }
}
</code></pre>
<h3 id="heading-step-3-create-a-notificationfactory">Step 3: Create a <code>NotificationFactory</code></h3>
<p>The <code>NotificationFactory</code> class is responsible for creating instances of <code>Notification</code> based on the specified type. This design ensures that the <code>NotificationController</code> doesn’t need to know about the details of object creation.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NotificationFactory</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Notification <span class="hljs-title">createNotification</span><span class="hljs-params">(String type)</span> </span>{
        <span class="hljs-keyword">switch</span> (type.toUpperCase()) {
            <span class="hljs-keyword">case</span> <span class="hljs-string">"EMAIL"</span>:
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> EmailNotification();
            <span class="hljs-keyword">case</span> <span class="hljs-string">"SMS"</span>:
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> SMSNotification();
            <span class="hljs-keyword">default</span>:
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Unknown notification type: "</span> + type);
        }
    }
}
</code></pre>
<p><strong>Factory Method</strong> (<code>createNotification()</code>):</p>
<ul>
<li><p>The factory method takes a string (<code>type</code>) as input and returns an instance of the corresponding notification class.</p>
</li>
<li><p><strong>Switch Statement</strong>: The switch statement selects the appropriate notification type based on the input.</p>
</li>
<li><p><strong>Error Handling</strong>: If the provided type is not recognized, it throws an <code>IllegalArgumentException</code>. This ensures that invalid types are caught early.</p>
</li>
</ul>
<p><strong>Why Use a Factory?</strong></p>
<ul>
<li><p><strong>Decoupling</strong>: The factory pattern decouples object creation from the business logic. This makes your code more modular and easier to maintain.</p>
</li>
<li><p><strong>Extensibility</strong>: If you want to add a new notification type, you only need to update the factory without changing the controller logic.</p>
</li>
</ul>
<h3 id="heading-step-4-use-the-factory-in-a-spring-boot-controller">Step 4: Use the Factory in a Spring Boot Controller</h3>
<p>Now, let’s put everything together by creating a Spring Boot controller that uses the <code>NotificationFactory</code> to send notifications based on the user’s request.</p>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> org.springframework.http.ResponseEntity;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RequestParam;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

<span class="hljs-meta">@RestController</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NotificationController</span> </span>{

    <span class="hljs-meta">@GetMapping("/notify")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> ResponseEntity&lt;String&gt; <span class="hljs-title">notify</span><span class="hljs-params">(<span class="hljs-meta">@RequestParam</span> String type, <span class="hljs-meta">@RequestParam</span> String message)</span> </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// Create the appropriate Notification object using the factory</span>
            Notification notification = NotificationFactory.createNotification(type);
            notification.send(message);
            <span class="hljs-keyword">return</span> ResponseEntity.ok(<span class="hljs-string">"Notification sent successfully!"</span>);
        } <span class="hljs-keyword">catch</span> (IllegalArgumentException e) {
            <span class="hljs-keyword">return</span> ResponseEntity.badRequest().body(e.getMessage());
        }
    }
}
</code></pre>
<p><strong>GET Endpoint</strong> (<code>/notify</code>):</p>
<ul>
<li><p>The controller exposes a <code>/notify</code> endpoint that accepts two query parameters: <code>type</code> (either "EMAIL" or "SMS") and <code>message</code>.</p>
</li>
<li><p>It uses the <code>NotificationFactory</code> to create the appropriate notification type and sends the message.</p>
</li>
<li><p><strong>Error Handling</strong>: If an invalid notification type is provided, the controller catches the <code>IllegalArgumentException</code> and returns a <code>400 Bad Request</code> response.</p>
</li>
</ul>
<h3 id="heading-step-5-testing-the-factory-pattern">Step 5: Testing the Factory Pattern</h3>
<p>Let’s test the endpoint using Postman or a browser:</p>
<ol>
<li><p><strong>Send an Email Notification</strong>:</p>
<pre><code class="lang-bash"> GET http://localhost:8080/notify?<span class="hljs-built_in">type</span>=email&amp;message=Hello%20Email
</code></pre>
<p> <strong>Output</strong>:</p>
<pre><code class="lang-bash"> Sending Email: Hello Email
</code></pre>
</li>
<li><p><strong>Send an SMS Notification</strong>:</p>
<pre><code class="lang-bash"> GET http://localhost:8080/notify?<span class="hljs-built_in">type</span>=sms&amp;message=Hello%20SMS
</code></pre>
<p> <strong>Output</strong>:</p>
<pre><code class="lang-bash"> Sending SMS: Hello SMS
</code></pre>
</li>
<li><p><strong>Test with an Invalid Type</strong>:</p>
<pre><code class="lang-bash"> GET http://localhost:8080/notify?<span class="hljs-built_in">type</span>=unknown&amp;message=Test
</code></pre>
<p> <strong>Output</strong>:</p>
<pre><code class="lang-bash"> Bad Request: Unknown notification <span class="hljs-built_in">type</span>: unknown
</code></pre>
</li>
</ol>
<h3 id="heading-real-world-use-cases-for-the-factory-pattern">Real-World Use Cases for the Factory Pattern</h3>
<p>The Factory pattern is particularly useful in scenarios where:</p>
<ol>
<li><p><strong>Dynamic Object Creation</strong>: When you need to create objects based on user input, like sending different types of notifications, generating reports in various formats, or handling different payment methods.</p>
</li>
<li><p><strong>Decoupling Object Creation</strong>: By using a factory, you can keep your main business logic separate from object creation, making your code more maintainable.</p>
</li>
<li><p><strong>Scalability</strong>: Easily extend your application to support new types of notifications without modifying existing code. Simply add a new class that implements the <code>Notification</code> interface and update the factory.</p>
</li>
</ol>
<h2 id="heading-what-is-the-strategy-pattern">What is the Strategy Pattern?</h2>
<p>The Strategy pattern is perfect when you need to switch between multiple algorithms or behaviors dynamically. It allows you to define a family of algorithms, encapsulate each one within separate classes, and make them easily interchangeable at runtime. This is especially useful for selecting an algorithm based on specific conditions, keeping your code clean, modular, and flexible.</p>
<p><strong>Real-World Use Case</strong>: Imagine an e-commerce system that needs to support multiple payment options, like credit cards, PayPal, or bank transfers. By using the Strategy pattern, you can easily add or modify payment methods without altering existing code. This approach ensures that your application remains scalable and maintainable as you introduce new features or update existing ones.</p>
<p>We’ll demonstrate this pattern with a Spring Boot example that handles payments using either a credit card or PayPal strategy.</p>
<h3 id="heading-step-1-define-a-paymentstrategy-interface">Step 1: Define a <code>PaymentStrategy</code> Interface</h3>
<p>We start by creating a common interface that all payment strategies will implement:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">PaymentStrategy</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">pay</span><span class="hljs-params">(<span class="hljs-keyword">double</span> amount)</span></span>;
}
</code></pre>
<p>The interface defines a contract for all payment methods, ensuring consistency across implementations.</p>
<h3 id="heading-step-2-implement-payment-strategies">Step 2: Implement Payment Strategies</h3>
<p>Create concrete classes for credit card and PayPal payments.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreditCardPayment</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">PaymentStrategy</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">pay</span><span class="hljs-params">(<span class="hljs-keyword">double</span> amount)</span> </span>{
        System.out.println(<span class="hljs-string">"Paid $"</span> + amount + <span class="hljs-string">" with Credit Card"</span>);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PayPalPayment</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">PaymentStrategy</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">pay</span><span class="hljs-params">(<span class="hljs-keyword">double</span> amount)</span> </span>{
        System.out.println(<span class="hljs-string">"Paid $"</span> + amount + <span class="hljs-string">" via PayPal"</span>);
    }
}
</code></pre>
<p>Each class implements the <code>pay()</code> method with its specific behavior.</p>
<h3 id="heading-step-3-use-the-strategy-in-a-controller">Step 3: Use the Strategy in a Controller</h3>
<p>Create a controller to dynamically select a payment strategy based on user input:</p>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> org.springframework.http.ResponseEntity;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RequestParam;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

<span class="hljs-meta">@RestController</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentController</span> </span>{

    <span class="hljs-meta">@GetMapping("/pay")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> ResponseEntity&lt;String&gt; <span class="hljs-title">processPayment</span><span class="hljs-params">(<span class="hljs-meta">@RequestParam</span> String method, <span class="hljs-meta">@RequestParam</span> <span class="hljs-keyword">double</span> amount)</span> </span>{
        PaymentStrategy strategy = selectPaymentStrategy(method);
        <span class="hljs-keyword">if</span> (strategy == <span class="hljs-keyword">null</span>) {
            <span class="hljs-keyword">return</span> ResponseEntity.badRequest().body(<span class="hljs-string">"Invalid payment method"</span>);
        }
        strategy.pay(amount);
        <span class="hljs-keyword">return</span> ResponseEntity.ok(<span class="hljs-string">"Payment processed successfully!"</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> PaymentStrategy <span class="hljs-title">selectPaymentStrategy</span><span class="hljs-params">(String method)</span> </span>{
        <span class="hljs-keyword">switch</span> (method.toUpperCase()) {
            <span class="hljs-keyword">case</span> <span class="hljs-string">"CREDIT"</span>: <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> CreditCardPayment();
            <span class="hljs-keyword">case</span> <span class="hljs-string">"PAYPAL"</span>: <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> PayPalPayment();
            <span class="hljs-keyword">default</span>: <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
        }
    }
}
</code></pre>
<p>The endpoint accepts <code>method</code> and <code>amount</code> as query parameters and processes the payment using the appropriate strategy.</p>
<h3 id="heading-step-4-testing-the-endpoint">Step 4: Testing the Endpoint</h3>
<ol>
<li><p><strong>Credit Card Payment</strong>:</p>
<pre><code class="lang-bash"> GET http://localhost:8080/pay?method=credit&amp;amount=100
</code></pre>
<p> Output: <code>Paid $100.0 with Credit Card</code></p>
</li>
<li><p><strong>PayPal Payment</strong>:</p>
<pre><code class="lang-bash"> GET http://localhost:8080/pay?method=paypal&amp;amount=50
</code></pre>
<p> Output: <code>Paid $50.0 via PayPal</code></p>
</li>
<li><p><strong>Invalid Method</strong>:</p>
<pre><code class="lang-bash"> GET http://localhost:8080/pay?method=bitcoin&amp;amount=25
</code></pre>
<p> Output: <code>Invalid payment method</code></p>
</li>
</ol>
<h3 id="heading-use-cases-for-the-strategy-pattern">Use Cases for the Strategy Pattern</h3>
<ul>
<li><p><strong>Payment Processing</strong>: Dynamically switch between different payment gateways.</p>
</li>
<li><p><strong>Sorting Algorithms</strong>: Choose the best sorting method based on data size.</p>
</li>
<li><p><strong>File Exporting</strong>: Export reports in various formats (PDF, Excel, CSV).</p>
</li>
</ul>
<h3 id="heading-key-takeaways-1">Key Takeaways</h3>
<ul>
<li><p>The Strategy pattern keeps your code modular and follows the Open/Closed principle.</p>
</li>
<li><p>Adding new strategies is easy—just create a new class implementing the <code>PaymentStrategy</code> interface.</p>
</li>
<li><p>It’s ideal for scenarios where you need flexible algorithm selection at runtime.</p>
</li>
</ul>
<p>Next, we’ll explore the Observer pattern, perfect for handling event-driven architectures.</p>
<h2 id="heading-what-is-the-observer-pattern">What is the Observer Pattern?</h2>
<p>The Observer pattern is ideal when you have one object (the subject) that needs to notify multiple other objects (observers) about changes in its state. It’s perfect for event-driven systems where updates need to be pushed to various components without creating tight coupling between them. This pattern allows you to maintain a clean architecture, especially when different parts of your system need to react to changes independently.</p>
<p><strong>Real-World Use Case</strong>: This pattern is commonly used in systems that send notifications or alerts, such as chat applications or stock price trackers, where updates need to be pushed to users in real-time. By using the Observer pattern, you can add or remove notification types easily without altering the core logic.</p>
<p>We’ll demonstrate how to implement this pattern in Spring Boot by building a simple notification system where both Email and SMS notifications are sent whenever a user registers.</p>
<h3 id="heading-step-1-create-an-observer-interface">Step 1: Create an <code>Observer</code> Interface</h3>
<p>We begin by defining a common interface that all observers will implement:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Observer</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">update</span><span class="hljs-params">(String event)</span></span>;
}
</code></pre>
<p>The interface establishes a contract where all observers must implement the <code>update()</code> method, which will be triggered whenever the subject changes.</p>
<h3 id="heading-step-2-implement-emailobserver-and-smsobserver">Step 2: Implement <code>EmailObserver</code> and <code>SMSObserver</code></h3>
<p>Next, we create two concrete implementations of the <code>Observer</code> interface to handle email and SMS notifications.</p>
<h4 id="heading-emailobserver-class"><code>EmailObserver</code> Class</h4>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EmailObserver</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Observer</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">update</span><span class="hljs-params">(String event)</span> </span>{
        System.out.println(<span class="hljs-string">"Email sent for event: "</span> + event);
    }
}
</code></pre>
<p>The <code>EmailObserver</code> handles sending email notifications whenever it's notified of an event.</p>
<h4 id="heading-smsobserver-class"><code>SMSObserver</code> Class</h4>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SMSObserver</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Observer</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">update</span><span class="hljs-params">(String event)</span> </span>{
        System.out.println(<span class="hljs-string">"SMS sent for event: "</span> + event);
    }
}
</code></pre>
<p>The <code>SMSObserver</code> handles sending SMS notifications whenever it's notified.</p>
<h3 id="heading-step-3-create-a-userservice-class-the-subject">Step 3: Create a <code>UserService</code> Class (The Subject)</h3>
<p>We’ll now create a <code>UserService</code> class that acts as the subject, notifying its registered observers whenever a user registers.</p>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> org.springframework.stereotype.Service;
<span class="hljs-keyword">import</span> java.util.ArrayList;
<span class="hljs-keyword">import</span> java.util.List;

<span class="hljs-meta">@Service</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserService</span> </span>{
    <span class="hljs-keyword">private</span> List&lt;Observer&gt; observers = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();

    <span class="hljs-comment">// Method to register observers</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">registerObserver</span><span class="hljs-params">(Observer observer)</span> </span>{
        observers.add(observer);
    }

    <span class="hljs-comment">// Method to notify all registered observers of an event</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">notifyObservers</span><span class="hljs-params">(String event)</span> </span>{
        <span class="hljs-keyword">for</span> (Observer observer : observers) {
            observer.update(event);
        }
    }

    <span class="hljs-comment">// Method to register a new user and notify observers</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">registerUser</span><span class="hljs-params">(String username)</span> </span>{
        System.out.println(<span class="hljs-string">"User registered: "</span> + username);
        notifyObservers(<span class="hljs-string">"User Registration"</span>);
    }
}
</code></pre>
<ul>
<li><p><strong>Observers List</strong>: Keeps track of all registered observers.</p>
</li>
<li><p><code>registerObserver()</code> Method: Adds new observers to the list.</p>
</li>
<li><p><code>notifyObservers()</code> Method: Notifies all registered observers when an event occurs.</p>
</li>
<li><p><code>registerUser()</code> Method: Registers a new user and triggers notifications to all observers.</p>
</li>
</ul>
<h3 id="heading-step-4-use-the-observer-pattern-in-a-controller">Step 4: Use the Observer Pattern in a Controller</h3>
<p>Finally, we’ll create a Spring Boot controller to expose an endpoint for user registration. This controller will register both <code>EmailObserver</code> and <code>SMSObserver</code> with the <code>UserService</code>.</p>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> org.springframework.http.ResponseEntity;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.*;

<span class="hljs-meta">@RestController</span>
<span class="hljs-meta">@RequestMapping("/api")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserController</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> UserService userService;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">UserController</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">this</span>.userService = <span class="hljs-keyword">new</span> UserService();
        <span class="hljs-comment">// Register observers</span>
        userService.registerObserver(<span class="hljs-keyword">new</span> EmailObserver());
        userService.registerObserver(<span class="hljs-keyword">new</span> SMSObserver());
    }

    <span class="hljs-meta">@PostMapping("/register")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> ResponseEntity&lt;String&gt; <span class="hljs-title">registerUser</span><span class="hljs-params">(<span class="hljs-meta">@RequestParam</span> String username)</span> </span>{
        userService.registerUser(username);
        <span class="hljs-keyword">return</span> ResponseEntity.ok(<span class="hljs-string">"User registered and notifications sent!"</span>);
    }
}
</code></pre>
<ul>
<li><p><strong>Endpoint</strong> (<code>/register</code>): Accepts a <code>username</code> parameter and registers the user, triggering notifications to all observers.</p>
</li>
<li><p><strong>Observers</strong>: Both <code>EmailObserver</code> and <code>SMSObserver</code> are registered with <code>UserService</code>, so they are notified whenever a user registers.</p>
</li>
</ul>
<h3 id="heading-testing-the-observer-pattern">Testing the Observer Pattern</h3>
<p>Now, let’s test our implementation using Postman or a browser:</p>
<pre><code class="lang-bash">POST http://localhost:8080/api/register?username=JohnDoe
</code></pre>
<p><strong>Expected Output in Console</strong>:</p>
<pre><code class="lang-java">User registered: JohnDoe
Email sent <span class="hljs-keyword">for</span> event: User Registration
SMS sent <span class="hljs-keyword">for</span> event: User Registration
</code></pre>
<p>The system registers the user and notifies both the Email and SMS observers, showcasing the flexibility of the Observer pattern.</p>
<h3 id="heading-real-world-applications-of-the-observer-pattern">Real-World Applications of the Observer Pattern</h3>
<ol>
<li><p><strong>Notification Systems</strong>: Sending updates to users via different channels (email, SMS, push notifications) when certain events occur.</p>
</li>
<li><p><strong>Event-Driven Architectures</strong>: Notifying multiple subsystems when specific actions take place, such as user activities or system alerts.</p>
</li>
<li><p><strong>Data Streaming</strong>: Broadcasting data changes to various consumers in real-time (for example, live stock prices or social media feeds).</p>
</li>
</ol>
<h2 id="heading-how-to-use-spring-boots-dependency-injection">How to Use Spring Boot’s Dependency Injection</h2>
<p>So far, we’ve been manually creating objects to demonstrate design patterns. However, in real-world Spring Boot applications, Dependency Injection (DI) is the preferred way to manage object creation. DI allows Spring to automatically handle the instantiation and wiring of your classes, making your code more modular, testable, and maintainable.</p>
<p>Let’s refactor our Strategy pattern example to take advantage of Spring Boot's powerful DI capabilities. This will allow us to switch between payment strategies dynamically, using Spring’s annotations to manage dependencies.</p>
<h3 id="heading-updated-strategy-pattern-using-spring-boots-di">Updated Strategy Pattern Using Spring Boot's DI</h3>
<p>In our refactored example, we’ll leverage Spring’s annotations like <code>@Component</code>, <code>@Service</code>, and <code>@Autowired</code> to streamline the process of injecting dependencies.</p>
<h4 id="heading-step-1-annotate-payment-strategies-with-component">Step 1: Annotate Payment Strategies with <code>@Component</code></h4>
<p>First, we’ll mark our strategy implementations with the <code>@Component</code> annotation so that Spring can detect and manage them automatically.</p>
<pre><code class="lang-java"><span class="hljs-meta">@Component("creditCardPayment")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreditCardPayment</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">PaymentStrategy</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">pay</span><span class="hljs-params">(<span class="hljs-keyword">double</span> amount)</span> </span>{
        System.out.println(<span class="hljs-string">"Paid $"</span> + amount + <span class="hljs-string">" with Credit Card"</span>);
    }
}

<span class="hljs-meta">@Component("payPalPayment")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PayPalPayment</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">PaymentStrategy</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">pay</span><span class="hljs-params">(<span class="hljs-keyword">double</span> amount)</span> </span>{
        System.out.println(<span class="hljs-string">"Paid $"</span> + amount + <span class="hljs-string">" using PayPal"</span>);
    }
}
</code></pre>
<ul>
<li><p><code>@Component</code> Annotation: By adding <code>@Component</code>, we tell Spring to treat these classes as Spring-managed beans. The string value (<code>"creditCardPayment"</code> and <code>"payPalPayment"</code>) acts as the bean identifier.</p>
</li>
<li><p><strong>Flexibility</strong>: This setup allows us to switch between strategies by using the appropriate bean identifier.</p>
</li>
</ul>
<h4 id="heading-step-2-refactor-the-paymentservice-to-use-dependency-injection">Step 2: Refactor the <code>PaymentService</code> to Use Dependency Injection</h4>
<p>Next, let’s modify the <code>PaymentService</code> to inject a specific payment strategy using <code>@Autowired</code> and <code>@Qualifier</code>.</p>
<pre><code class="lang-java"><span class="hljs-meta">@Service</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentService</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> PaymentStrategy paymentStrategy;

    <span class="hljs-meta">@Autowired</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">PaymentService</span><span class="hljs-params">(<span class="hljs-meta">@Qualifier("payPalPayment")</span> PaymentStrategy paymentStrategy)</span> </span>{
        <span class="hljs-keyword">this</span>.paymentStrategy = paymentStrategy;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">processPayment</span><span class="hljs-params">(<span class="hljs-keyword">double</span> amount)</span> </span>{
        paymentStrategy.pay(amount);
    }
}
</code></pre>
<ul>
<li><p><code>@Service</code> Annotation: Marks <code>PaymentService</code> as a Spring-managed service bean.</p>
</li>
<li><p><code>@Autowired</code>: Spring injects the required dependency automatically.</p>
</li>
<li><p><code>@Qualifier</code>: Specifies which implementation of <code>PaymentStrategy</code> to inject. In this example, we’re using <code>"payPalPayment"</code>.</p>
</li>
<li><p><strong>Ease of Configuration</strong>: By simply changing the <code>@Qualifier</code> value, you can switch the payment strategy without altering any business logic.</p>
</li>
</ul>
<h3 id="heading-step-3-using-the-refactored-service-in-a-controller">Step 3: Using the Refactored Service in a Controller</h3>
<p>To see the benefits of this refactoring, let’s update the controller to use our <code>PaymentService</code>:</p>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.*;

<span class="hljs-meta">@RestController</span>
<span class="hljs-meta">@RequestMapping("/api")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentController</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> PaymentService paymentService;

    <span class="hljs-meta">@Autowired</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">PaymentController</span><span class="hljs-params">(PaymentService paymentService)</span> </span>{
        <span class="hljs-keyword">this</span>.paymentService = paymentService;
    }

    <span class="hljs-meta">@GetMapping("/pay")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">makePayment</span><span class="hljs-params">(<span class="hljs-meta">@RequestParam</span> <span class="hljs-keyword">double</span> amount)</span> </span>{
        paymentService.processPayment(amount);
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Payment processed using the current strategy!"</span>;
    }
}
</code></pre>
<ul>
<li><p><code>@Autowired</code>: The controller automatically receives the <code>PaymentService</code> with the injected payment strategy.</p>
</li>
<li><p><strong>GET Endpoint (</strong><code>/pay</code>): When accessed, it processes a payment using the currently configured strategy (PayPal in this example).</p>
</li>
</ul>
<h3 id="heading-testing-the-refactored-strategy-pattern-with-di">Testing the Refactored Strategy Pattern with DI</h3>
<p>Now, let’s test the new implementation using Postman or a browser:</p>
<pre><code class="lang-bash">GET http://localhost:8080/api/pay?amount=100
</code></pre>
<p><strong>Expected Output</strong>:</p>
<pre><code class="lang-bash">Paid <span class="hljs-variable">$100</span>.0 using PayPal
</code></pre>
<p>If you change the qualifier in <code>PaymentService</code> to <code>"creditCardPayment"</code>, the output will change accordingly:</p>
<pre><code class="lang-bash">Paid <span class="hljs-variable">$100</span>.0 with Credit Card
</code></pre>
<h3 id="heading-benefits-of-using-dependency-injection">Benefits of Using Dependency Injection</h3>
<ul>
<li><p><strong>Loose Coupling</strong>: The service and controller don’t need to know the details of how a payment is processed. They simply rely on Spring to inject the correct implementation.</p>
</li>
<li><p><strong>Modularity</strong>: You can easily add new payment methods (for example, <code>BankTransferPayment</code>, <code>CryptoPayment</code>) by creating new classes annotated with <code>@Component</code> and adjusting the <code>@Qualifier</code>.</p>
</li>
<li><p><strong>Configurability</strong>: By leveraging Spring Profiles, you can switch strategies based on the environment (for example, development vs. production).</p>
</li>
</ul>
<p><strong>Example</strong>: You can use <code>@Profile</code> to automatically inject different strategies based on the active profile:</p>
<pre><code class="lang-java"><span class="hljs-meta">@Component</span>
<span class="hljs-meta">@Profile("dev")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DevPaymentStrategy</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">PaymentStrategy</span> </span>{ <span class="hljs-comment">/* ... */</span> }

<span class="hljs-meta">@Component</span>
<span class="hljs-meta">@Profile("prod")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProdPaymentStrategy</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">PaymentStrategy</span> </span>{ <span class="hljs-comment">/* ... */</span> }
</code></pre>
<h3 id="heading-key-takeaways-2">Key Takeaways</h3>
<ul>
<li><p>By using Spring Boot’s DI, you can simplify object creation and improve the flexibility of your code.</p>
</li>
<li><p>The Strategy Pattern combined with DI allows you to easily switch between different strategies without changing your core business logic.</p>
</li>
<li><p>Using <code>@Qualifier</code> and Spring Profiles gives you the flexibility to configure your application based on different environments or requirements.</p>
</li>
</ul>
<p>This approach not only makes your code cleaner but also prepares it for more advanced configurations and scaling in the future. In the next section, we’ll explore Best Practices and Optimization Tips to take your Spring Boot applications to the next level.</p>
<h2 id="heading-best-practices-and-optimization-tips">Best Practices and Optimization Tips</h2>
<h3 id="heading-general-best-practices">General Best Practices</h3>
<ul>
<li><p><strong>Don’t overuse patterns</strong>: Use them only when necessary. Overengineering can make your code harder to maintain.</p>
</li>
<li><p><strong>Favor composition over inheritance</strong>: Patterns like Strategy and Observer are great examples of this principle.</p>
</li>
<li><p><strong>Keep your patterns flexible</strong>: Leverage interfaces to keep your code decoupled.</p>
</li>
</ul>
<h3 id="heading-performance-considerations">Performance Considerations</h3>
<ul>
<li><p><strong>Singleton Pattern</strong>: Ensure thread safety by using <code>synchronized</code> or the <code>Bill Pugh Singleton Design</code>.</p>
</li>
<li><p><strong>Factory Pattern</strong>: Cache objects if they are expensive to create.</p>
</li>
<li><p><strong>Observer Pattern</strong>: Use asynchronous processing if you have many observers to prevent blocking.</p>
</li>
</ul>
<h3 id="heading-advanced-topics">Advanced Topics</h3>
<ul>
<li><p>Using <strong>Reflection</strong> with the Factory pattern for dynamic class loading.</p>
</li>
<li><p>Leveraging <strong>Spring Profiles</strong> to switch strategies based on the environment.</p>
</li>
<li><p>Adding <strong>Swagger Documentation</strong> for your API endpoints.</p>
</li>
</ul>
<h2 id="heading-conclusion-and-key-takeaways">Conclusion and Key Takeaways</h2>
<p>In this tutorial, we explored some of the most powerful design patterns—Singleton, Factory, Strategy, and Observer—and demonstrated how to implement them in Spring Boot. Let’s briefly summarize each pattern and highlight what it’s best suited for:</p>
<p><strong>Singleton Pattern</strong>:</p>
<ul>
<li><p><strong>Summary</strong>: Ensures that a class has only one instance and provides a global access point to it.</p>
</li>
<li><p><strong>Best For</strong>: Managing shared resources like configuration settings, database connections, or logging services. It’s ideal when you want to control access to a shared instance across your entire application.</p>
</li>
</ul>
<p><strong>Factory Pattern</strong>:</p>
<ul>
<li><p><strong>Summary</strong>: Provides a way to create objects without specifying the exact class to be instantiated. This pattern decouples object creation from the business logic.</p>
</li>
<li><p><strong>Best For</strong>: Scenarios where you need to create different types of objects based on input conditions, such as sending notifications via email, SMS, or push notifications. It’s great for making your code more modular and extensible.</p>
</li>
</ul>
<p><strong>Strategy Pattern</strong>:</p>
<ul>
<li><p><strong>Summary</strong>: Allows you to define a family of algorithms, encapsulate each one, and make them interchangeable. This pattern helps you choose an algorithm at runtime.</p>
</li>
<li><p><strong>Best For</strong>: When you need to switch between different behaviors or algorithms dynamically, such as processing various payment methods in an e-commerce application. It keeps your code flexible and adheres to the Open/Closed Principle.</p>
</li>
</ul>
<p><strong>Observer Pattern</strong>:</p>
<ul>
<li><p><strong>Summary</strong>: Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified automatically.</p>
</li>
<li><p><strong>Best For</strong>: Event-driven systems like notification services, real-time updates in chat apps, or systems that need to react to changes in data. It’s ideal for decoupling components and making your system more scalable.</p>
</li>
</ul>
<h3 id="heading-whats-next">What’s Next?</h3>
<p>Now that you’ve learned these essential design patterns, try integrating them into your existing projects to see how they can improve your code structure and scalability. Here are a few suggestions for further exploration:</p>
<ul>
<li><p><strong>Experiment</strong>: Try implementing other design patterns like <strong>Decorator</strong>, <strong>Proxy</strong>, and <strong>Builder</strong> to expand your toolkit.</p>
</li>
<li><p><strong>Practice</strong>: Use these patterns to refactor existing projects and enhance their maintainability.</p>
</li>
<li><p><strong>Share</strong>: If you have any questions or want to share your experience, feel free to reach out!</p>
</li>
</ul>
<p>I hope this guide has helped you understand how to effectively use design patterns in Java. Keep experimenting, and happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Dropbox-like Distributed File Storage System Using MinIO and gRPC ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, I’ll guide you through building a distributed file storage system inspired by Dropbox, using MinIO (an open-source, S3-compatible object storage server) and gRPC. The goal is to create a system that can store, replicate, and manage ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-dropbox-like-distributed-file-storage-system-using-minio-and-grpc/</link>
                <guid isPermaLink="false">6733b4dc73da063aa0407447</guid>
                
                    <category>
                        <![CDATA[ #minio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ gRPC ]]>
                    </category>
                
                    <category>
                        <![CDATA[ storage ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ minio object storage ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Birkaran Sachdev ]]>
                </dc:creator>
                <pubDate>Tue, 12 Nov 2024 20:04:44 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/GWQ67jjUg9g/upload/e37080969188b807a15d6ebdaf813fa2.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, I’ll guide you through building a <strong>distributed file storage system</strong> inspired by Dropbox, using MinIO (an open-source, S3-compatible object storage server) and gRPC. The goal is to create a system that can <strong>store, replicate, and manage files</strong> across multiple nodes, ensuring data availability and resilience.</p>
<p>We'll implement core features like file replication, metadata management, and versioning, all while demonstrating how to achieve eventual consistency in a distributed environment. By the end, you'll have a fully functional distributed file storage system that can handle high traffic, optimize storage, and ensure data integrity.</p>
<h3 id="heading-what-you-will-learn">What You Will Learn</h3>
<ul>
<li><p>How to set up <strong>MinIO</strong> for distributed object storage.</p>
</li>
<li><p>How to use <strong>gRPC</strong> for efficient client-server communication.</p>
</li>
<li><p>How to implement <strong>file replication</strong> and <strong>metadata management</strong>.</p>
</li>
<li><p>How to understand <strong>data consistency</strong> in a distributed system.</p>
</li>
<li><p>How to use <strong>Docker</strong> to deploy a scalable, distributed architecture.</p>
</li>
</ul>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>Before starting, ensure you have the following installed:</p>
<ul>
<li><p>Node.js (v14 or higher)</p>
</li>
<li><p>MinIO</p>
</li>
<li><p>gRPC and gRPC-tools</p>
</li>
<li><p>Docker</p>
</li>
</ul>
<p>You’ll also need to have a basic understanding of Node.js, object storage, and distributed systems.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-project-overview">Project Overview</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-1-setting-up-the-project">Step 1: Setting Up the Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-setting-up-minio-distributed-storage-nodes">Step 2: Setting Up MinIO Distributed Storage Nodes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-defining-the-grpc-protocol">Step 3: Defining the gRPC Protocol</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-implementing-the-grpc-server">Step 4: Implementing the gRPC Server</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-creating-the-client">Step 5: Creating the Client</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-6-running-the-system">Step 6: Running the System</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion-what-youve-learned">Conclusion: What You’ve Learned</a></p>
</li>
</ul>
<h2 id="heading-project-overview">Project Overview</h2>
<p>We'll build a <strong>distributed file storage system</strong> where:</p>
<ol>
<li><p>Users can upload and download files.</p>
</li>
<li><p>Files are replicated across multiple storage nodes to ensure high availability.</p>
</li>
<li><p>Metadata (like file names, upload times, and versions) is managed centrally.</p>
</li>
<li><p>The system handles <strong>eventual consistency</strong> by syncing file updates across nodes.</p>
</li>
</ol>
<h3 id="heading-system-architecture">System Architecture</h3>
<p>Our system will consist of:</p>
<ol>
<li><p><strong>gRPC Server</strong>: Manages file uploads, downloads, and metadata.</p>
</li>
<li><p><strong>MinIO Distributed Storage Nodes</strong>: Handles object storage and replication.</p>
</li>
<li><p><strong>Client Interface</strong>: Allows users to interact with the system via HTTP.</p>
</li>
</ol>
<h2 id="heading-step-1-setting-up-the-project">Step 1: Setting Up the Project</h2>
<p>Create a new directory for the project and initialize a Node.js application:</p>
<pre><code class="lang-javascript">mkdir distributed-file-storage
cd distributed-file-storage
npm init -y
</code></pre>
<p>Now, install the necessary dependencies:</p>
<pre><code class="lang-javascript">npm install grpc @grpc/grpc-js @grpc/proto-loader express multer dotenv minio
</code></pre>
<ul>
<li><p><strong>grpc</strong>: For building gRPC server and client.</p>
</li>
<li><p><strong>@grpc/proto-loader</strong>: Loads gRPC protocol files.</p>
</li>
<li><p><strong>express</strong>: For the client-side HTTP server.</p>
</li>
<li><p><strong>multer</strong>: For handling file uploads.</p>
</li>
<li><p><strong>dotenv</strong>: For managing environment variables.</p>
</li>
<li><p><strong>minio</strong>: MinIO client for interacting with storage nodes.</p>
</li>
</ul>
<p>Create a <strong>.env</strong> file with the following content:</p>
<pre><code class="lang-javascript">MINIO_ENDPOINT_1=localhost:<span class="hljs-number">9001</span>
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
PORT=<span class="hljs-number">5000</span>
</code></pre>
<h2 id="heading-step-2-setting-up-minio-distributed-storage-nodes">Step 2: Setting Up MinIO Distributed Storage Nodes</h2>
<p>We'll use <strong>Docker</strong> to run multiple MinIO instances, simulating a distributed environment. Run the following commands to set up three MinIO containers:</p>
<pre><code class="lang-javascript">docker run -p <span class="hljs-number">9001</span>:<span class="hljs-number">9000</span> --name minio1 -e <span class="hljs-string">"MINIO_ACCESS_KEY=minioadmin"</span> -e <span class="hljs-string">"MINIO_SECRET_KEY=minioadmin"</span> -d minio/minio server /data
docker run -p <span class="hljs-number">9002</span>:<span class="hljs-number">9000</span> --name minio2 -e <span class="hljs-string">"MINIO_ACCESS_KEY=minioadmin"</span> -e <span class="hljs-string">"MINIO_SECRET_KEY=minioadmin"</span> -d minio/minio server /data
docker run -p <span class="hljs-number">9003</span>:<span class="hljs-number">9000</span> --name minio3 -e <span class="hljs-string">"MINIO_ACCESS_KEY=minioadmin"</span> -e <span class="hljs-string">"MINIO_SECRET_KEY=minioadmin"</span> -d minio/minio server /data
</code></pre>
<p>These commands will start three MinIO nodes, each listening on a different port.</p>
<h2 id="heading-step-3-defining-the-grpc-protocol">Step 3: Defining the gRPC Protocol</h2>
<p>Create a new folder named <strong>protos</strong> and inside it, create a file called <strong>storage.proto</strong>:</p>
<pre><code class="lang-javascript">syntax = <span class="hljs-string">"proto3"</span>;

service FileStorage {
  rpc UploadFile(stream FileRequest) returns (UploadResponse);
  rpc DownloadFile(FileDownloadRequest) returns (stream FileResponse);
  rpc GetMetadata(FileMetadataRequest) returns (MetadataResponse);
}

message FileRequest {
  bytes fileData = <span class="hljs-number">1</span>;
  string fileName = <span class="hljs-number">2</span>;
}

message UploadResponse {
  string message = <span class="hljs-number">1</span>;
}

message FileDownloadRequest {
  string fileName = <span class="hljs-number">1</span>;
}

message FileResponse {
  bytes fileData = <span class="hljs-number">1</span>;
}

message FileMetadataRequest {
  string fileName = <span class="hljs-number">1</span>;
}

message MetadataResponse {
  string fileName = <span class="hljs-number">1</span>;
  string uploadTime = <span class="hljs-number">2</span>;
  string version = <span class="hljs-number">3</span>;
}
</code></pre>
<ul>
<li><p><strong>UploadFile</strong>: Streams file data from the client to the server.</p>
</li>
<li><p><strong>DownloadFile</strong>: Streams file data from the server to the client.</p>
</li>
<li><p><strong>GetMetadata</strong>: Retrieves metadata like file name, upload time, and version.</p>
</li>
</ul>
<h2 id="heading-step-4-implementing-the-grpc-server">Step 4: Implementing the gRPC Server</h2>
<p>Create a file called <strong>server.js</strong>:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();
<span class="hljs-keyword">const</span> grpc = <span class="hljs-built_in">require</span>(<span class="hljs-string">'@grpc/grpc-js'</span>);
<span class="hljs-keyword">const</span> protoLoader = <span class="hljs-built_in">require</span>(<span class="hljs-string">'@grpc/proto-loader'</span>);
<span class="hljs-keyword">const</span> Minio = <span class="hljs-built_in">require</span>(<span class="hljs-string">'minio'</span>);
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);
<span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>);

<span class="hljs-keyword">const</span> packageDefinition = protoLoader.loadSync(<span class="hljs-string">'protos/storage.proto'</span>);
<span class="hljs-keyword">const</span> storageProto = grpc.loadPackageDefinition(packageDefinition).FileStorage;

<span class="hljs-comment">// Set up MinIO clients for each node</span>
<span class="hljs-keyword">const</span> minioClients = [
  <span class="hljs-keyword">new</span> Minio.Client({
    <span class="hljs-attr">endPoint</span>: process.env.MINIO_ENDPOINT_1.split(<span class="hljs-string">':'</span>)[<span class="hljs-number">0</span>],
    <span class="hljs-attr">port</span>: <span class="hljs-built_in">parseInt</span>(process.env.MINIO_ENDPOINT_1.split(<span class="hljs-string">':'</span>)[<span class="hljs-number">1</span>]),
    <span class="hljs-attr">accessKey</span>: process.env.MINIO_ACCESS_KEY,
    <span class="hljs-attr">secretKey</span>: process.env.MINIO_SECRET_KEY,
    <span class="hljs-attr">useSSL</span>: <span class="hljs-literal">false</span>,
  })
];

<span class="hljs-comment">// Upload file to MinIO</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">uploadFile</span>(<span class="hljs-params">call, callback</span>) </span>{
  <span class="hljs-keyword">const</span> chunks = [];
  call.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =&gt;</span> chunks.push(chunk.fileData));
  call.on(<span class="hljs-string">'end'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> buffer = Buffer.concat(chunks);
    <span class="hljs-keyword">const</span> fileName = call.metadata.get(<span class="hljs-string">'fileName'</span>)[<span class="hljs-number">0</span>];

    <span class="hljs-comment">// Store file in MinIO</span>
    <span class="hljs-keyword">const</span> client = minioClients[<span class="hljs-number">0</span>];
    <span class="hljs-keyword">await</span> client.putObject(<span class="hljs-string">'files'</span>, fileName, buffer);
    callback(<span class="hljs-literal">null</span>, { <span class="hljs-attr">message</span>: <span class="hljs-string">`File <span class="hljs-subst">${fileName}</span> uploaded successfully`</span> });
  });
}

<span class="hljs-comment">// Download file from MinIO</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">downloadFile</span>(<span class="hljs-params">call</span>) </span>{
  <span class="hljs-keyword">const</span> { fileName } = call.request;
  <span class="hljs-keyword">const</span> client = minioClients[<span class="hljs-number">0</span>];

  client.getObject(<span class="hljs-string">'files'</span>, fileName, <span class="hljs-function">(<span class="hljs-params">err, stream</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (err) <span class="hljs-keyword">return</span> call.emit(<span class="hljs-string">'error'</span>, err);
    stream.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =&gt;</span> call.write({ <span class="hljs-attr">fileData</span>: chunk }));
    stream.on(<span class="hljs-string">'end'</span>, <span class="hljs-function">() =&gt;</span> call.end());
  });
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> server = <span class="hljs-keyword">new</span> grpc.Server();
  server.addService(storageProto.FileStorage.service, { uploadFile, downloadFile });
  server.bindAsync(<span class="hljs-string">'0.0.0.0:5000'</span>, grpc.ServerCredentials.createInsecure(), <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'gRPC server running on port 5000'</span>);
    server.start();
  });
}

main();
</code></pre>
<p>Here’s what’s going on in this code:</p>
<ol>
<li><p><strong>uploadFile</strong>: Handles file uploads by streaming data to the server and storing it in MinIO.</p>
</li>
<li><p><strong>downloadFile</strong>: Streams the requested file back to the client from MinIO.</p>
</li>
<li><p><strong>MinIO Clients</strong>: We set up multiple MinIO clients to handle distributed storage.</p>
</li>
</ol>
<h2 id="heading-step-5-creating-the-client">Step 5: Creating the Client</h2>
<p>Create a file named <strong>client.js</strong>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> grpc = <span class="hljs-built_in">require</span>(<span class="hljs-string">'@grpc/grpc-js'</span>);
<span class="hljs-keyword">const</span> protoLoader = <span class="hljs-built_in">require</span>(<span class="hljs-string">'@grpc/proto-loader'</span>);
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);

<span class="hljs-keyword">const</span> packageDefinition = protoLoader.loadSync(<span class="hljs-string">'protos/storage.proto'</span>);
<span class="hljs-keyword">const</span> storageProto = grpc.loadPackageDefinition(packageDefinition).FileStorage;
<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> storageProto(<span class="hljs-string">'localhost:5000'</span>, grpc.credentials.createInsecure());

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">uploadFile</span>(<span class="hljs-params">filePath</span>) </span>{
  <span class="hljs-keyword">const</span> call = client.uploadFile();
  <span class="hljs-keyword">const</span> fileName = filePath.split(<span class="hljs-string">'/'</span>).pop();
  <span class="hljs-keyword">const</span> stream = fs.createReadStream(filePath);

  stream.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =&gt;</span> call.write({ <span class="hljs-attr">fileData</span>: chunk }));
  stream.on(<span class="hljs-string">'end'</span>, <span class="hljs-function">() =&gt;</span> call.end());
  call.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">response</span>) =&gt;</span> <span class="hljs-built_in">console</span>.log(response.message));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">downloadFile</span>(<span class="hljs-params">fileName</span>) </span>{
  <span class="hljs-keyword">const</span> call = client.downloadFile({ fileName });
  <span class="hljs-keyword">const</span> writeStream = fs.createWriteStream(<span class="hljs-string">`downloaded_<span class="hljs-subst">${fileName}</span>`</span>);

  call.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =&gt;</span> writeStream.write(chunk.fileData));
  call.on(<span class="hljs-string">'end'</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Downloaded <span class="hljs-subst">${fileName}</span>`</span>));
}

uploadFile(<span class="hljs-string">'test.txt'</span>);  <span class="hljs-comment">// Example usage</span>
</code></pre>
<h2 id="heading-step-6-running-the-system">Step 6: Running the System</h2>
<ol>
<li><p><strong>Start the gRPC Server</strong>:</p>
<pre><code class="lang-javascript"> node server.js
</code></pre>
</li>
<li><p><strong>Run the Client</strong>:</p>
<pre><code class="lang-javascript"> node client.js
</code></pre>
</li>
</ol>
<h2 id="heading-conclusion-what-youve-learned">Conclusion: What You’ve Learned</h2>
<p>Congratulations! You've built a distributed file storage system using <strong>MinIO</strong> and <strong>gRPC</strong>. In this tutorial, you learned how to:</p>
<ol>
<li><p>Set up a <strong>distributed object storage</strong> system using MinIO.</p>
</li>
<li><p>Use <strong>gRPC</strong> to handle file uploads, downloads, and metadata management.</p>
</li>
<li><p>Implement <strong>file replication</strong> and <strong>eventual consistency</strong> across multiple nodes.</p>
</li>
<li><p>Utilize <strong>Docker</strong> to simulate a scalable distributed environment.</p>
</li>
</ol>
<h3 id="heading-next-steps">Next Steps:</h3>
<ol>
<li><p><strong>Add File Versioning</strong>: Store multiple versions of files for rollback.</p>
</li>
<li><p><strong>Implement Authentication</strong>: Secure your gRPC endpoints with JWT.</p>
</li>
<li><p><strong>Deploy with Kubernetes</strong>: Scale your system across multiple nodes for high availability.</p>
</li>
</ol>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Multi-Module Projects in Spring Boot for Scalable Microservices ]]>
                </title>
                <description>
                    <![CDATA[ As software applications grow in complexity, managing scalability, modularity, and clarity becomes essential. Spring Boot’s multi-module structure allows you to manage different parts of the application independently, which lets your team develop, te... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-multi-module-projects-in-spring-boot-for-scalable-microservices/</link>
                <guid isPermaLink="false">6733855c0e235bf7a79c5c4f</guid>
                
                    <category>
                        <![CDATA[ Spring Boot ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Java ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Microservices ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software architecture ]]>
                    </category>
                
                    <category>
                        <![CDATA[ maven ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Backend Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ scalability ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Birkaran Sachdev ]]>
                </dc:creator>
                <pubDate>Tue, 12 Nov 2024 16:42:04 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/uyfohHiTxho/upload/716c6610c336976df67b833912170336.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>As software applications grow in complexity, managing scalability, modularity, and clarity becomes essential.</p>
<p>Spring Boot’s multi-module structure allows you to manage different parts of the application independently, which lets your team develop, test, and deploy components separately. This structure keeps code organized and modular, making it useful for both microservices and large monolithic systems.</p>
<p>In this tutorial, you’ll build a multi-module Spring Boot project, with each module dedicated to a specific responsibility. You’ll learn how to set up modules, configure inter-module communication, handle errors, implement JWT-based security, and deploy using Docker.</p>
<p><strong>Prerequisites</strong>:</p>
<ul>
<li><p>Basic knowledge of Spring Boot and Maven.</p>
</li>
<li><p>Familiarity with Docker and CI/CD concepts (optional but helpful).</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-1-why-multi-module-projects">Why Multi-Module Projects?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-project-structure-and-architecture">Project Structure and Architecture</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-how-to-set-up-the-parent-project">How to Set Up the Parent Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-how-to-create-the-modules">How to Create the Modules</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-5-inter-module-communication">Inter-Module Communication</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-6-common-pitfalls-and-solutions">Common Pitfalls and Solutions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-7-testing-strategy">Testing Strategy</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-8-error-handling-and-logging">Error Handling and Logging</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-9-security-and-jwt-integration">Security and JWT Integration</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-10-deployment-with-docker-and-cicd">Deployment with Docker and CI/CD</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-11-best-practices-and-advanced-use-cases">Best Practices and Advanced Use Cases</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-12-conclusion-and-key-takeaways">Conclusion and Key Takeaways</a></p>
</li>
</ol>
<h2 id="heading-1-why-multi-module-projects">1. Why Multi-Module Projects?</h2>
<p>In single-module projects, components are often tightly coupled, making it difficult to scale and manage complex codebases. A multi-module structure offers several advantages:</p>
<ul>
<li><p><strong>Modularity</strong>: Each module is dedicated to a specific task, such as User Management or Inventory, simplifying management and troubleshooting.</p>
</li>
<li><p><strong>Team Scalability</strong>: Teams can work independently on different modules, minimizing conflicts and enhancing productivity.</p>
</li>
<li><p><strong>Flexible Deployment</strong>: Modules can be deployed or updated independently, which is particularly beneficial for microservices or large applications with numerous features.</p>
</li>
</ul>
<h3 id="heading-real-world-example"><strong>Real-World Example</strong></h3>
<p>Consider a large e-commerce application. Its architecture can be divided into distinct modules:</p>
<ul>
<li><p><strong>Customer Management</strong>: Responsible for handling customer profiles, preferences, and authentication.</p>
</li>
<li><p><strong>Product Management</strong>: Focuses on managing product details, stock, and pricing.</p>
</li>
<li><p><strong>Order Processing</strong>: Manages orders, payments, and order tracking.</p>
</li>
<li><p><strong>Inventory Management</strong>: Oversees stock levels and supplier orders.</p>
</li>
</ul>
<h3 id="heading-case-study-netflix"><strong>Case Study: Netflix</strong></h3>
<p>To illustrate these benefits, let's examine how Netflix employs a multi-module architecture.</p>
<p>Netflix is a leading example of a company that effectively uses this approach through its microservices architecture. Each microservice at Netflix is dedicated to a specific function, such as user authentication, content recommendations, or streaming services.</p>
<p>This modular structure enables Netflix to scale its operations efficiently, deploy updates independently, and maintain high availability and performance. By decoupling services, Netflix can manage millions of users and deliver content seamlessly worldwide, ensuring a robust and flexible system that supports its vast and dynamic platform.</p>
<p>This architecture not only enhances scalability but also improves fault isolation, allowing Netflix to innovate rapidly and respond effectively to user demands.</p>
<h2 id="heading-2-project-structure-and-architecture">2. Project Structure and Architecture</h2>
<p>Now let’s get back to our example project. Your multi-module Spring Boot project will use five key modules. Here’s the layout:</p>
<pre><code class="lang-plaintext">codespring-boot-multi-module/
 ├── common/               # Shared utilities and constants
 ├── domain/               # Domain entities
 ├── repository/           # Data access layer (DAL)
 ├── service/              # Business logic
 └── web/                  # Main Spring Boot application and controllers
</code></pre>
<p>Each module has a specific role:</p>
<ul>
<li><p><code>common</code>: Stores shared utilities, constants, and configuration files used across other modules.</p>
</li>
<li><p><code>domain</code>: Contains data models for your application.</p>
</li>
<li><p><code>repository</code>: Manages database operations.</p>
</li>
<li><p><code>service</code>: Encapsulates business logic.</p>
</li>
<li><p><code>web</code>: Defines REST API endpoints and serves as the application’s entry point.</p>
</li>
</ul>
<p>This structure aligns with <strong>separation of concerns</strong> principles, where each layer is independent and handles its own logic.</p>
<p>The diagram below illustrates the various modules:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730873719792/adfc3689-26ae-477a-9850-75070a777e5e.png" alt="Diagram showing a software architecture with five modules: Web, Service, Repository, Domain, and Common, connected by arrows indicating relationships." class="image--center mx-auto" width="1306" height="767" loading="lazy"></p>
<h2 id="heading-3-how-to-set-up-the-parent-project">3. How to Set Up the Parent Project</h2>
<h3 id="heading-step-1-create-the-root-project">Step 1: Create the Root Project</h3>
<p>Let’s run these commands to create the Maven parent project:</p>
<pre><code class="lang-bash">mvn archetype:generate -DgroupId=com.example -DartifactId=spring-boot-multi-module -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=<span class="hljs-literal">false</span>
<span class="hljs-built_in">cd</span> spring-boot-multi-module
</code></pre>
<h3 id="heading-step-2-configure-the-parent-pomxml">Step 2: Configure the Parent <code>pom.xml</code></h3>
<p>In the <code>pom.xml</code>, let’s define our dependencies and modules:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">project</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0"</span> <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span> <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0 http://www.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">modelVersion</span>&gt;</span>4.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">modelVersion</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.example<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-multi-module<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.0-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">packaging</span>&gt;</span>pom<span class="hljs-tag">&lt;/<span class="hljs-name">packaging</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">modules</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">module</span>&gt;</span>common<span class="hljs-tag">&lt;/<span class="hljs-name">module</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">module</span>&gt;</span>domain<span class="hljs-tag">&lt;/<span class="hljs-name">module</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">module</span>&gt;</span>repository<span class="hljs-tag">&lt;/<span class="hljs-name">module</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">module</span>&gt;</span>service<span class="hljs-tag">&lt;/<span class="hljs-name">module</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">module</span>&gt;</span>web<span class="hljs-tag">&lt;/<span class="hljs-name">module</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">modules</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">properties</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">java.version</span>&gt;</span>11<span class="hljs-tag">&lt;/<span class="hljs-name">java.version</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">spring.boot.version</span>&gt;</span>2.5.4<span class="hljs-tag">&lt;/<span class="hljs-name">spring.boot.version</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">properties</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependencyManagement</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-dependencies<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>${spring.boot.version}<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">type</span>&gt;</span>pom<span class="hljs-tag">&lt;/<span class="hljs-name">type</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>import<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependencyManagement</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">build</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">plugins</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">plugins</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">build</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">project</span>&gt;</span>
</code></pre>
<p>This <code>pom.xml</code> file centralizes dependencies and configurations, making it easier to manage shared settings across modules.</p>
<h2 id="heading-4-how-to-create-the-modules">4. How to Create the Modules</h2>
<h3 id="heading-common-module">Common Module</h3>
<p>Let’s create a <strong>common</strong> module to define shared utilities like date formatters. Create this module and add a sample utility class:</p>
<pre><code class="lang-bash">mvn archetype:generate -DgroupId=com.example.common -DartifactId=common -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=<span class="hljs-literal">false</span>
</code></pre>
<p><strong>Date Formatter Utility:</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.example.common;

<span class="hljs-keyword">import</span> java.time.LocalDate;
<span class="hljs-keyword">import</span> java.time.format.DateTimeFormatter;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DateUtils</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">formatDate</span><span class="hljs-params">(LocalDate date)</span> </span>{
        <span class="hljs-keyword">return</span> date.format(DateTimeFormatter.ofPattern(<span class="hljs-string">"yyyy-MM-dd"</span>));
    }
}
</code></pre>
<h3 id="heading-domain-module">Domain Module</h3>
<p>In the <strong>domain</strong> module, you will define your data models.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.example.domain;

<span class="hljs-keyword">import</span> javax.persistence.Entity;
<span class="hljs-keyword">import</span> javax.persistence.Id;

<span class="hljs-meta">@Entity</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
    <span class="hljs-meta">@Id</span>
    <span class="hljs-keyword">private</span> Long id;
    <span class="hljs-keyword">private</span> String name;

    <span class="hljs-comment">// Getters and Setters</span>
}
</code></pre>
<h3 id="heading-repository-module">Repository Module</h3>
<p>Let’s create the <strong>repository</strong> module to manage data access. Here’s a basic repository interface:</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.example.repository;

<span class="hljs-keyword">import</span> com.example.domain.User;
<span class="hljs-keyword">import</span> org.springframework.data.jpa.repository.JpaRepository;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">UserRepository</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">JpaRepository</span>&lt;<span class="hljs-title">User</span>, <span class="hljs-title">Long</span>&gt; </span>{}
</code></pre>
<h3 id="heading-service-module">Service Module</h3>
<p>Let’s create the <strong>service</strong> module to hold your business logic. Here’s an example service class:</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.example.service;

<span class="hljs-keyword">import</span> com.example.domain.User;
<span class="hljs-keyword">import</span> com.example.repository.UserRepository;
<span class="hljs-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;
<span class="hljs-keyword">import</span> org.springframework.stereotype.Service;

<span class="hljs-meta">@Service</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserService</span> </span>{

    <span class="hljs-meta">@Autowired</span>
    <span class="hljs-keyword">private</span> UserRepository userRepository;

    <span class="hljs-function"><span class="hljs-keyword">public</span> User <span class="hljs-title">getUserById</span><span class="hljs-params">(Long id)</span> </span>{
        <span class="hljs-keyword">return</span> userRepository.findById(id).orElse(<span class="hljs-keyword">null</span>);
    }
}
</code></pre>
<h3 id="heading-web-module">Web Module</h3>
<p>The <strong>web</strong> module serves as the REST API layer.</p>
<pre><code class="lang-java"><span class="hljs-meta">@RestController</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserController</span> </span>{

    <span class="hljs-meta">@Autowired</span>
    <span class="hljs-keyword">private</span> UserService userService;

    <span class="hljs-meta">@GetMapping("/users/{id}")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> User <span class="hljs-title">getUserById</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable</span> Long id)</span> </span>{
        <span class="hljs-keyword">return</span> userService.getUserById(id);
    }
}
</code></pre>
<h2 id="heading-5-inter-module-communication">5. Inter-Module Communication</h2>
<p>To avoid direct dependencies, you can use <strong>REST APIs</strong> or <strong>message brokers</strong> (like Kafka) for inter-module communication. This ensures loose coupling and allows each module to communicate independently.</p>
<p>The diagram below demonstrates how modules communicate with each other:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730874358819/89d7f058-d074-4b1d-bbb7-81a7bdcb868e.png" alt="Flowchart showing the interaction between modules in the software architecture: Web Module handles API endpoints and returns responses, Service Module executes business logic, Repository Module accesses data and returns processed data, and connects to a Database." class="image--center mx-auto" width="932" height="1263" loading="lazy"></p>
<p>The diagram illustrates how different system components communicate to handle requests efficiently.</p>
<p>The <strong>Web Module</strong> processes incoming API requests and forwards them to the <strong>Service Module</strong>, which contains the business logic. The <strong>Service Module</strong> then interacts with the <strong>Repository Module</strong> to fetch or update data in the <strong>Database</strong>. This layered approach ensures that each module operates independently, promoting flexibility and easier maintenance.</p>
<p><strong>Example Using Feign Client</strong>:</p>
<p>In the context of inter-module communication, using tools like <strong>Feign Clients</strong> is a powerful way to achieve loose coupling between services.</p>
<p>The Feign client allows one module to seamlessly communicate with another through REST API calls, without requiring direct dependencies. This approach fits perfectly within the layered architecture described earlier, where the <strong>Service Module</strong> can fetch data from other services or microservices using Feign clients, rather than directly accessing databases or hard-coding HTTP requests.</p>
<p>This not only simplifies the code but also improves scalability and maintainability by isolating service dependencies.</p>
<pre><code class="lang-java"><span class="hljs-meta">@FeignClient(name = "userServiceClient", url = "http://localhost:8081")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">UserServiceClient</span> </span>{
    <span class="hljs-meta">@GetMapping("/users/{id}")</span>
    <span class="hljs-function">User <span class="hljs-title">getUserById</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable("id")</span> Long id)</span></span>;
}
</code></pre>
<h2 id="heading-6-common-pitfalls-and-solutions">6. Common Pitfalls and Solutions</h2>
<p>When implementing a multi-module architecture, you may encounter several challenges. Here are some common pitfalls and their solutions:</p>
<ul>
<li><p><strong>Circular Dependencies</strong>: Modules may inadvertently depend on each other, creating a circular dependency that complicates builds and deployments.</p>
<ul>
<li><strong>Solution</strong>: Carefully design module interfaces and use dependency management tools to detect and resolve circular dependencies early in the development process.</li>
</ul>
</li>
<li><p><strong>Over-Engineering</strong>: There's a risk of creating too many modules, leading to unnecessary complexity.</p>
<ul>
<li><strong>Solution</strong>: Start with a minimal set of modules and only split further when there's a clear need, ensuring each module has a distinct responsibility.</li>
</ul>
</li>
<li><p><strong>Inconsistent Configurations</strong>: Managing configurations across multiple modules can lead to inconsistencies.</p>
<ul>
<li><strong>Solution</strong>: Use centralized configuration management tools, such as Spring Cloud Config, to maintain consistency across modules.</li>
</ul>
</li>
<li><p><strong>Communication Overhead</strong>: Inter-module communication can introduce latency and complexity.</p>
<ul>
<li><strong>Solution</strong>: Optimize communication by using efficient protocols and consider asynchronous messaging where appropriate to reduce latency.</li>
</ul>
</li>
<li><p><strong>Testing Complexity</strong>: Testing a multi-module project can be more complex due to the interactions between modules.</p>
<ul>
<li><strong>Solution</strong>: Implement a robust testing strategy that includes unit tests for individual modules and integration tests for inter-module interactions.</li>
</ul>
</li>
</ul>
<p>By being aware of these pitfalls and applying these solutions, you can effectively manage the complexities of a multi-module architecture and ensure a smooth development process.</p>
<h2 id="heading-7-testing-strategy-and-configuration">7. Testing Strategy and Configuration</h2>
<p>Testing each module independently and as a unit is critical in multi-module setups.</p>
<h3 id="heading-unit-tests">Unit Tests</h3>
<p>Here, we’ll use JUnit and Mockito for performing unit tests:</p>
<pre><code class="lang-java"><span class="hljs-meta">@RunWith(MockitoJUnitRunner.class)</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserServiceTest</span> </span>{

    <span class="hljs-meta">@Mock</span>
    <span class="hljs-keyword">private</span> UserRepository userRepository;

    <span class="hljs-meta">@InjectMocks</span>
    <span class="hljs-keyword">private</span> UserService userService;

    <span class="hljs-meta">@Test</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testGetUserById</span><span class="hljs-params">()</span> </span>{
        User user = <span class="hljs-keyword">new</span> User();
        user.setId(<span class="hljs-number">1L</span>);
        user.setName(<span class="hljs-string">"John"</span>);

        Mockito.when(userRepository.findById(<span class="hljs-number">1L</span>)).thenReturn(Optional.of(user));

        User result = userService.getUserById(<span class="hljs-number">1L</span>);
        assertEquals(<span class="hljs-string">"John"</span>, result.getName());
    }
}
</code></pre>
<h3 id="heading-integration-tests">Integration Tests</h3>
<p>And we’ll use Testcontainers with an in-memory database for integration tests:</p>
<pre><code class="lang-java"><span class="hljs-meta">@Testcontainers</span>
<span class="hljs-meta">@ExtendWith(SpringExtension.class)</span>
<span class="hljs-meta">@SpringBootTest</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserServiceIntegrationTest</span> </span>{

    <span class="hljs-meta">@Container</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> PostgreSQLContainer&lt;?&gt; postgresqlContainer = <span class="hljs-keyword">new</span> PostgreSQLContainer&lt;&gt;(<span class="hljs-string">"postgres:latest"</span>);

    <span class="hljs-meta">@Autowired</span>
    <span class="hljs-keyword">private</span> UserService userService;

    <span class="hljs-meta">@Test</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testFindById</span><span class="hljs-params">()</span> </span>{
        User user = userService.getUserById(<span class="hljs-number">1L</span>);
        assertNotNull(user);
    }
}
</code></pre>
<h2 id="heading-8-error-handling-and-logging">8. Error Handling and Logging</h2>
<p>Error handling and logging ensure a robust and debuggable application.</p>
<h3 id="heading-error-handling">Error Handling</h3>
<p>In this section, we'll explore how to handle errors gracefully in your Spring Boot application using a <strong>global exception handler</strong>. By using <code>@ControllerAdvice</code>, we'll set up a centralized way to catch and respond to errors, keeping our code clean and our responses consistent.</p>
<pre><code class="lang-java"><span class="hljs-meta">@ControllerAdvice</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GlobalExceptionHandler</span> </span>{

    <span class="hljs-meta">@ExceptionHandler(UserNotFoundException.class)</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> ResponseEntity&lt;String&gt; <span class="hljs-title">handleUserNotFoundException</span><span class="hljs-params">(UserNotFoundException ex)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ResponseEntity&lt;&gt;(<span class="hljs-string">"User not found"</span>, HttpStatus.NOT_FOUND);
    }
}
</code></pre>
<p>In the code example above, we define a <code>GlobalExceptionHandler</code> that catches any <code>UserNotFoundException</code> and returns a friendly message like "User not found" with a status of <code>404</code>. This way, you don’t have to handle this exception in every controller—you’ve got it covered in one place!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730874789550/feed75e6-f92c-4102-b3c3-a01f189f3cd7.png" alt="Flowchart depicting a sequence of interactions between a Client, Web Module, Global Error Handler, and Logger. The process involves handling requests, processing them, and managing exceptions. If an exception occurs, it is handled and logged, followed by an error response. If no exception occurs, a successful response is returned." class="image--center mx-auto" width="1617" height="1350" loading="lazy"></p>
<p>Now, let’s take a look at the diagram. Here’s how it all flows: when a client sends a request to our <strong>Web Module</strong>, if everything goes smoothly, you'll get a successful response. But if something goes wrong, like a user not being found, the error will be caught by our <strong>Global Error Handler</strong>. This handler logs the issue and returns a clean, structured response to the client.</p>
<p>This approach ensures that users get clear error messages while keeping your app’s internals hidden and secure.</p>
<h3 id="heading-logging">Logging</h3>
<p>Structured logging in each module improves traceability and debugging. You can use a centralized logging system like Logback and include correlation IDs to trace requests.</p>
<h2 id="heading-9-security-and-jwt-integration">9. Security and JWT Integration</h2>
<p>In this section, we’re going to set up <strong>JSON Web Tokens (JWT)</strong> to secure our endpoints and control access based on user roles. We'll configure this in the <code>SecurityConfig</code> class, which will help us enforce who can access what parts of our application.</p>
<pre><code class="lang-java"><span class="hljs-meta">@EnableWebSecurity</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SecurityConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">WebSecurityConfigurerAdapter</span> </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">configure</span><span class="hljs-params">(HttpSecurity http)</span> <span class="hljs-keyword">throws</span> Exception </span>{
        http.authorizeRequests()
            .antMatchers(<span class="hljs-string">"/admin/**"</span>).hasRole(<span class="hljs-string">"ADMIN"</span>)
            .antMatchers(<span class="hljs-string">"/user/**"</span>).hasAnyRole(<span class="hljs-string">"USER"</span>, <span class="hljs-string">"ADMIN"</span>)
            .anyRequest().authenticated()
            .and()
            .oauth2ResourceServer().jwt();
    }
}
</code></pre>
<p>In the code example above, you can see how we’ve defined access rules:</p>
<ul>
<li><p>The <code>/admin/**</code> endpoints are restricted to users with the <code>ADMIN</code> role.</p>
</li>
<li><p>The <code>/user/**</code> endpoints can be accessed by users with either the <code>USER</code> or <code>ADMIN</code> roles.</p>
</li>
<li><p>Any other requests will require the user to be authenticated.</p>
</li>
</ul>
<p>Next, we set up our application to validate incoming tokens using <code>.oauth2ResourceServer().jwt();</code>. This ensures that only requests with a valid token can access our secured endpoints.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730875088355/b0502e8e-88e4-4aab-bf52-81360892ffbb.png" alt="Diagram depicting a sequence of interactions among five components: Client, Web Module, Security Filter, Service, and Repository. Arrows represent steps for token authentication and accessing a service, including requests, validations, data fetching, and responses, with outcomes for both valid and invalid tokens." class="image--center mx-auto" width="2012" height="1531" loading="lazy"></p>
<p>Now, let’s walk through the diagram. When a client sends a request to access a resource, the <strong>Security Filter</strong> first checks if the provided JWT token is valid. If the token is valid, the request proceeds to the <strong>Service Module</strong> to fetch or process the data. If not, access is denied right away, and the client receives an error response.</p>
<p>This flow ensures that only authenticated users can access sensitive resources, keeping our application secure.</p>
<h2 id="heading-10-deployment-with-docker-and-cicd">10. Deployment with Docker and CI/CD</h2>
<p>In this section, we'll containerize each module using <strong>Docker</strong> to make our application easier to deploy and run consistently across different environments. We’ll also set up a <strong>CI/CD pipeline</strong> using GitHub Actions (but you can use Jenkins too if you prefer). Automating this process ensures that any changes you push are automatically built, tested, and deployed.</p>
<h3 id="heading-step-1-containerizing-with-docker">Step 1: Containerizing with Docker</h3>
<p>We start by creating a <strong>Dockerfile</strong> for the <strong>Web Module:</strong></p>
<pre><code class="lang-java">FROM openjdk:<span class="hljs-number">11</span>-jre-slim
COPY target/web-<span class="hljs-number">1.0</span>-SNAPSHOT.jar app.jar
ENTRYPOINT [<span class="hljs-string">"java"</span>, <span class="hljs-string">"-jar"</span>, <span class="hljs-string">"/app.jar"</span>]
</code></pre>
<p>Here, we’re using a lightweight version of Java 11 to keep our image size small. We copy the compiled <code>.jar</code> file into the container and set it up to run when the container starts.</p>
<h3 id="heading-step-2-using-docker-compose-for-multi-module-deployment">Step 2: Using Docker Compose for Multi-Module Deployment</h3>
<p>Now, we'll use a <strong>Docker Compose</strong> file to orchestrate multiple modules together:</p>
<pre><code class="lang-java">version: <span class="hljs-string">'3'</span>
services:
  web:
    build: ./web
    ports:
      - <span class="hljs-string">"8080:8080"</span>
  service:
    build: ./service
    ports:
      - <span class="hljs-string">"8081:8081"</span>
</code></pre>
<p>With this setup, we can run both the <strong>Web Module</strong> and the <strong>Service Module</strong> at the same time, making it easy to spin up the entire application with a single command. Each service is built separately from its own directory, and we expose the necessary ports to access them.</p>
<h3 id="heading-cicd-example-with-github-actions">CI/CD Example with GitHub Actions</h3>
<pre><code class="lang-java">name: CI Pipeline

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout<span class="hljs-meta">@v2</span>
    - name: Set up JDK <span class="hljs-number">11</span>
      uses: actions/setup-java<span class="hljs-meta">@v2</span>
      with:
        java-version: <span class="hljs-string">'11'</span>
    - name: Build with Maven
      run: mvn clean install
</code></pre>
<p>This pipeline automatically kicks in whenever you push new code or create a pull request. It checks out your code, sets up Java, and runs a Maven build to ensure everything is working correctly.</p>
<h2 id="heading-11-best-practices-and-advanced-use-cases">11. Best Practices and Advanced Use Cases</h2>
<p>The following best practices ensure maintainability and scalability.</p>
<h3 id="heading-best-practices">Best Practices</h3>
<ul>
<li><p><strong>Avoid Circular Dependencies</strong>: Ensure modules don’t have circular references to avoid build issues.</p>
</li>
<li><p><strong>Separate Concerns Clearly</strong>: Each module should focus on one responsibility.</p>
</li>
<li><p><strong>Centralized Configurations</strong>: Manage configurations centrally for consistent setups.</p>
</li>
</ul>
<h3 id="heading-advanced-use-cases">Advanced Use Cases</h3>
<ol>
<li><p><strong>Asynchronous Messaging with Kafka</strong>: Use Kafka for decoupled communication between services. Modules can publish and subscribe to events asynchronously.</p>
</li>
<li><p><strong>REST Client with Feign</strong>: Use Feign to call services within modules. Define a Feign client interface for communication.</p>
</li>
<li><p><strong>Caching for Performance</strong>: Use Spring Cache in the service module for optimizing data retrieval.</p>
</li>
</ol>
<h2 id="heading-conclusion-and-key-takeaways">Conclusion and Key Takeaways</h2>
<p>A multi-module Spring Boot project provides modularity, scalability, and ease of maintenance.</p>
<p>In this tutorial, you learned to set up modules, manage inter-module communication, handle errors, add security, and deploy with Docker.</p>
<p>Following best practices and using advanced techniques like messaging and caching will further optimize your multi-module architecture for production use.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
