<?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[ Spring Boot - 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[ Spring Boot - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 14 May 2026 11:47:31 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/spring-boot-cj3rk5tin007imsk8uz3eg4kz/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <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>
