<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Saurabh Mahajan</title>
    <description>The latest articles on DEV Community by Saurabh Mahajan (@saurabhthedev).</description>
    <link>https://dev.to/saurabhthedev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1266023%2Fa212964c-e6f8-4481-ae17-b684b545a96b.jpeg</url>
      <title>DEV Community: Saurabh Mahajan</title>
      <link>https://dev.to/saurabhthedev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/saurabhthedev"/>
    <language>en</language>
    <item>
      <title>S.O.L.I.D Principles - The Practical Guide</title>
      <dc:creator>Saurabh Mahajan</dc:creator>
      <pubDate>Wed, 24 Apr 2024 07:22:11 +0000</pubDate>
      <link>https://dev.to/saurabhthedev/solid-principles-the-practical-guide-2fj3</link>
      <guid>https://dev.to/saurabhthedev/solid-principles-the-practical-guide-2fj3</guid>
      <description>&lt;p&gt;Welcome to our exploration of SOLID principlesa foundational set of design principles in software engineering that can elevate the quality and maintainability of your code. In the ever-evolving landscape of software development, writing code that is not only functional but also adaptable and maintainable is crucial. This is where SOLID principles come into play, offering a framework to guide developers in creating clean, flexible, and scalable software designs.&lt;/p&gt;

&lt;p&gt;In this blog, we'll delve into the essence of each SOLID principle, understanding what they entail and why they matter. Whether you're a seasoned developer looking to refine your skills or a newcomer eager to grasp fundamental principles, this journey through SOLID principles promises to provide valuable insights and actionable knowledge. Let's embark on this exploration together and unlock the power of SOLID principles to elevate your software craftsmanship.&lt;/p&gt;




&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;SOLID principles&lt;/strong&gt; are a set of design principles that aim to make software designs more understandable, flexible, and maintainable.&lt;/p&gt;

&lt;p&gt;The SOLID acronym stands for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;S - Single Responsibility Principle (SRP)&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;O - Open/Closed Principle (OCP)&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;L - Liskov Substitution Principle (LSP)&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;I - Interface Segregation Principle (ISP)&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;D - Dependency Inversion Principle (DIP)&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These principles provide guidelines for writing clean, maintainable, and scalable code. By adhering to them, developers can create software systems that are easier to understand, extend, and modify.&lt;/p&gt;




&lt;h1&gt;
  
  
  History
&lt;/h1&gt;

&lt;p&gt;SOLID principles were introduced by Robert C. Martin (also known as Uncle Bob) in the early 2000s and have since become a cornerstone of object-oriented design and programming. Robert C. Martin introduced these principles in his paper "Design Principles and Design Patterns," presented at the OOPSLA conference in 1995. They were further popularized in his book "Agile Software Development, Principles, Patterns, and Practices," published in 2002. Since then, they have become foundational concepts in software engineering and are often taught in computer science courses and used in software development practices worldwide.&lt;/p&gt;




&lt;h1&gt;
  
  
  S - Single Responsibility Principle (SRP)
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Sd9AKjBa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1713597457276/27b47920-2477-4c3b-ba18-a3092c2b3bdc.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sd9AKjBa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1713597457276/27b47920-2477-4c3b-ba18-a3092c2b3bdc.webp" alt="" width="720" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Definition
&lt;/h2&gt;

&lt;p&gt;A class should have only one reason to change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explanation
&lt;/h2&gt;

&lt;p&gt;This principle suggests that a class should have only one responsibility or job. It should encapsulate one and only one aspect of functionality within the software. This principle promotes a modular approach to software development by encouraging developers to break down complex systems into smaller, more manageable components.&lt;/p&gt;

&lt;p&gt;To understand SRP more thoroughly, let's delve into its key aspects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Responsibility&lt;/strong&gt; : A responsibility in the context of SRP refers to a reason for a class to change. It can be defined as a cohesive set of tasks or functionalities that the class is responsible for.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reason to Change&lt;/strong&gt; : A reason to change is any requirement or feature that might necessitate modifications to the code. By adhering to SRP, we aim to minimize the number of reasons for a class to change, thus reducing the likelihood of unintended side effects when modifications are made.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Encapsulation&lt;/strong&gt; : SRP is closely related to the concept of encapsulation, which is one of the core principles of object-oriented programming. Encapsulation involves bundling data and the methods that operate on that data into a single unit (i.e., a class). SRP suggests that this unit should have a single, well-defined responsibility.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cohesion&lt;/strong&gt; : SRP encourages high cohesion within classes. Cohesion refers to the degree to which the elements of a module (e.g., a class) belong together. A class with high cohesion focuses on a single task or responsibility, making it easier to understand, maintain, and test.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Separation of Concerns&lt;/strong&gt; : SRP promotes the separation of concerns, which is a design principle aimed at separating a computer program into distinct sections, each addressing a separate concern. By separating concerns, we can manage complexity more effectively and improve maintainability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Benefits&lt;/strong&gt; :&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Example - Violation
&lt;/h2&gt;

&lt;p&gt;Let's consider an example where the Single Responsibility Principle is violated in an Account controller that handles deposit, withdrawal, and money transfer operations. We'll combine authorization logic, validation logic, and database transaction logic within the controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;@Controllerpublic class AccountController { private AccountService accountService; // Authorization and validation logic combined with business logic @PostMapping("/deposit") @ResponseBody public String deposit(@RequestParam("accountId") String accountId, @RequestParam("amount") double amount) { // Authorization logic if (!authorizeUser()) { return "Unauthorized"; } // Validation logic if (amount &amp;lt;= 0) { return "Invalid amount"; } // Database transaction logic if (accountService.deposit(accountId, amount)) { return "Deposit successful"; } else { return "Deposit failed"; } } // Similar methods for withdrawal and transfer private boolean authorizeUser() { // Authorization logic implementation return true; // Placeholder implementation }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;AccountController&lt;/code&gt; class is responsible for handling HTTP requests related to account operations. However, it is violating the Single Responsibility Principle by combining authorization logic, validation logic, and database transaction logic within the controller.&lt;/p&gt;

&lt;p&gt;To correct this violation and adhere to the Single Responsibility Principle, we can separate these concerns into distinct classes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;AuthorizationService&lt;/code&gt;: Responsible for handling authorization logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ValidationService&lt;/code&gt;: Responsible for handling validation logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;AccountService&lt;/code&gt;: Responsible for handling database transactions related to account operations.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Example - Fix
&lt;/h2&gt;

&lt;p&gt;Here's how we can refactor the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class AuthorizationService { public boolean authorizeUser() { // Authorization logic implementation return true; // Placeholder implementation }}

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class ValidationService { public boolean validateAmount(double amount) { return amount &amp;gt; 0; }}

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class AccountService { @Autowired private AccountRepository accountRepository; public boolean deposit(String accountId, double amount) { // Database transaction logic // Call methods from AccountRepository return true; // Placeholder implementation } // Similar methods for withdrawal and transfer}

import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.stereotype.Repository;@Repositorypublic interface AccountRepository extends JpaRepository&amp;lt;Account, Long&amp;gt; { // Define methods for interacting with the account database}

import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;@Controllerpublic class AccountController { @Autowired private AuthorizationService authorizationService; @Autowired private ValidationService validationService; @Autowired private AccountService accountService; @PostMapping("/deposit") @ResponseBody public String deposit(@RequestParam("accountId") String accountId, @RequestParam("amount") double amount) { if (!authorizationService.authorizeUser()) { return "Unauthorized"; } if (!validationService.validateAmount(amount)) { return "Invalid amount"; } if (accountService.deposit(accountId, amount)) { return "Deposit successful"; } else { return "Deposit failed"; } } // Similar methods for withdrawal and transfer}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this refactored version, we've separated the concerns into distinct classes: &lt;code&gt;AuthorizationService&lt;/code&gt;, &lt;code&gt;ValidationService&lt;/code&gt;, and &lt;code&gt;AccountService&lt;/code&gt;. The &lt;code&gt;AccountController&lt;/code&gt; now only orchestrates the flow of control by delegating the authorization and validation tasks to their respective services, and the database transaction logic is handled by the &lt;code&gt;AccountService&lt;/code&gt;. This separation of concerns adheres to the Single Responsibility Principle, making the codebase more modular, maintainable, and testable.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;O - Open/Closed Principle (OCP)&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DFzhGnHe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1713598800681/63fd27a9-fe5c-4ca0-8a53-4c1c9cc6cf3b.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DFzhGnHe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1713598800681/63fd27a9-fe5c-4ca0-8a53-4c1c9cc6cf3b.webp" alt="" width="720" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Definition
&lt;/h2&gt;

&lt;p&gt;Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explanation
&lt;/h2&gt;

&lt;p&gt;OCP emphasizes that software entities should allow for extension without requiring changes to their source code. This is achieved through abstraction and polymorphism.&lt;/p&gt;

&lt;p&gt;In simpler terms, this principle suggests that once a class (or module or function) is written and tested, it should not be modified to add new behavior or to alter existing behavior. Instead, it should be designed in a way that allows new functionalities to be added without changing its source code.&lt;/p&gt;

&lt;p&gt;Here's a detailed explanation of the Open-Closed Principle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open for Extension&lt;/strong&gt; : This aspect implies that the behavior of a software entity can be extended without modifying its source code. In practical terms, this means that you should be able to add new functionality to a class or module by creating new code (such as subclasses, new modules, or new functions) rather than changing the existing code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Closed for Modification&lt;/strong&gt; : This aspect implies that the existing source code of a software entity should not be changed. Once a class or module is developed, tested, and considered stable, it should remain untouched. Modifying existing code can introduce bugs and unintended consequences, especially in large systems.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To achieve the Open-Closed Principle, several design patterns and techniques can be applied:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Abstraction&lt;/strong&gt; : The base class provides a common interface, while subclasses can override or extend specific behaviors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interfaces&lt;/strong&gt; : Define interfaces or abstract classes that specify the behavior expected from different implementations. This allows new implementations to be added without modifying existing code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Composition over Inheritance&lt;/strong&gt; : Instead of using inheritance, use composition to build flexible and extensible systems. This involves creating classes that contain instances of other classes, allowing behavior to be composed dynamically.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Strategy Pattern&lt;/strong&gt; : Define a family of algorithms, encapsulate each one as an object, and make them interchangeable. This allows the behavior of a class to vary independently from the class itself, promoting the open-closed principle.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Decorator Pattern&lt;/strong&gt; : Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dependency Injection&lt;/strong&gt; : Instead of directly creating dependencies within a class, inject them from the outside. This allows different implementations to be provided at runtime, promoting flexibility and extensibility.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By following the Open-Closed Principle, you can create software that is easier to maintain, extend, and test. It encourages modular, reusable, and loosely coupled designs, which are essential for building scalable and adaptable systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example - Violation
&lt;/h2&gt;

&lt;p&gt;Suppose we have a &lt;code&gt;AccessControl&lt;/code&gt; interface and two concrete implementations: &lt;code&gt;BasicAccessControl&lt;/code&gt; and &lt;code&gt;AdminAccessControl&lt;/code&gt;. They both provide methods to check access and perform some security-related operations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface AccessControl { boolean hasAccess(String userId);}class BasicAccessControl implements AccessControl { @Override public boolean hasAccess(String userId) { // Basic access control logic return true; }}class AdminAccessControl implements AccessControl { @Override public boolean hasAccess(String userId) { // Admin access control logic return true; } // New method added violating OCP public void logAccess(String userId) { // Log access details System.out.println("Access logged for user: " + userId); }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;code&gt;AdminAccessControl&lt;/code&gt; violates the Open-Closed Principle by adding a new method &lt;code&gt;logAccess()&lt;/code&gt; to the class. If we continue adding new methods for different purposes, it would lead to modifying existing classes whenever new functionalities are introduced, which is against the principle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example - Fix
&lt;/h2&gt;

&lt;p&gt;To adhere to the Open-Closed Principle, we can introduce a new class hierarchy for handling additional functionalities. We'll introduce an &lt;code&gt;AccessControlLogger&lt;/code&gt; interface and concrete implementations for logging access details. This way, we can extend the behavior of &lt;code&gt;AccessControl&lt;/code&gt; without modifying existing classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface AccessControl { boolean hasAccess(String userId);}class BasicAccessControl implements AccessControl { @Override public boolean hasAccess(String userId) { // Basic access control logic return true; }}interface AccessControlLogger { void logAccess(String userId);}class BasicAccessControlLogger implements AccessControlLogger { @Override public void logAccess(String userId) { // Log basic access details System.out.println("Basic access logged for user: " + userId); }}class AdminAccessControl implements AccessControl, AccessControlLogger { @Override public boolean hasAccess(String userId) { // Admin access control logic return true; } @Override public void logAccess(String userId) { // Log admin access details System.out.println("Admin access logged for user: " + userId); }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, &lt;code&gt;AdminAccessControl&lt;/code&gt; implements both &lt;code&gt;AccessControl&lt;/code&gt; and &lt;code&gt;AccessControlLogger&lt;/code&gt;, allowing it to provide access control and logging functionalities separately. This approach follows the Open-Closed Principle because we can add new functionalities (like logging) without modifying existing classes.&lt;/p&gt;




&lt;h1&gt;
  
  
  L - Liskov Substitution Principle (LSP)
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---Fjb_zJ0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1713598817897/fca50916-12ff-4bbd-a7d8-818c33882c7d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---Fjb_zJ0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1713598817897/fca50916-12ff-4bbd-a7d8-818c33882c7d.png" alt="" width="800" height="603"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Definition
&lt;/h2&gt;

&lt;p&gt;A subclass should be substitutable for its superclass without causing errors or unexpected behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explanation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Key Points:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inheritance Hierarchy&lt;/strong&gt; : The principle primarily deals with inheritance relationships, where a subclass inherits from a superclass. The subclass should extend or specialize the behavior of the superclass but not alter its fundamental contract or assumptions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Behavior Preservation&lt;/strong&gt; : When substituting a subclass instance for a superclass instance, the client code should continue to function correctly. This means that the subclass must honor the same contracts, adhere to the same preconditions and postconditions, and respect the same invariants as the superclass.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Type Contracts&lt;/strong&gt; : The LSP emphasizes the importance of adhering to the contracts defined by the superclass. This includes method signatures, behavior expectations, and any constraints specified by the superclass. Subclasses can strengthen (but not weaken) these contracts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Design by Contract&lt;/strong&gt; : The principle aligns with the broader concept of "design by contract," where classes explicitly state their preconditions, postconditions, and invariants. Subclasses are expected to adhere to and possibly extend these contracts, ensuring that they maintain the same level of reliability and predictability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Robustness and Extensibility&lt;/strong&gt; : Adhering to the LSP enhances the robustness and extensibility of object-oriented systems. It allows for easy substitution of components, facilitates code reuse, and simplifies maintenance, as changes to subclasses don't break client code relying on superclass behavior.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Benefits:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintainability&lt;/strong&gt; : Eases maintenance by allowing for easy substitution of objects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Robustness&lt;/strong&gt; : Ensures that changes to subclasses don't inadvertently break client code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Code Reusability&lt;/strong&gt; : Encourages code reuse through inheritance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Design Clarity&lt;/strong&gt; : Promotes clear interfaces and contracts between classes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By adhering to the Liskov Substitution Principle, developers can create more flexible, robust, and maintainable object-oriented systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example - Violation
&lt;/h2&gt;

&lt;p&gt;Let's consider a scenario where we have a caching system with two implementations: a &lt;code&gt;LocalCache&lt;/code&gt; and a &lt;code&gt;NetworkCache&lt;/code&gt;. We'll demonstrate a violation of the Liskov Substitution Principle and then correct it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Cache { // Common cache methods public void put(String key, Object value) { // implementation } public Object get(String key) { // implementation return null; }}class LocalCache extends Cache { // Local cache specific methods and implementations}class NetworkCache extends Cache { // Network cache specific methods and implementations}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's say the &lt;code&gt;NetworkCache&lt;/code&gt; has to make a network call to fetch the data, but the &lt;code&gt;get&lt;/code&gt; method in the &lt;code&gt;Cache&lt;/code&gt; class is synchronous. To handle the network call, &lt;code&gt;NetworkCache&lt;/code&gt; might need an asynchronous &lt;code&gt;get&lt;/code&gt; method. In this case, &lt;code&gt;NetworkCache&lt;/code&gt; cannot be substituted for &lt;code&gt;Cache&lt;/code&gt; without affecting the behavior, violating LSP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example - Fix
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface Cache { void put(String key, Object value); Object get(String key);}class LocalCache implements Cache { @Override public void put(String key, Object value) { // Local cache specific implementation } @Override public Object get(String key) { // Local cache specific implementation return null; }}class NetworkCache implements Cache { @Override public void put(String key, Object value) { // Network cache specific implementation } @Override public Object get(String key) { // Asynchronous network call implementation // This might involve using callbacks, CompletableFuture, or similar mechanisms // This method might not return the actual value immediately but instead returns a Future or uses a callback return fetchDataAsynchronously(key); } private CompletableFuture&amp;lt;Object&amp;gt; fetchDataAsynchronously(String key) { // Simulated asynchronous network call return CompletableFuture.supplyAsync(() -&amp;gt; { // Actual network call to fetch data return fetchDataFromNetwork(key); }); } private Object fetchDataFromNetwork(String key) { // Simulated network data fetch return null; }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this corrected version, both &lt;code&gt;LocalCache&lt;/code&gt; and &lt;code&gt;NetworkCache&lt;/code&gt; implement the &lt;code&gt;Cache&lt;/code&gt; interface. The &lt;code&gt;get&lt;/code&gt; method in &lt;code&gt;NetworkCache&lt;/code&gt; returns a &lt;code&gt;CompletableFuture&lt;/code&gt; representing the asynchronous result of the network call. This allows &lt;code&gt;NetworkCache&lt;/code&gt; to be substituted for &lt;code&gt;Cache&lt;/code&gt; without altering the behavior expected by client code, thus satisfying the Liskov Substitution Principle.&lt;/p&gt;




&lt;h1&gt;
  
  
  I - &lt;strong&gt;Interface Segregation Principle (ISP)&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h8BmUNtC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1713598835525/a65774d6-5f71-4b4e-8c4a-c4fb6c57308d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h8BmUNtC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1713598835525/a65774d6-5f71-4b4e-8c4a-c4fb6c57308d.png" alt="" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Definition
&lt;/h2&gt;

&lt;p&gt;Clients should not be forced to depend on interfaces they do not use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explanation
&lt;/h2&gt;

&lt;p&gt;The principle emphasizes the importance of designing software interfaces in a way that clients should not be forced to depend on methods they do not use. In simpler terms, ISP advocates for smaller, cohesive interfaces tailored to specific client requirements rather than large, monolithic ones.&lt;/p&gt;

&lt;p&gt;At its core, ISP encourages developers to create specialized interfaces that contain only the necessary methods relevant to a specific client or group of clients. This prevents clients from being burdened with methods they don't need, which in turn promotes cleaner, more maintainable code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;

&lt;p&gt;Consider a scenario where you have an interface called &lt;code&gt;Worker&lt;/code&gt; which has methods such as &lt;code&gt;work()&lt;/code&gt;, &lt;code&gt;eat()&lt;/code&gt;, and &lt;code&gt;sleep()&lt;/code&gt;. Now, let's say you have two types of clients: &lt;code&gt;Robot&lt;/code&gt; and &lt;code&gt;Human&lt;/code&gt;. Robots don't eat or sleep, they only work. Humans, on the other hand, do all three tasks.&lt;/p&gt;

&lt;p&gt;With a single &lt;code&gt;Worker&lt;/code&gt; interface, both &lt;code&gt;Robot&lt;/code&gt; and &lt;code&gt;Human&lt;/code&gt; classes would have to implement all three methods. This violates the Interface Segregation Principle because the &lt;code&gt;Robot&lt;/code&gt; class would be forced to implement methods it doesn't need.&lt;/p&gt;

&lt;p&gt;By segregating the interfaces, you could have an &lt;code&gt;Workable&lt;/code&gt; interface containing only the &lt;code&gt;work()&lt;/code&gt; method, and a &lt;code&gt;Eatable&lt;/code&gt; interface containing only the &lt;code&gt;eat()&lt;/code&gt; method. Both &lt;code&gt;Robot&lt;/code&gt; and &lt;code&gt;Human&lt;/code&gt; classes would then implement only the interfaces relevant to them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduced Dependency&lt;/strong&gt; : Clients are not forced to depend on methods they don't use, reducing unnecessary dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improved Cohesion&lt;/strong&gt; : Interfaces become more focused and cohesive, leading to clearer and more maintainable code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easier Testing&lt;/strong&gt; : Smaller interfaces are easier to test because there are fewer methods to consider in each test case.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexibility and Scalability&lt;/strong&gt; : It facilitates easier modifications and extensions as new clients can be accommodated without affecting existing ones.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Implementation Guidelines:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Identify Clients&lt;/strong&gt; : Understand the different types of clients that will interact with your system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Group Methods&lt;/strong&gt; : Group methods based on client requirements and create smaller, focused interfaces.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prefer Composition over Inheritance&lt;/strong&gt; : Sometimes, ISP can be achieved more effectively through composition rather than inheritance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Refactor Existing Code&lt;/strong&gt; : If necessary, refactor existing interfaces to adhere to the ISP.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Interface Segregation Principle is a fundamental concept in OOP design that promotes flexibility, maintainability, and scalability in software systems. By focusing on the specific needs of clients and avoiding unnecessary dependencies, developers can create more robust and adaptable codebases. Following ISP, along with other SOLID principles, can lead to better-designed, more maintainable, and easier-to-understand software architectures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example - Violation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// MediaPlayer.javapublic interface MediaPlayer { String fetchAudioUrl(); String fetchVideoUrl();}// WindowsMediaPlayer.javapublic class WindowsMediaPlayer implements MediaPlayer { @Override public String fetchAudioUrl() { return "Fetch audio URL for Windows Media Player"; } @Override public String fetchVideoUrl() { return "Fetch video URL for Windows Media Player"; }}// VlcMediaPlayer.javapublic class VlcMediaPlayer implements MediaPlayer { @Override public String fetchAudioUrl() { return "Fetch audio URL for VLC Media Player"; } @Override public String fetchVideoUrl() { return "Fetch video URL for VLC Media Player"; }}// SpotifyMediaPlayer.javapublic class SpotifyMediaPlayer implements MediaPlayer { @Override public String fetchAudioUrl() { return "Fetch audio URL for Spotify Media Player"; } @Override public String fetchVideoUrl() { // Spotify doesn't support direct fetching of video URLs throw new UnsupportedOperationException("Spotify does not support fetching video URLs"); }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The issue here is that all implementations of the &lt;code&gt;MediaPlayer&lt;/code&gt; interface are forced to implement both &lt;code&gt;fetchAudioUrl()&lt;/code&gt; and &lt;code&gt;fetchVideoUrl()&lt;/code&gt; methods, even if some players like &lt;code&gt;SpotifyMediaPlayer&lt;/code&gt; don't support fetching audio URLs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example - Fix
&lt;/h2&gt;

&lt;p&gt;To adhere to the Interface Segregation Principle, we should split the &lt;code&gt;MediaPlayer&lt;/code&gt; interface into two separate interfaces, one for audio-related functionalities and another for video-related functionalities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// AudioPlayer.javapublic interface AudioPlayer { String fetchAudioUrl();}// VideoPlayer.javapublic interface VideoPlayer { String fetchVideoUrl();}// WindowsMediaPlayer.javapublic class WindowsMediaPlayer implements AudioPlayer, VideoPlayer { @Override public String fetchAudioUrl() { return "Fetch audio URL for Windows Media Player"; } @Override public String fetchVideoUrl() { return "Fetch video URL for Windows Media Player"; }}// VlcMediaPlayer.javapublic class VlcMediaPlayer implements AudioPlayer, VideoPlayer { @Override public String fetchAudioUrl() { return "Fetch audio URL for VLC Media Player"; } @Override public String fetchVideoUrl() { return "Fetch video URL for VLC Media Player"; }}// SpotifyMediaPlayer.javapublic class SpotifyMediaPlayer implements VideoPlayer { @Override public String fetchVideoUrl() { return "Fetch video URL for Spotify Media Player"; }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By splitting the &lt;code&gt;MediaPlayer&lt;/code&gt; interface into &lt;code&gt;AudioPlayer&lt;/code&gt; and &lt;code&gt;VideoPlayer&lt;/code&gt;, each class is now only forced to implement the methods that are relevant to it. This adheres to the Interface Segregation Principle, as clients are not forced to depend on interfaces they do not use. Now, &lt;code&gt;SpotifyMediaPlayer&lt;/code&gt; doesn't need to implement the &lt;code&gt;fetchAudioUrl()&lt;/code&gt; method, thus preventing it from having to throw an &lt;code&gt;UnsupportedOperationException&lt;/code&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  D - &lt;strong&gt;Dependency Inversion Principle (DIP)&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S0yiqPYE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1713598852517/a5b7dc15-aa82-4c29-970e-12d0bd2b1680.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S0yiqPYE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1713598852517/a5b7dc15-aa82-4c29-970e-12d0bd2b1680.webp" alt="" width="638" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Definition
&lt;/h2&gt;

&lt;p&gt;High-level modules should not depend on low-level modules; both should depend on abstractions. Abstractions should not depend on details; details should depend on abstractions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explanation
&lt;/h2&gt;

&lt;p&gt;The principle suggests that high-level modules (which contain the more abstract and policy-related aspects of the application) should not depend on low-level modules (which contain the more detailed implementation details). Instead, both should depend on abstractions, which can be interfaces or abstract classes. This decouples modules and makes the code more flexible and easier to maintain.&lt;/p&gt;

&lt;p&gt;At its core, the Dependency Inversion Principle suggests two main things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;High-level modules should not depend on low-level modules. Both should depend on abstractions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Abstractions should not depend on details. Details should depend on abstractions.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's break down these concepts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;High-level modules should not depend on low-level modules&lt;/strong&gt; : This means that classes or modules that implement high-level policy or logic should not depend on the details of low-level components, such as specific implementations or concrete classes. Instead, both high-level and low-level modules should depend on abstractions. For example, rather than a high-level module directly instantiating or depending on a specific database class, it should depend on an interface or an abstract class that defines the behavior expected from the database component. This promotes decoupling and allows for easier substitution of components.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Abstractions should not depend on details&lt;/strong&gt; : This suggests that interfaces or abstract classes (abstractions) should define the behavior or contract that components adhere to, without depending on the specific implementation details of those components. In other words, the high-level policy or logic should not be affected by changes to the low-level details. This allows for greater flexibility and extensibility in the system. For example, if you have an interface for a logging service, it should define methods like &lt;code&gt;logInfo&lt;/code&gt;, &lt;code&gt;logError&lt;/code&gt;, etc., without specifying how those methods are implemented. Concrete logging implementations can then adhere to this interface, allowing for easy swapping between different logging mechanisms without affecting the rest of the codebase.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By adhering to the Dependency Inversion Principle, you create a more modular and flexible design where components can be easily replaced or extended without impacting other parts of the system. It also facilitates easier testing since components can be easily mocked or replaced with stubs or fakes. Overall, DIP promotes a design that is easier to maintain, extend, and understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example - Violation
&lt;/h2&gt;

&lt;p&gt;Initially, the &lt;code&gt;NotificationService&lt;/code&gt; directly depends on the concrete implementation of the &lt;code&gt;EmailService&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// High-level moduleclass NotificationService { private EmailService emailService; public NotificationService() { this.emailService = new EmailService(); } public void sendNotification(String message) { emailService.sendEmail("user@example.com", message); }}// Low-level moduleclass EmailService { public void sendEmail(String recipient, String message) { // Logic to send email System.out.println("Email sent to " + recipient + ": " + message); }}public class Main { public static void main(String[] args) { NotificationService notificationService = new NotificationService(); notificationService.sendNotification("Hello, this is a notification!"); }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example - Fix
&lt;/h2&gt;

&lt;p&gt;To adhere to the Dependency Inversion Principle, we'll introduce an abstraction (interface) &lt;code&gt;NotificationProvider&lt;/code&gt; that both &lt;code&gt;NotificationService&lt;/code&gt;, &lt;code&gt;EmailService&lt;/code&gt;, and &lt;code&gt;SMSService&lt;/code&gt; will depend on. This way, &lt;code&gt;NotificationService&lt;/code&gt; won't depend directly on the concrete implementations of these services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Abstractioninterface NotificationProvider { void sendNotification(String recipient, String message);}// Low-level moduleclass EmailService implements NotificationProvider { public void sendNotification(String recipient, String message) { // Logic to send email System.out.println("Email sent to " + recipient + ": " + message); }}// Low-level moduleclass SMSService implements NotificationProvider { public void sendNotification(String recipient, String message) { // Logic to send SMS System.out.println("SMS sent to " + recipient + ": " + message); }}// High-level moduleclass NotificationService { private NotificationProvider notificationProvider; public NotificationService(NotificationProvider notificationProvider) { this.notificationProvider = notificationProvider; } public void sendNotification(String recipient, String message) { notificationProvider.sendNotification(recipient, message); }}public class Main { public static void main(String[] args) { NotificationProvider emailService = new EmailService(); // Dependency injection NotificationProvider smsService = new SMSService(); // Dependency injection NotificationService emailNotificationService = new NotificationService(emailService); emailNotificationService.sendNotification("user@example.com", "Hello, this is an email notification!"); NotificationService smsNotificationService = new NotificationService(smsService); smsNotificationService.sendNotification("1234567890", "Hello, this is an SMS notification!"); }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this refactored version, both &lt;code&gt;EmailService&lt;/code&gt; and &lt;code&gt;SMSService&lt;/code&gt; implement the &lt;code&gt;NotificationProvider&lt;/code&gt; interface. This allows the &lt;code&gt;NotificationService&lt;/code&gt; to be decoupled from specific implementations of notification providers and to depend on abstractions. This design adheres to the Dependency Inversion Principle, promoting flexibility and easier maintenance of the codebase.&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;In conclusion, SOLID principles serve as guiding beacons in the vast sea of software development, illuminating the path to creating robust, maintainable, and adaptable codebases. By adhering to these principlesSingle Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversiondevelopers can foster codebases that are easier to understand, extend, and modify.&lt;/p&gt;

&lt;p&gt;As we wrap up our exploration of SOLID principles, it's essential to recognize their significance in shaping not only individual code modules but entire software ecosystems. By embracing SOLID principles, developers empower themselves to write code that not only solves immediate problems but also stands the test of time, evolving gracefully as requirements change and technologies advance.&lt;/p&gt;

&lt;p&gt;So, whether you're embarking on a new project or refactoring existing code, remember the SOLID principles as your guiding compass. Let them steer you towards software designs that are not only elegant but also resilient in the face of change. Together, let's build a future where SOLID principles are more than just principlesthey're the foundation upon which exceptional software is built.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Object-Oriented Programming Concepts</title>
      <dc:creator>Saurabh Mahajan</dc:creator>
      <pubDate>Mon, 08 Apr 2024 18:13:37 +0000</pubDate>
      <link>https://dev.to/saurabhthedev/object-oriented-programming-concepts-1404</link>
      <guid>https://dev.to/saurabhthedev/object-oriented-programming-concepts-1404</guid>
      <description>&lt;p&gt;Object-Oriented Programming (OOP) has emerged as a powerful paradigm in the world of software development, revolutionizing the way we design and build applications. At its core, OOP is a approach that models real-world concepts by creating objects that encapsulate both data and the code that operates on that data.&lt;/p&gt;

&lt;p&gt;The beauty of OOP lies in its ability to create modular, reusable, and extensible code. By organizing software into objects, programmers can break down complex problems into smaller, more manageable components, each with its own well-defined responsibilities and behaviors.&lt;/p&gt;

&lt;p&gt;OOP concepts have been instrumental in the development of robust, scalable, and maintainable software systems across various domains, from web applications and mobile apps to games and scientific simulations. By embracing OOP principles, programmers can create code that is not only functional but also easier to understand, modify, and extend over time.&lt;/p&gt;

&lt;p&gt;In this blog, we'll explore the intricacies of OOP concepts, providing examples and real-world use cases to help you grasp their significance and practical applications. Whether you're a beginner or an experienced programmer, mastering OOP will undoubtedly enhance your ability to write clean, efficient, and future-proof code.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why is OOPs required?
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modularity&lt;/strong&gt; : OOPs promotes modular design, where the program is divided into smaller, reusable components (objects), making it easier to develop, maintain, and modify the code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Code Reusability&lt;/strong&gt; : Through inheritance, objects can reuse code from existing classes, reducing redundancy and promoting code reuse.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Abstraction&lt;/strong&gt; : OOPs allows for data abstraction, which means that the implementation details of an object are hidden from the outside world, providing better security and flexibility.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Encapsulation&lt;/strong&gt; : By encapsulating data and methods within objects, OOPs provides better control over data access and modification, improving code organization and security.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Polymorphism&lt;/strong&gt; : Polymorphism allows objects of different classes to be treated as objects of a common superclass, enabling more flexible and extensible code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintainability&lt;/strong&gt; : OOPs promotes modularity, which makes it easier to identify and fix errors, as well as add new features or modify existing ones, improving code maintainability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-world Modeling&lt;/strong&gt; : OOPs provides a way to model real-world objects and their interactions, making it easier to understand and design complex systems.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h1&gt;
  
  
  Class
&lt;/h1&gt;

&lt;p&gt;A class is a blueprint or template for creating objects. It defines a set of properties (attributes) and behaviors (methods) that objects of that class will have. Classes are the fundamental building blocks of Object-Oriented Programming (OOP).&lt;/p&gt;

&lt;p&gt;A class is a user-defined data type that encapsulates data (attributes) and code (methods) that operate on that data.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class BankAccount { // Attributes (Properties) private String accountNumber; private String accountHolderName; private double balance; // Constructor public BankAccount(String accountNumber, String accountHolderName, double initialBalance) { this.accountNumber = accountNumber; this.accountHolderName = accountHolderName; this.balance = initialBalance; } // Methods (Behaviors) public void deposit(double amount) { balance += amount; System.out.println( "Deposited $" + amount + ". New balance: $" + balance); } public void withdraw(double amount) { if (balance &amp;gt;= amount) { balance -= amount; System.out.println( "Withdrew $" + amount + ". New balance: $" + balance); } else { System.out.println("Insufficient funds."); } } // Getter and Setter methods for attributes // ...}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;BankAccount&lt;/code&gt; class has three attributes (&lt;code&gt;accountNumber&lt;/code&gt;, &lt;code&gt;accountHolderName&lt;/code&gt;, and &lt;code&gt;balance&lt;/code&gt;) that define the state of a bank account object. It also has a constructor that initializes these attributes when creating a new &lt;code&gt;BankAccount&lt;/code&gt; object. Additionally, the class has two methods (&lt;code&gt;deposit()&lt;/code&gt; and &lt;code&gt;withdraw()&lt;/code&gt;) that define the behavior of a bank account object.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Components of a Class:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modifiers&lt;/strong&gt; : These specify the access level of the class, such as &lt;code&gt;public&lt;/code&gt;, &lt;code&gt;private&lt;/code&gt;, &lt;code&gt;protected&lt;/code&gt;, or default (no modifier).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Class Name&lt;/strong&gt; : The name of the class, following the naming conventions (e.g., &lt;code&gt;BankAccount&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Superclass (if any)&lt;/strong&gt;: If the class inherits from another class, the name of the superclass is specified using the &lt;code&gt;extends&lt;/code&gt; keyword.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interfaces (if any)&lt;/strong&gt;: If the class implements one or more interfaces, they are specified using the &lt;code&gt;implements&lt;/code&gt; keyword.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Body&lt;/strong&gt; : The class body is enclosed within curly braces &lt;code&gt;{ }&lt;/code&gt;, containing the class members (attributes and methods).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h1&gt;
  
  
  Object
&lt;/h1&gt;

&lt;p&gt;An object is an instance of a class. It is a real-world entity that has its own state (attributes) and behavior (methods) defined by the class it belongs to.&lt;/p&gt;

&lt;p&gt;An object is a single instance of a class that encapsulates data and the code that operates on that data.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BankAccount account1 = new BankAccount("123456789", "John Doe", 1000.0);BankAccount account2 = new BankAccount("987654321", "Jane Smith", 5000.0);account1.deposit(500.0); // Output: Deposited $500.0. New balance: $1500.0account2.withdraw(1000.0); // Output: Withdrew $1000.0. New balance: $4000.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;code&gt;account1&lt;/code&gt; and &lt;code&gt;account2&lt;/code&gt; are two separate objects of the &lt;code&gt;BankAccount&lt;/code&gt; class, each with its own set of attribute values (&lt;code&gt;accountNumber&lt;/code&gt;, &lt;code&gt;accountHolderName&lt;/code&gt;, and &lt;code&gt;balance&lt;/code&gt;). We can invoke the &lt;code&gt;deposit()&lt;/code&gt; and &lt;code&gt;withdraw()&lt;/code&gt; methods on these objects, and they will behave according to their respective states.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Components of an Object:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;State&lt;/strong&gt; : The state of an object is defined by its attributes or properties (e.g., &lt;code&gt;accountNumber&lt;/code&gt;, &lt;code&gt;accountHolderName&lt;/code&gt;, and &lt;code&gt;balance&lt;/code&gt; in the &lt;code&gt;BankAccount&lt;/code&gt; class).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Behavior&lt;/strong&gt; : The behavior of an object is defined by its methods (e.g., &lt;code&gt;deposit()&lt;/code&gt; and &lt;code&gt;withdraw()&lt;/code&gt; in the &lt;code&gt;BankAccount&lt;/code&gt; class).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Identity&lt;/strong&gt; : Each object has a unique identity, which is typically represented by its memory address or reference.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cTKHqCLm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1712599278042/2d950434-cc5e-4e22-bfbd-8bd2edc9fe90.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cTKHqCLm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1712599278042/2d950434-cc5e-4e22-bfbd-8bd2edc9fe90.webp" alt="" width="720" height="712"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Major Pillars of OOPs
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Abstraction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jefTVvyf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1712599469196/64ce0685-ac6c-4bb7-b6b2-fcf7c035beb4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jefTVvyf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1712599469196/64ce0685-ac6c-4bb7-b6b2-fcf7c035beb4.png" alt="" width="720" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Abstraction is the process of hiding unnecessary details and exposing only the essential features of an object. It allows you to define the interface (how an object should behave) without revealing the implementation details (how the object works internally).&lt;/p&gt;

&lt;p&gt;Abstraction is a way of representing complex real-world entities in a simplified manner by focusing on their essential features and ignoring unnecessary details.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Main { public static void main(String[] args) { Animal dog = new Dog("Buddy"); System.out.println("Name: " + dog.getName()); // Output: Name: Buddy dog.makeSound(); // Output: Woof! }}abstract class Animal { private String name; public Animal(String name) { this.name = name; } public String getName() { return name; } public abstract void makeSound();}class Dog extends Animal { public Dog(String name) { super(name); } @Override public void makeSound() { System.out.println("Woof!"); }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;Animal&lt;/code&gt; class is an abstract class that defines the common behavior of animals (e.g., having a name) but leaves the implementation of &lt;code&gt;makeSound()&lt;/code&gt; to its concrete subclasses (e.g., &lt;code&gt;Dog&lt;/code&gt;). The &lt;code&gt;Dog&lt;/code&gt; class provides the implementation for the &lt;code&gt;makeSound()&lt;/code&gt; method.&lt;/p&gt;

&lt;h2&gt;
  
  
  Encapsulation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xKDsG3SX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1712599670417/a4131492-0d34-439b-8fac-a83116b02f24.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xKDsG3SX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1712599670417/a4131492-0d34-439b-8fac-a83116b02f24.jpeg" alt="" width="622" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Encapsulation is the bundling of data (properties) and methods (behaviors) together within a single unit, known as a class. It helps in achieving data abstraction and controlling the access to the object's internal state.&lt;/p&gt;

&lt;p&gt;Encapsulation is the mechanism of binding data and the code that operates on that data into a single unit, known as a class. It restricts direct access to the object's internal state and provides controlled access through public methods.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Main { public static void main(String[] args) { BankAccount account = new BankAccount(); System.out.println("Initial balance: " + account.getBalance()); // Output: Initial balance: 0.0 account.deposit(1000.0); System.out.println("Balance after deposit: " + account.getBalance()); // Output: Balance after deposit: 1000.0 account.withdraw(500.0); System.out.println("Balance after withdrawal: " + account.getBalance()); // Output: Balance after withdrawal: 500.0 account.withdraw(600.0); // Output: Insufficient funds. }}class BankAccount { private double balance; public double getBalance() { return balance; } public void deposit(double amount) { balance += amount; } public void withdraw(double amount) { if (balance &amp;gt;= amount) balance -= amount; else System.out.println("Insufficient funds."); }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;BankAccount&lt;/code&gt; class encapsulates the &lt;code&gt;balance&lt;/code&gt; field by making it private and providing public methods (&lt;code&gt;getBalance&lt;/code&gt;, &lt;code&gt;deposit&lt;/code&gt;, and &lt;code&gt;withdraw&lt;/code&gt;) to access and modify the balance in a controlled manner.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Inheritance&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xW5yvHr9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1712599682846/978869aa-c11b-470a-a5a0-7a56c1cb063e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xW5yvHr9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1712599682846/978869aa-c11b-470a-a5a0-7a56c1cb063e.png" alt="" width="500" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inheritance is a mechanism that allows a new class (child or derived class) to be based on an existing class (parent or base class). The child class inherits properties and methods from the parent class, promoting code reuse and enabling the creation of hierarchical relationships.&lt;/p&gt;

&lt;p&gt;Inheritance is a concept in OOP that allows a new class (derived class) to inherit properties and behaviors from an existing class (base class), forming an "is-a" relationship between the classes.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Main { public static void main(String[] args) { Vehicle vehicle = new Vehicle("Toyota", "Corolla"); vehicle.start(); // Output: Vehicle started. Car car = new Car("Honda", "Civic", 4); car.start(); // Output: Vehicle started. car.openTrunk(); // Output: Trunk opened. }}class Vehicle { protected String make; protected String model; public Vehicle(String make, String model) { this.make = make; this.model = model; } public void start() { System.out.println("Vehicle started."); }}class Car extends Vehicle { private int numDoors; public Car(String make, String model, int numDoors) { super(make, model); this.numDoors = numDoors; } public void openTrunk() { System.out.println("Trunk opened."); }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;Car&lt;/code&gt; class inherits from the &lt;code&gt;Vehicle&lt;/code&gt; class. It inherits the &lt;code&gt;make&lt;/code&gt;, &lt;code&gt;model&lt;/code&gt;, and &lt;code&gt;start()&lt;/code&gt; method from the &lt;code&gt;Vehicle&lt;/code&gt; class and adds its own &lt;code&gt;numDoors&lt;/code&gt; attribute and &lt;code&gt;openTrunk()&lt;/code&gt; method.&lt;/p&gt;

&lt;h2&gt;
  
  
  Polymorphism
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9X0UsIb---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1712599698185/6939d78c-fb2f-4c39-a17f-1140e16248cc.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9X0UsIb---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1712599698185/6939d78c-fb2f-4c39-a17f-1140e16248cc.webp" alt="" width="257" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Polymorphism is a concept in OOP that allows objects of different classes to be treated as objects of a common superclass, enabling code reusability and flexibility. It can be achieved through method overriding (runtime polymorphism) or method overloading (compile-time polymorphism).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Runtime Polymorphism (Method Overriding)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Runtime polymorphism, also known as method overriding, occurs when a subclass provides its own implementation of a method that is already defined in its superclass.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Animal { public void makeSound() { System.out.println("Animal sound"); }}class Dog extends Animal { @Override public void makeSound() { System.out.println("Woof!"); }}class Cat extends Animal { @Override public void makeSound() { System.out.println("Meow!"); }}public class Main { public static void main(String[] args) { Animal animal1 = new Animal(); Animal animal2 = new Dog(); Animal animal3 = new Cat(); animal1.makeSound(); // Output: Animal sound animal2.makeSound(); // Output: Woof! animal3.makeSound(); // Output: Meow! }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;Dog&lt;/code&gt; and &lt;code&gt;Cat&lt;/code&gt; classes override the &lt;code&gt;makeSound()&lt;/code&gt; method of the &lt;code&gt;Animal&lt;/code&gt; class, exhibiting runtime polymorphism. The &lt;code&gt;makeSound()&lt;/code&gt; method behaves differently based on the actual object type (Animal, Dog, or Cat) at runtime.&lt;/p&gt;

&lt;p&gt;Rules:-&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Methods of child and parent class must have the same name.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Methods of child and parent class must have the same parameters.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;IS-A relationship is mandatory (inheritance).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;One cannot override the private methods of a parent class.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;One cannot override Final methods.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;One cannot override static methods.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Compile-time Polymorphism (Method Overloading)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Compile-time polymorphism, also known as method overloading, occurs when a class has multiple methods with the same name but different parameter lists (different number of parameters, different types of parameters, or both).&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Main { public static void main(String[] args) { Calculator calculator = new Calculator(); int sum1 = calculator.add(2, 3); // Output: 5 double sum2 = calculator.add(2.5, 3.7); // Output: 6.2 int sum3 = calculator.add(1, 2, 3); // Output: 6 }}class Calculator { public int add(int a, int b) { return a + b; } public double add(double a, double b) { return a + b; } public int add(int a, int b, int c) { return a + b + c; }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we create an instance of the &lt;code&gt;Calculator&lt;/code&gt; class and call different overloaded versions of the &lt;code&gt;add()&lt;/code&gt; method. The compiler determines the correct method to call based on the number and types of arguments provided, demonstrating compile-time polymorphism.&lt;/p&gt;

&lt;p&gt;Types:-&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;By changing number of parameters&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;By changing data type of any parameter&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;By changing sequence of parameters&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Minor Pillars of OOPs
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Typing
&lt;/h2&gt;

&lt;p&gt;In a strongly typed language like Java, every variable and expression must have a well-defined data type. The compiler enforces type safety by ensuring that operations are performed only on compatible data types. This helps catch type-related errors at compile-time, making the code more robust and reliable.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int age = 25; // Validage = "thirty"; // Compiler error: Incompatible typesString name = "John";int length = name.length(); // Valid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example, the variable &lt;code&gt;age&lt;/code&gt; is declared as an &lt;code&gt;int&lt;/code&gt;, so you cannot assign a string value like &lt;code&gt;"thirty"&lt;/code&gt; to it. The compiler will raise an error. However, you can call the &lt;code&gt;length()&lt;/code&gt; method on the &lt;code&gt;String&lt;/code&gt; object &lt;code&gt;name&lt;/code&gt; and assign the result (an &lt;code&gt;int&lt;/code&gt; value) to the &lt;code&gt;length&lt;/code&gt; variable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Persistance
&lt;/h2&gt;

&lt;p&gt;Persistence refers to the ability of an object to survive beyond the lifetime of the program or process that created it. In other words, persistent objects can be stored and retrieved from non-volatile storage, such as a file system or a database, without losing their state.&lt;/p&gt;

&lt;p&gt;Example:-&lt;/p&gt;

&lt;p&gt;Consider a &lt;code&gt;Student&lt;/code&gt; class that represents a student's information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Student { private String name; private int age; private double gpa; // Constructors, getters, and setters}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make &lt;code&gt;Student&lt;/code&gt; objects persistent, you can use serialization, which is the process of converting an object's state to a byte stream that can be saved to a file or sent over a network. Java provides built-in support for serialization through the &lt;code&gt;Serializable&lt;/code&gt; interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import java.io.*;public class PersistenceExample { public static void main(String[] args) { Student student = new Student("John", 20, 3.8); // Serialize the object to a file try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.dat"))) { oos.writeObject(student); } catch (IOException e) { e.printStackTrace(); } // Later, deserialize the object from the file Student deserializedStudent = null; try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student.dat"))) { deserializedStudent = (Student) ois.readObject(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } // The deserializedStudent object has the same state as the original student object System.out.println(deserializedStudent.getName()); // Output: John System.out.println(deserializedStudent.getAge()); // Output: 20 System.out.println(deserializedStudent.getGpa()); // Output: 3.8 }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, a &lt;code&gt;Student&lt;/code&gt; object is serialized to a file named &lt;code&gt;student.dat&lt;/code&gt;. Later, the object is deserialized from the same file, and its state is preserved, allowing you to work with the same object even after the program has terminated and restarted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concurrency
&lt;/h2&gt;

&lt;p&gt;Concurrency refers to the ability of multiple computations or threads to execute simultaneously and potentially interact with each other.&lt;/p&gt;

&lt;p&gt;In Java, you can create and manage threads to achieve concurrency. However, concurrent access to shared resources (e.g., objects or data structures) can lead to race conditions and other synchronization issues.&lt;/p&gt;

&lt;p&gt;Example:-&lt;/p&gt;

&lt;p&gt;Consider a &lt;code&gt;BankAccount&lt;/code&gt; class that represents a bank account with a balance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class BankAccount { private double balance; public BankAccount(double initialBalance) { this.balance = initialBalance; } public synchronized void deposit(double amount) { double newBalance = balance + amount; // Simulate a delay to illustrate the potential for race conditions try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } balance = newBalance; } public synchronized void withdraw(double amount) { double newBalance = balance - amount; // Simulate a delay to illustrate the potential for race conditions try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } balance = newBalance; } public double getBalance() { return balance; }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's create two threads that both try to deposit and withdraw money from the same &lt;code&gt;BankAccount&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ConcurrencyExample { public static void main(String[] args) { BankAccount account = new BankAccount(1000.0); Runnable depositTask = () -&amp;gt; { for (int i = 0; i &amp;lt; 1000; i++) { account.deposit(10.0); } }; Runnable withdrawTask = () -&amp;gt; { for (int i = 0; i &amp;lt; 1000; i++) { account.withdraw(10.0); } }; Thread depositThread = new Thread(depositTask); Thread withdrawThread = new Thread(withdrawTask); depositThread.start(); withdrawThread.start(); try { depositThread.join(); withdrawThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Final balance: " + account.getBalance()); }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, two threads (&lt;code&gt;depositThread&lt;/code&gt; and &lt;code&gt;withdrawThread&lt;/code&gt;) are created to perform 1000 deposit and withdrawal operations, respectively, on the same &lt;code&gt;BankAccount&lt;/code&gt; object. Without proper synchronization, the balance may not be accurately updated due to race conditions.&lt;/p&gt;

&lt;p&gt;To prevent race conditions, the &lt;code&gt;deposit()&lt;/code&gt; and &lt;code&gt;withdraw()&lt;/code&gt; methods in the &lt;code&gt;BankAccount&lt;/code&gt; class are marked as &lt;code&gt;synchronized&lt;/code&gt;. This ensures that only one thread can execute these methods at a time, preventing concurrent access to the &lt;code&gt;balance&lt;/code&gt; field and ensuring data consistency.&lt;/p&gt;

&lt;p&gt;When you run this program, the final balance printed should be 1000.0 (the initial balance), since the number of deposits and withdrawals is equal.&lt;/p&gt;

&lt;p&gt;These examples illustrate how Java's strong typing, persistence capabilities, and concurrency support help in building robust and reliable applications. Strong typing enforces type safety, persistence allows objects to survive beyond program execution, and concurrency enables parallel execution while providing mechanisms to handle shared resources safely.&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Throughout this blog, we've explored the fundamental concepts that underpin Object-Oriented Programming (OOP). We've delved into the building blocks of OOP, such as classes and objects, and how they enable the creation of modular, reusable, and extensible code.&lt;/p&gt;

&lt;p&gt;We've examined the principles of abstraction and encapsulation, which provide a structured approach to managing complexity and ensuring data integrity. Abstraction allows us to focus on the essential features of an object, while encapsulation protects an object's internal state from unintended modifications.&lt;/p&gt;

&lt;p&gt;Inheritance, a powerful mechanism in OOP, enables code reuse and the creation of hierarchical class relationships. By inheriting properties and methods from existing classes, we can create more specialized classes that extend and enhance the functionality of their parent classes.&lt;/p&gt;

&lt;p&gt;Polymorphism, both compile-time and runtime, adds flexibility to our code by allowing objects of different classes to be treated as objects of a common superclass. This versatility enables methods to work with a wide range of object types without needing to know their specific implementations.&lt;/p&gt;

&lt;p&gt;We've also explored the concept of strong typing, which enforces type safety and helps catch errors during compilation, leading to more robust and reliable code.&lt;/p&gt;

&lt;p&gt;Additionally, we've discussed persistence, which allows objects to persist beyond the lifetime of the program, enabling data storage and retrieval across multiple sessions.&lt;/p&gt;

&lt;p&gt;Finally, we've touched upon concurrency, a crucial aspect of modern software development, where multiple threads or processes can execute simultaneously, facilitating efficient utilization of system resources and improving application responsiveness.&lt;/p&gt;

&lt;p&gt;By mastering these OOP concepts and applying them effectively, developers can create software that is not only functional but also maintainable, scalable, and adaptable to changing requirements. OOP provides a solid foundation for building complex systems, fostering collaboration, and promoting code reuse across projects.&lt;/p&gt;

&lt;p&gt;As we continue our journey in OOP, remember to embrace best practices, such as writing clean and self-documenting code, adhering to design patterns, and leveraging the power of object-oriented principles to create efficient and elegant solutions.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Code, Cloud and Coffee: Deploying a MySQL, Spring and React application on AWS Free Tier with CI/CD through GitHub Actions</title>
      <dc:creator>Saurabh Mahajan</dc:creator>
      <pubDate>Sun, 03 Mar 2024 09:28:45 +0000</pubDate>
      <link>https://dev.to/saurabhthedev/code-cloud-and-coffee-deploying-a-mysql-spring-and-react-application-on-aws-free-tier-with-cicd-through-github-actions-2ll4</link>
      <guid>https://dev.to/saurabhthedev/code-cloud-and-coffee-deploying-a-mysql-spring-and-react-application-on-aws-free-tier-with-cicd-through-github-actions-2ll4</guid>
      <description>

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Embarking on the AWS Free Tier journey, I set out to weave a tapestry of technology by deploying a full-stack application, combining the power of MySQL, Spring, and React. This blog serves as a chronicle of my exploration into the world of cloud computing, where budget constraints met the ambition to create a robust and dynamic web application.&lt;/p&gt;

&lt;p&gt;In the realm of databases, I opted for Amazon RDS to host my MySQL database, leveraging its managed services to streamline administration tasks. On the backend, an EC2 instance powered by Spring provided the backbone for my application's logic and data processing. For the frontend, the user interface found its home on AWS Amplify, simplifying the deployment of my React application with a focus on scalability and ease of use.&lt;/p&gt;

&lt;p&gt;This journey wasn't just about deploying code; it was a navigation through the intricate landscape of AWS services, striking a balance between functionality and frugality. Join me as I unravel the threads of my experience, sharing the triumphs, challenges, and lessons learned in the pursuit of a fully functional, cost-effective full-stack application on the AWS Free Tier. Let this be a guide for those venturing into the cloud, where innovation meets the constraints of a tight budget. Welcome to the story of MySQL, Spring, and React on AWS Free Tier.&lt;/p&gt;




&lt;h3&gt;
  
  
  AWS Services used
&lt;/h3&gt;

&lt;p&gt;For the database, I chose Amazon RDS (Relational Database Service) to manage the MySQL database. RDS simplifies database management with automated backups, patching, and scalability features. The benefits include reduced operational overhead, easy scalability, and enhanced security through encryption. However, the AWS Free Tier imposes limitations on instance types and storage, which may require monitoring to avoid additional charges.&lt;/p&gt;

&lt;p&gt;On the server side, Amazon EC2 (Elastic Compute Cloud) was employed for hosting the Spring application on a docker container. EC2 provides scalable compute capacity with full control over virtual machines. This allows customization and flexibility in choosing instance types based on application needs. While the AWS Free Tier offers limited hours of t2.micro instances per month, additional charges may apply for data transfer and storage beyond the allotted limits.&lt;/p&gt;

&lt;p&gt;For the frontend, AWS Amplify was selected to streamline React application deployment. Amplify simplifies the development process with automatic deployment, hosting on a global CDN, and a user-friendly CI/CD pipeline. Its benefits include quick setup and integration with other AWS services. However, limitations in build minutes and storage on the Free Tier, along with potential charges for data transfer out of the hosting environment, should be considered when utilizing Amplify for hosting the frontend.&lt;/p&gt;




&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1zv14F7Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706990797454/2fee6cb6-abd1-4db3-b59d-f5713039e8be.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1zv14F7Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706990797454/2fee6cb6-abd1-4db3-b59d-f5713039e8be.png" alt="" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our application's architecture is a sophisticated ecosystem designed for seamless interaction between its integral parts. The backbone of our backend data storage is the MySQL database hosted on Amazon RDS, offering scalability and managed services. Meanwhile, the application's logic resides in a Spring backend, encapsulated within a Docker container on an EC2 instance. This environment is orchestrated by Docker, intricately configuring the MySQL and the Spring application to communicate flawlessly over the EC2 and RDS.&lt;/p&gt;

&lt;p&gt;The orchestration reaches its crescendo with the Continuous Integration and Continuous Deployment (CI/CD) workflow facilitated by GitHub Actions. Whenever code changes are pushed to our GitHub repository, the orchestrated symphony begins. A self-hosted runner on the EC2 instance takes center stage, fetching the latest changes and seamlessly updating our Spring backend. This ensures that our backend remains cutting-edge with every push.&lt;/p&gt;

&lt;p&gt;Simultaneously, the frontend undergoes a transformation of its own. The React frontend code, residing in our GitHub repository, is automatically deployed to AWS Amplify, a platform offering scalable and managed hosting for our frontend. This dynamic deployment process guarantees that our users experience the most up-to-date version of our application without any manual intervention.&lt;/p&gt;

&lt;p&gt;The Github Actions will utilize 3 workflows. First for Continuous Integration which will be triggered on every push to the repository, second for building the back-end app docker image and pushing it to our private dockerhub repository and the last one for continuous delivery which will begin execution when the second workflow completes execution. The continuous delivery workflow will be run on a self-hosted runner which will be running on the ec2 instance. It will pull the lastest uploaded backend app image and run it in a container.&lt;/p&gt;

&lt;p&gt;In essence, this intricate dance of technologies integrates AWS services, Docker containerization, and robust CI/CD practices into a harmonious system. Each component plays a crucial role, contributing to the continuous deployment and enhancement of our full-stack application. This carefully orchestrated architecture not only ensures a smooth and automated workflow but also lays the foundation for a scalable and efficient application ecosystem.&lt;/p&gt;




&lt;h3&gt;
  
  
  Repository Structure
&lt;/h3&gt;

&lt;p&gt;Link - &lt;a href="https://github.com/saurabhthecodewizard/opus"&gt;https://github.com/saurabhthecodewizard/opus&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xCB_-J0b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706993760577/b3cce7d5-eca4-4982-b4fe-69f68a0a74b4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xCB_-J0b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706993760577/b3cce7d5-eca4-4982-b4fe-69f68a0a74b4.png" alt="" width="268" height="127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is how the folder structure will look like for this example.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;server&lt;/code&gt; folder will have spring application code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;client&lt;/code&gt; folder will have react app code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;.github/workflows&lt;/code&gt; will have the CI/CD workflows&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  RDS Setup for Database
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to the AWS Management Console and open the Amazon RDS dashboard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the "Create database" button.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose the "Standard create" method.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the "Engine options" section, select "MySQL" as the database engine and choose the latest engine version available.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Under "Templates," select "Free Tier" to ensure the usage falls within the free tier limits.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provide a unique DB instance identifier of your choice.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set a username and password for the database credentials, as these will be needed for connecting to the database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose the "db.t4g.micro" instance type under "Free tier" to stay within the free tier limits.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For storage, select "gp2" as the storage type and select 20 GB storage since 20 GB is allocated for free tier, and uncheck the "Enable storage auto scaling" option.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the "Connectivity" section, choose "Don't connect to an EC2 compute resource" and allow public access, as the database will be accessed locally and later from the EC2 instance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the security group with EC2, RDS and AdministratorAccess.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Under the "Database authentication" section, choose "Password authentication."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the "Additional configuration" section, uncheck "Automated backups," "Encryption," and "Minor version upgrade."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It may show you estimated cost, but we should be good as long as we are on free tier.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, click on the "Create database" button to initiate the creation of the MySQL RDS instance.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Development Setup
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Develop a Spring application in the &lt;code&gt;server&lt;/code&gt; folder with a simple API fetching data from the database, using the RDS database configuration (The RDS database url can be take from endpoint from RDS instance details and the username and password should be the one you setup during the RDS instance creation).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This project uses flyway migrations for versioning the database. More information can be found here - &lt;a href="https://www.baeldung.com/database-migrations-with-flyway"&gt;https://www.baeldung.com/database-migrations-with-flyway&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# application.properties# Database configurationspring.datasource.url=${SPRING_DATASOURCE_URL}spring.datasource.username=${SPRING_DATASOURCE_USERNAME}spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}spring.jpa.show-sql=true# Flyway configurationspring.flyway.baseline-on-migrate=truespring.flyway.locations=classpath:db/migration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;I have added a &lt;code&gt;clients&lt;/code&gt; table and a single entity to it in the migration.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- V1697817392__add_opus_to_clients.sqlCREATE TABLE clients ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, create_date DATE, status TINYINT(1), -- 1 for true (active), 0 for false (inactive) website VARCHAR(255), picture_url VARCHAR(255));INSERT INTO clients (name, create_date, status, website, picture_url)VALUES ('Opus', '2023-10-20', 1, 'https://www.opus.com', 'https://www.opus.com/image.jpg');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add an endpoint for us to test the application
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Controller.java@GetMapping("/")public ClientDTO getOpus() { return clientService.getClientById(1L);}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The api should be working now which will fetch our client entity from the rds instance&lt;/p&gt;




&lt;h3&gt;
  
  
  Docker Setup - Backend
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;Dockerfile&lt;/code&gt; to &lt;code&gt;./server&lt;/code&gt; directory.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ./server/Dockerfile# Use the Maven 3.9.5 image as the build environmentFROM maven:3.9.5 AS build# Set the working directory within the container to /appWORKDIR /app# Copy the content of the current directory into the /app directory in the containerCOPY . .# Run Maven to clean and install dependenciesRUN mvn clean install# Use the OpenJDK 21 image as the base image for the final containerFROM openjdk:21# Set the working directory within the container to /appWORKDIR /app# Copy the compiled JAR file from the build stage to the /app directory in the final containerCOPY --from=build /app/target/*.jar server.jar# Expose port 8080 to allow external connectionsEXPOSE 8080# Define the default command to run the Java applicationCMD ["java", "-jar", "server.jar"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can test this by running the image locally by the following commands in the &lt;code&gt;./server&lt;/code&gt; directory&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build the server image
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build -t server .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;You can see if the image is built successfully by running &lt;code&gt;docker image ls&lt;/code&gt; or in docker desktop app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run the image in container with appropriate port mapping and appropriate environment variables in the command&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d -p 8080:8080 -e SPRING_DATASOURCE_URL=${DB_URL} -e SPRING_DATASOURCE_USERNAME=${DB_USERNAME} -e SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD} -e SPRING_MAIL_USERNAME=${SPRING_MAIL_USERNAME} -e SPRING_MAIL_PASSWORD=${SPRING_MAIL_PASSWORD} --name server-container server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the container running &lt;code&gt;docker container ls&lt;/code&gt; or in the docker desktop app.&lt;/p&gt;




&lt;h3&gt;
  
  
  Github Secrets
&lt;/h3&gt;

&lt;p&gt;Add the following github secrets which we will be using in our workflows for CI/CD as shown in the picture below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1raA5Ze6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1707042491827/844c9778-028d-42bb-b10f-c1a9c508652e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1raA5Ze6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1707042491827/844c9778-028d-42bb-b10f-c1a9c508652e.png" alt="" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Github Actions Setup
&lt;/h3&gt;

&lt;p&gt;We will be creating 3 files in the &lt;code&gt;.github/workflows&lt;/code&gt; directory.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Continuous Integration - &lt;code&gt;server-intergartion.yml&lt;/code&gt; :-&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Server Build - &lt;code&gt;server-build.yml&lt;/code&gt; :-&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Continuous Deployment - &lt;code&gt;server-deploy.yml&lt;/code&gt; :-&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  EC2 setup for back-end
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Sign in to the AWS Management Console.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open the EC2 dashboard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the "Instances" link in the left navigation pane.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click the "Launch Instances" button.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select Ubuntu Server&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Elb2s8F4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706992111058/128edecb-11ec-4a83-9caf-181e4eb56509.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Elb2s8F4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706992111058/128edecb-11ec-4a83-9caf-181e4eb56509.png" alt="" width="776" height="894"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select &lt;code&gt;t2.micro&lt;/code&gt; to stay in the free tier&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8hs_5Tv6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706992298709/54c736ed-2711-4b12-9308-4b95fd083de4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8hs_5Tv6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706992298709/54c736ed-2711-4b12-9308-4b95fd083de4.png" alt="" width="774" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a Key pair of &lt;code&gt;.pem&lt;/code&gt; type so we can login through SSH&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nPBgtzDO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706992135304/66bc21bc-a789-4b45-b3da-688d66af97ae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nPBgtzDO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706992135304/66bc21bc-a789-4b45-b3da-688d66af97ae.png" alt="" width="599" height="621"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Allow HTTP/HTTPS traffic from internet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Launch the instance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Change the inbound rules as given below&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tXindCyn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706992174841/82482db0-f4b8-47bd-87f6-e06d0bd8a15b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tXindCyn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706992174841/82482db0-f4b8-47bd-87f6-e06d0bd8a15b.png" alt="" width="800" height="125"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Github Runner setup on EC2
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to &lt;code&gt;.pem&lt;/code&gt; file&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connect to EC2 instance through SSH client&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Update the package list on your EC2 instance&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upgrade the installed packages on your EC2 instance. The &lt;code&gt;-y&lt;/code&gt; flag automatically confirms the upgrade.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create Github Runner&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run all the commands provided through the &lt;code&gt;Downlaod&lt;/code&gt; and &lt;code&gt;Configure&lt;/code&gt; section of the github runner on our EC2 instance that we previously connected through SSH client.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run the &lt;code&gt;./&lt;/code&gt;&lt;a href="http://run.sh"&gt;&lt;code&gt;run.sh&lt;/code&gt;&lt;/a&gt; script in the background using the &lt;code&gt;&amp;amp;&lt;/code&gt; operator to keep the process running in the background while freeing up the terminal.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Confirm runner connection&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install Docker on EC2 instance&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Trigger the CI/CD flow&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Push our code to the repository (This should automatically trigger the &lt;code&gt;Server Build&lt;/code&gt; workflow).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Trigger the &lt;code&gt;Server Build&lt;/code&gt; workflow manually through github actions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The successful completion of &lt;code&gt;Server Build&lt;/code&gt; workflow should automatically trigger the &lt;code&gt;Server Deploy&lt;/code&gt; workflow.&lt;br&gt;&lt;br&gt;
Note: If you have any errors in the server build or server integration, you will have to solve those locally and then trigger the CI/CD again.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Your latest server build should now be successfully deployed on the EC2 instance and you can check by triggering the api we provided.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can also check the depoloyment by calling the api through browser with appropriate url. You can get the public IP from instance details and the rest of the api url as you configured in the spring application.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Setup nginx server on EC2 for proxy
&lt;/h3&gt;

&lt;p&gt;Run the following commands on EC2 instance&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install nginx&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Get Docker container IP&lt;br&gt;&lt;br&gt;
Find the IP address of your Docker container using the following command. Replace &lt;code&gt;containerId&lt;/code&gt; with the actual ID. You can find the docker container id by running &lt;code&gt;docker container ps&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Navigate to nginx sites-available directory&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Edit the default configuration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the &lt;code&gt;location / { ... }&lt;/code&gt; block, add the following line with the appropriate IP address and port:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Restart nginx&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check the Nginx configuration for syntax errors&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nginx is now configured as a reverse proxy for your Docker container. Requests to the Nginx server will be forwarded to the Docker container. Make sure to replace the placeholder IP address and port with the actual values from step 2.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Setup HTTPS on EC2
&lt;/h3&gt;

&lt;p&gt;You can setup HTTPS on EC2 if you have custom domain. Follow this process if you do not have custom domain.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Now, install &lt;a href="https://caddyserver.com/"&gt;Caddy&lt;/a&gt; on the server. Instructions can be &lt;a href="https://caddyserver.com/docs/install"&gt;found here&lt;/a&gt;. Caddy 2 is a powerful open source web server with automatic HTTPS. Caddy simplifies your infrastructure and takes care of TLS certificate renewals.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next we'll create a &lt;code&gt;Caddyfile&lt;/code&gt; with the following contents(Insert your ec2 public ip).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Front-end setup on AWS Amplify
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a basic react app and consume the api we created in the Spring application with the ec2 URL&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Navigate to AWS Amplify on AWS management console.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;New app -&amp;gt; Host a web app&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select Github&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Authorize the repository -&amp;gt; Select your repository -&amp;gt; Select your branch&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(Optional) -&amp;gt; If the frontend application is not in the root directory, mention the correct directory of the app in the folder section&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next -&amp;gt; Next -&amp;gt; Save and Deploy&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note: You may need to make changes to the build commands or your react app based on the packages and libraries you are using&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;You can setup the backend url in environment variables in amplify if you want&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Your app is now live&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--apBLNEdH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1707047088890/d6c3a53b-7705-4a54-8396-55b22780073b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--apBLNEdH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1707047088890/d6c3a53b-7705-4a54-8396-55b22780073b.png" alt="" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In conclusion, the journey of deploying a full-stack application on AWS Free Tier was a dynamic exploration that delved into the intricacies of cloud architecture and development. The experience not only involved the successful deployment of MySQL on RDS, Spring on EC2, and React on AWS Amplify but also incorporated a streamlined CI/CD pipeline. The backend server benefited from GitHub Actions, ensuring continuous integration and deployment, while AWS Amplify seamlessly handled the CI/CD for the frontend.&lt;/p&gt;

&lt;p&gt;As I reflect on the project, it's clear that the CI/CD setup significantly contributed to the overall efficiency of development and deployment processes. GitHub Actions provided a robust solution for automating backend deployments, while AWS Amplify's integration with the frontend codebase streamlined the continuous delivery workflow. The synergy between these tools not only enhanced the development speed but also facilitated a more consistent and reliable release cycle.&lt;/p&gt;

&lt;p&gt;While navigating through the intricacies of AWS services, I gained insights into best practices for cloud development, security considerations, and the critical role of scalability. The process wasn't without its hurdles, but each challenge presented an opportunity for growth and learning.&lt;/p&gt;

&lt;p&gt;Sharing these experiences serves not only as a documentation of my journey but also as a guide for fellow developers venturing into the realm of AWS. By reflecting on the lessons learned and embracing the continuous learning ethos of cloud development, I believe the community can benefit from a shared knowledge pool that empowers developers to make informed decisions and build robust, scalable applications on AWS Free Tier.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Dockerizing a Fullstack Application crafted in MySQL, Spring Boot &amp; React with Docker Compose</title>
      <dc:creator>Saurabh Mahajan</dc:creator>
      <pubDate>Wed, 14 Feb 2024 20:10:10 +0000</pubDate>
      <link>https://dev.to/saurabhthedev/dockerizing-a-fullstack-application-crafted-in-mysql-spring-boot-react-with-docker-compose-4ac</link>
      <guid>https://dev.to/saurabhthedev/dockerizing-a-fullstack-application-crafted-in-mysql-spring-boot-react-with-docker-compose-4ac</guid>
      <description>&lt;p&gt;Welcome to the core of modern development practices, where Docker Compose orchestrates a symphony for your fullstack app. As a dynamic Fullstack Software Engineer, you grasp the pivotal role containerization plays in shaping software development.&lt;/p&gt;

&lt;p&gt;This blog is your guide to unlocking Docker Compose's potential in building a fullstack app with MySQL, Spring Boot, and React. Docker Compose acts as the linchpin, streamlining the encapsulation, and management of each component.&lt;/p&gt;

&lt;p&gt;We'll navigate setting up a development environment within Docker containers, explore the relationship between database design and Dockerized Spring Boot backends, and witness the magic of containerized React frontends. At every step, Docker Compose ensures cohesion and consistency across the development lifecycle.&lt;/p&gt;

&lt;p&gt;Whether you're refining containerization skills or demystifying Docker, this blog empowers both seasoned developers and curious enthusiasts. Join us as we unravel Docker Compose's potential, elevating your fullstack development experience.&lt;/p&gt;

&lt;p&gt;Fasten your seatbelt, and lets sail through the realms of Docker Compose-driven fullstack development. 🚢&lt;/p&gt;




&lt;h3&gt;
  
  
  What are containers and how docker comes into picture?
&lt;/h3&gt;

&lt;p&gt;Containers are lightweight, portable, and self-sufficient units that encapsulate software and its dependencies. They provide a consistent and isolated environment for running applications, allowing developers to package an application along with its dependencies, libraries, and runtime into a single container image. Containers are designed to be easily deployable across various environments, ensuring consistency from development to production.&lt;/p&gt;

&lt;p&gt;Docker is a platform that enables the creation, deployment, and management of containers. It utilizes containerization technology to package applications and their dependencies into containers. Docker provides a set of tools and a runtime environment to build, ship, and run containers efficiently.&lt;/p&gt;

&lt;p&gt;The typical Docker workflow involves creating a Dockerfile, which defines the steps to build a container image, and then using Docker commands to build, tag, and run containers based on that image. Docker Hub is a registry service provided by Docker, where container images can be stored and shared publicly or privately.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8W28cL8y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706117242052/8a50e986-4204-4b20-b6df-9c8e0aad4417.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8W28cL8y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706117242052/8a50e986-4204-4b20-b6df-9c8e0aad4417.png" alt="" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  What does docker compose do?
&lt;/h3&gt;

&lt;p&gt;Docker Compose is a tool for defining and running multi-container Docker applications. It allows you to define a multi-container environment using a simple YAML file, often named &lt;code&gt;docker-compose.yml&lt;/code&gt;, and then use a single command to start and run the entire application stack. Docker Compose is particularly useful for managing the orchestration of complex applications that require multiple services and containers to work together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MgclGIyU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706117354917/eb68b9a2-cc7c-430a-a12e-e8af5734be6e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MgclGIyU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1706117354917/eb68b9a2-cc7c-430a-a12e-e8af5734be6e.png" alt="" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are some key aspects and functionalities of Docker Compose:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Declarative Configuration:&lt;/strong&gt; Docker Compose uses a declarative YAML file to define the services, networks, and volumes required for your application. This file specifies how containers should be configured and how they should interact.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Service Definition:&lt;/strong&gt; Each containerized component of your application is defined as a service in the &lt;code&gt;docker-compose.yml&lt;/code&gt; file. Services can include information such as the base Docker image, container name, ports to expose, environment variables, and more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Networking:&lt;/strong&gt; Docker Compose automatically creates a network for your services, allowing containers within the same &lt;code&gt;docker-compose.yml&lt;/code&gt; file to communicate with each other using service names as hostnames.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Volume Sharing:&lt;/strong&gt; You can define volumes in the &lt;code&gt;docker-compose.yml&lt;/code&gt; file, enabling data persistence and sharing between containers. This is useful for scenarios where you want to persist data beyond the lifecycle of a single container.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Environment Variables:&lt;/strong&gt; Docker Compose allows you to set environment variables for each service, making it easy to configure different aspects of your application without modifying the container images.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Orchestration and Scaling:&lt;/strong&gt; Docker Compose simplifies the process of orchestrating multiple containers. You can start, stop, and scale your entire application with a single command. This makes it convenient for development, testing, and even production environments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ease of Use:&lt;/strong&gt; Docker Compose provides a user-friendly command-line interface, making it accessible to both developers and system administrators. Common operations like starting, stopping, and inspecting the status of services are straightforward.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Pre-requisites
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install Docker&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install Docker Compose if not already installed through the Docker installation above&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Java &amp;amp; Maven - The example in this blog uses Java v21 and Maven v3.9.5&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Node - The example in this blog uses node v18&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The project structure used for the example in this blog is as follows:-&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  &lt;a href="https://github.com/saurabhthecodewizard/docker-compose-demo"&gt;S&lt;/a&gt;etup a spring application with docker configuration
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a Spring project with appropriate dependencies like Spring Web, Spring Data JPA and MySQL Driver.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Insert your MySQL configuration in the application properties&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The back-end should be running at &lt;a href="http://localhost:8080"&gt;http://localhost:8080&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Setup a React application with docker configuration
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a react app and install axios to call apis&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The example in this blog uses a react project created by Vite, so we need to modify the &lt;code&gt;vite.config.ts&lt;/code&gt;to run the app on fixed port 3000 in our machine as well as in the container. Ignore this step if you are not using vite.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a basic html table with Edit and Delete button for each row and add new product form below the table&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The front-end should be running at &lt;a href="http://localhost:3000"&gt;http://localhost:3000&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Docker Compose Config
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;code&gt;Dockerfile&lt;/code&gt; in backend directory&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;code&gt;Dockerfile&lt;/code&gt; in frontend directory&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add a .dockerignore in the frontend directory to restrict node_modules directory to hold up space&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We can update the &lt;code&gt;application.properties&lt;/code&gt; in the backend to support dynamic environment variables now.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the project source directory i.e. &lt;a href="https://github.com/saurabhthecodewizard/docker-compose-demo"&gt;docker-compose-demo&lt;/a&gt; directory, we need to add a &lt;code&gt;docker-compose.yml&lt;/code&gt;. The environment variables should be appropriately used in the &lt;code&gt;docker-compose.yml&lt;/code&gt; and &lt;code&gt;application.properties&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Specify the version of the Docker Compose file formatversion: '3.8'# Define the services that will run in your applicationservices: # Configuration for the MySQL database service database: # Use the MySQL 8 Docker image image: mysql:8 environment: # Set the root password for MySQL MYSQL_ROOT_PASSWORD: password # Specify the name of the database to be created MYSQL_DATABASE: product # Specify the MySQL user and its password MYSQL_USER: product MYSQL_PASSWORD: password volumes: # Mount a volume to persist MySQL data - mysql_data:/var/lib/mysql # Configuration for the backend application server: # Build the server image using the Dockerfile in the ./server directory build: context: ./backend # Expose port 8080 on the host and map it to port 8080 in the container ports: - "8080:8080" environment: # Set the Spring DataSource URL to connect to the MySQL database service SPRING_DATASOURCE_URL: jdbc:mysql://database:3306/product # Set the username for connecting to the MySQL database SPRING_DATASOURCE_USERNAME: product # Set the password for connecting to the MySQL database SPRING_DATASOURCE_PASSWORD: password # Depend on the database service, ensuring it starts before the server depends_on: - database # Configuration for the frontend application client: # Build the client image using the Dockerfile in the ./client directory build: context: ./frontend # Expose port 3000 on the host and map it to port 5173 in the container ports: - "3000:3000" # Depend on the server service, ensuring it starts before the client depends_on: - server# Define a volume named mysql_data for persisting MySQL datavolumes: mysql_data:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Running the database, back-end and front-end containers in the docker network
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Run the following command to create images of mysql, server and client services provided in &lt;code&gt;docker-compose.yml&lt;/code&gt;, create and run the images in containers in a single network in detached mode i.e. run the containers in the background, detached from the terminal&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We can use the following command to stop and remove the containers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If we restart our containers again by &lt;code&gt;docker-compose up -d&lt;/code&gt;, we should have our inserted data from before since we provided a volume named &lt;code&gt;mysql_data&lt;/code&gt; in &lt;code&gt;docker-compose.yml&lt;/code&gt; for persisting MySQL data.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Github repository to the provided example - &lt;a href="https://github.com/saurabhthecodewizard/docker-compose-demo"&gt;Docker Compose Demo&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.docker.com/"&gt;Docker documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.docker.com/compose/"&gt;Docker Compose documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In conclusion, this blog covered the fundamental concepts of containers and how Docker facilitates their creation, deployment, and management. It delved into the role of Docker Compose as a tool for orchestrating multi-container Docker applications, emphasizing its declarative configuration, service definition, networking, volume sharing, environment variable setup, and ease of use.&lt;/p&gt;

&lt;p&gt;The blog provides streamlined steps for configuring a Spring backend and a React frontend application with Docker, along with Dockerfiles for building container images. The Docker Compose configuration file is introduced briefly, emphasizing service setups and volume usage for data persistence.&lt;/p&gt;

&lt;p&gt;The concluding section briefly guides readers on running the entire application stack with Docker Compose, underscoring the importance of volumes in persisting data.&lt;/p&gt;

&lt;p&gt;In essence, this blog serves as a quick reference for developers seeking practical insights into containerization using Docker and Docker Compose, offering a simplified guide for a multi-container application environment.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Navigating the Coding Odyssey: My 100DaysOfCode Challenge Journey</title>
      <dc:creator>Saurabh Mahajan</dc:creator>
      <pubDate>Sat, 03 Feb 2024 14:06:32 +0000</pubDate>
      <link>https://dev.to/saurabhthedev/navigating-the-coding-odyssey-my-100daysofcode-challenge-journey-3ooi</link>
      <guid>https://dev.to/saurabhthedev/navigating-the-coding-odyssey-my-100daysofcode-challenge-journey-3ooi</guid>
      <description>&lt;p&gt;Embarking on a journey of self-improvement was a path often paved with challenges, victories, and the relentless pursuit of growth. In the realm of coding, this journey took the form of the 100DaysOfCode challenge - a commitment to spend a dedicated hour each day for a hundred days, learning and honing coding skills. What began as a personal challenge soon evolved into a transformative odyssey through the vast landscape of web development.&lt;/p&gt;

&lt;p&gt;As the first lines of code were written, and the initial hurdles were overcome, I found myself immersed in a world of algorithms, languages, and the sheer joy of creation. This blog is a reflection on those 100 days, a chronicle of the lessons learned, technologies embraced, and the triumphs and tribulations that came with them.&lt;/p&gt;

&lt;p&gt;Join me as I navigated through the intricacies of different technologies, unraveling the threads of database management, front-end development, back-end development and Cloud computing, and discovering the fascinating world that lies beneath the surface of our favorite websites and applications. This was not just a coding challenge; it was a personal exploration, a commitment to growth, and a testament to the transformative power of perseverance in the face of complexity.&lt;/p&gt;

&lt;p&gt;So, let's dive into the code and explore the milestones, the discoveries, and the unexpected joys that the 100DaysOfCode challenge brought into my coding journey.&lt;/p&gt;




&lt;h3&gt;
  
  
  Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Introduction to the Challenge&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Before the Challenge&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Planning&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tools and Technologies learned&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Projects Showcase&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Learning Moments&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Building Consistency&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Community Engagement&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Issues Faced&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Benefits &amp;amp; Impact&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After the Challenge&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Resources&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Conclusion&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Introduction to the Challenge
&lt;/h3&gt;

&lt;p&gt;In the ever-evolving landscape of technology, the need for continuous learning and skill enhancement has become paramount. Recognizing this, the 100DaysOfCode challenge has emerged as a powerful and popular initiative within the coding community. The premise is simple yet transformative: dedicate at least one hour each day to coding for a consecutive 100 days.&lt;/p&gt;

&lt;p&gt;This challenge serves as a catalyst for personal and professional growth, aiming to instill discipline, consistency, and a genuine love for coding. Whether you're a seasoned developer looking to expand your skill set or a novice eager to embark on a coding journey, the 100DaysOfCode challenge provides a structured and supportive framework to achieve your coding goals.&lt;/p&gt;

&lt;p&gt;Beyond the mere act of coding, this challenge fosters a sense of community by connecting participants worldwide through social media platforms. Sharing progress, challenges, and insights with the hashtag #100DaysOfCode creates a collaborative atmosphere that encourages and motivates individuals throughout their coding odyssey.&lt;/p&gt;

&lt;p&gt;Joining the 100DaysOfCode challenge isn't just about completing a hundred days of codingit's about embracing a mindset of continuous improvement, breaking through barriers, and celebrating the small victories that collectively lead to significant progress. As countless developers have discovered, this challenge isn't just a commitment to code; it's a commitment to personal and professional development, one day at a time.&lt;/p&gt;

&lt;p&gt;For more information on the origin of the challenge:-&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.100daysofcode.com/"&gt;https://www.100daysofcode.com/&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Before the Challenge
&lt;/h3&gt;

&lt;p&gt;Before embarking on the 100DaysOfCode challenge, my coding journey was characterized by a foundation of curiosity and a desire to learn. While confident in the front-end and back-end coding aspects, there were notable gaps in my knowledge, particularly in design, database management, and certain cloud computing and data structure concepts. Though comfortable with technologies like Java, Spring, React, and SQL, I wasn't yet considered a full-stack development professional due to these knowledge gaps.&lt;/p&gt;

&lt;p&gt;My goals were aspirational, yet there was a noticeable gap between where I stood and where I envisioned myself in the ever-evolving world of technology. The prospect of handling back-end processes, understanding databases, and seamlessly integrating front-end and back-end development seemed daunting but immensely exciting.&lt;/p&gt;

&lt;p&gt;In essence, I was at the foothills of a vast mountain of knowledge, eager to ascend and explore the panoramic views that awaited me in the realm of full-stack development.&lt;/p&gt;




&lt;h3&gt;
  
  
  Planning
&lt;/h3&gt;

&lt;p&gt;Embarking on the 100DaysOfCode challenge requires thoughtful planning and a clear roadmap to navigate the coding journey successfully. Before diving into the daily coding sessions, I took the time to establish a structured plan, ensuring that the next 100 days would be both productive and rewarding.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I outlined clear and ambitious goals centered around mastering key domains in full stack development and expanding my existing knowledge. My focus was on becoming proficient in Database Management, Back-end Development, Front-end Development, and Cloud Computing. Additionally, I aimed to delve into essential concepts like Data Structures, Containerization, and Linux.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Recognizing the need for consistency, I divided my overarching goals into manageable sprints of 15 days each. Each sprint was dedicated to a specific topic or domain, ensuring a focused and immersive learning experience. This structured approach allowed me to balance depth and breadth in my coding journey.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Selecting the right tools and technologies required meticulous research, considering various parameters such as industry relevance, personal interest, and scalability. Each choice was made with the goal of acquiring practical skills and knowledge that would contribute to my full-stack development proficiency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To track progress effectively, I established milestones to be completed at the end of every second sprint. These milestones served as checkpoints for significant achievements, whether it was successfully implementing a feature, completing a comprehensive tutorial, or building a functional project. Celebrating these milestones provided motivation and a sense of accomplishment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flexibility was a key component of the plan. Recognizing that unexpected challenges could arise, I built in the flexibility to adapt the schedule and goals as needed. This adaptive approach allowed me to navigate unforeseen obstacles without derailing the overall progress.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Regular reflection on my progress became a fundamental aspect of the planning process. This allowed me to assess what was working well, identify areas for improvement, and make necessary adjustments. The ability to adapt and refine the plan ensured that it remained dynamic and responsive to evolving learning needs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In essence, the planning phase was not just about setting goals; it was about crafting a roadmap for a comprehensive and successful 100DaysOfCode challenge. As I moved through the meticulously planned sprints and milestones, each day became a purposeful step toward achieving mastery in full-stack development and beyond.&lt;/p&gt;




&lt;h3&gt;
  
  
  Tools and Technologies learned
&lt;/h3&gt;

&lt;p&gt;General concepts:-&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Basics of Data Structures: Array, LinkedList, Stack, Queues&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Leetcode problems related to String, Arrays, HashMap, Linked List &lt;strong&gt;,&lt;/strong&gt; Interval, backtracking, Stack, Sliding window &amp;amp; 2D matrix.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Algorithms: Stack, Queue, Resize Arrays, Dequeue(Randomized), Union Find, Knuth Shuffle, Convex Hull, Selection Sort, Bubble sort, Insertion sort, Shell sort, Merge sort, Quick sort, Dijkstra Threeway, Heap sort, Heapify.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reversed heap, Binary Tree, Binary Search Tree, AVL Trees, Red Black Trees.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implementation of Symbol Tables, Kadanes algorithm.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Containerization with Docker and Docker Compose.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Docker compose a full-stack application.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Database management:-&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;MySQL and Flyway Migration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Indexing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Database Principles&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Backend Development&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Technologies: Java 17/21, Springboot 3, Python&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;OpenAPI code generation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;RBAC system for user Authorization&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data structures and algorithms with Python&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Frontend Development&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;OpenAPI Integration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;React Routing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Common reusable components in React suitable for interviews&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hands-on with React libraries like Framer Motion, Three.js&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;NextJs, Tailwind CSS, Swiper.Js, and Framer Motion for the latest portfolio website&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Vite vs CRA (Create React App) concept&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Migration of a React application from CRA to Vite&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cloud Computing and DevOps&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;AWS services like IAM, EC2, S3, ELB, ALB, ECR, ECS, EKS, RDS, AWS Amplify, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deployment on various platforms like AWS EC2, S3, Netlify, Vercel, Render, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Proxy_pass with Nginx in EC2&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Setting AWS Cloudwatch logs with RDS, EC2, and ECS&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Full-stack project deployment with CI/CD&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Spring app CI/CD through Github Actions &amp;amp; deployment on EC2 instance (Free SSL with Caddy for testing) as a Docker container&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MySQL on AWS RDS with AWS Cloudwatch logs enabled &amp;amp; React app on AWS Amplify &amp;amp; Vercel&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implemented a full-stack app in containers with Docker compose&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS Database and Analytics, Compute services, ECS, Lambda, Batch, Lightsail&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cloud integrations, Cloud Monitoring, VPC and Networking, Security and Compliance &amp;amp; Machine Learning&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Projects Showcase
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Opus - A corporate management system&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Docker containers running in a single docker network with docker compose&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CI/CD of a fullstack application&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3d portfolio developer website&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Portfolio website&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data Structure and Algorithms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Python&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Learning Moments
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;CSS layout was a significant challenge, especially when working on responsive designs. On Day 68, I dedicated time to master CSS Flexbox and Grid. The "aha" moment came when I realized how these tools could simplify complex layouts and streamline the design process. This newfound knowledge immediately translated into more visually appealing and structurally sound web pages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As the complexity of my projects increased, so did moments of self-doubt. Day 82 became a turning point when I faced and conquered imposter syndrome. Connecting with the coding community, sharing my struggles, and receiving encouragement helped me realize that setbacks were a natural part of the learning journey. It was okay not to have all the answers immediately.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On Day 94, I built and deployed my first full-stack application, integrating server-side logic, databases, and client-side functionality. Simultaneously, I embraced continuous integration, setting up automated testing and deployment pipelines. This streamlined my development process, reinforcing the importance of code quality. Witnessing my code undergo automated testing and deployment with each push to the repository was a gratifying and efficient improvement, marking a significant step in enhancing the efficiency and reliability of my coding projects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As the 100DaysOfCode challenge concluded, the final learning moment was a reflective one. Looking back at the journey, I celebrated the accumulation of knowledge, the growth in problem-solving skills, and the newfound confidence in tackling diverse coding tasks. The challenge may have ended, but the learning journey continues.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Building Consistency
&lt;/h3&gt;

&lt;p&gt;Consistency was the cornerstone of my 100DaysOfCode journey. Tracking and maintaining a consistent coding schedule not only kept me on course but also played a crucial role in my overall progress.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Daily time commitment - I dedicated an average of at least 3-4 hours per day to coding, ensuring a balance between depth of learning and sustainable progress. On weekends, I adjusted my schedule, dedicating longer blocks of time (18-20 hours) to accommodate more complex projects and deep dives into challenging topics.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Progress tracking - Each day, I maintained a coding journal, documenting the topics covered, challenges faced, and solutions discovered. This practice not only served as a reflection tool but also helped identify patterns in my learning journey. I organized my code repositories on platforms like GitHub, creating a visual timeline of my projects. This allowed me to track the evolution of my coding skills and revisit past projects for reinforcement.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Learning resources - To maintain variety and avoid monotony, I diversified my learning resources. This included online courses, documentation, coding challenges, and community forums. Leveraging different formats kept the learning experience engaging and dynamic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Community engagement - Regular updates on social media platforms, using the #100DaysOfCode hashtag, allowed me to connect with the coding community. Sharing achievements, challenges, and insights fostered a sense of accountability and support.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adaptation &amp;amp; Flexibility - Recognizing that life can be unpredictable, I developed contingency plans for busy days or unexpected commitments. This ensured that even on challenging days, I could meet my minimum coding commitment. As the challenge progressed, I adapted my schedule based on peak productivity times. Identifying when I was most focused and adjusting my coding hours accordingly contributed to sustained consistency.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building consistency was not just about meeting a daily coding quota; it was a holistic approach that considered time management, resource diversity, community engagement, and adaptability. These elements collectively formed the framework for a successful and rewarding 100DaysOfCode journey.&lt;/p&gt;




&lt;h3&gt;
  
  
  Community Engagement
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Consistent daily updates on Twitter (X) using the #100DaysOfCode hashtag played a crucial role in fostering accountability, connecting with a global coding community, and documenting my coding journey, turning social media into a platform for shared learning and encouragement.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Regular GitHub contributions not only showcased my evolving skills through a visual timeline but also served as a personal portfolio, demonstrating coding proficiency and collaboration in open-source projects, thus enhancing my credibility as a developer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Utilizing Discord for real-time guidance provided immediate solutions to coding challenges, while engagement in discussions and project showcases facilitated constructive feedback and exposure to diverse coding perspectives, emphasizing the importance of collaborative learning.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sharing solutions to LeetCode challenges on both Discord and GitHub not only demonstrated problem-solving skills but also allowed me to actively participate in coding discussions, expanding my understanding of optimal strategies and enriching my problem-solving toolkit.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This multifaceted community engagement strategy, spanning Twitter (X), GitHub, Discord, and LeetCode, proved essential in transforming the 100DaysOfCode challenge into a dynamic and collaborative experience. It went beyond personal progress documentation, creating a supportive network and fostering shared enthusiasm for coding.&lt;/p&gt;




&lt;h3&gt;
  
  
  Issues Faced
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Juggling full-time work, personal commitments, and the 100DaysOfCode challenge required careful planning and adaptability to establish a consistent coding schedule.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Intense daily coding sessions, particularly during challenging project phases, led to burnout and mental fatigue. Implementing breaks, relaxation techniques, and adjusting the daily workload helped mitigate these issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Engaging with the coding community triggered impostor syndrome, especially when comparing progress with others. Recognizing this mindset and refocusing on personal goals played a crucial role in overcoming self-doubt.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Long challenges led to monotony and plateaus in motivation. Implementing variety in coding tasks, exploring new projects, and occasionally participating in coding events helped reignite enthusiasm.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;External factors, such as family emergencies or unexpected work demands, disrupted the daily coding routine. Developing contingency plans and the ability to adapt became crucial in mitigating these disruptions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The abundance of coding resources sometimes led to decision paralysis. Selecting high-quality, relevant resources became crucial to avoid information overload and maintain a focused learning path.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Benefits &amp;amp; Impact
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Consistent daily coding sessions led to quantifiable progress, including faster problem-solving, efficient coding practices, and proficiency in various languages and frameworks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GitHub contributions created a visible portfolio, showcasing growth and proficiency, demonstrating adaptability and skills in diverse areas of web development.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Active participation on Twitter (formerly known as X) and GitHub garnered recognition, evident through likes &amp;amp; retweets, providing motivation and a sense of accomplishment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Discord channels provided real-time problem resolution, with engagement from experienced developers and mentors offering insights and valuable advice.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Regular participation in LeetCode challenges honed problem-solving skills and exposed diverse coding approaches, influencing real-world problem-solving strategies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Active participation in Discord project showcases offered constructive feedback, alternative perspectives, and collaborative learning experiences, refining coding practices.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Public sharing of code snippets and project updates on social media contributed to increased confidence, with positive feedback reinforcing a sense of achievement.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The combination of daily Twitter updates, GitHub contributions, Discord engagement, and LeetCode challenges created a multifaceted learning experience, extending beyond technical skills to include community interaction, collaboration, and shared passion for coding.&lt;/p&gt;




&lt;h3&gt;
  
  
  After the Challenge
&lt;/h3&gt;

&lt;p&gt;The 100DaysOfCode challenge fueled a substantial transformation in my skill set. Originating as a software engineer with proficiency in frontend and backend development, I strategically closed knowledge gaps during the challenge.&lt;/p&gt;

&lt;p&gt;I enhanced my grasp of Data Structures, tackling Leetcode problems related to Array, LinkedList, Stack, Queues, and more. Projects evolved into industry-grade solutions, incorporating Java 17/21, Springboot 3, MySQL, Flyway Migration, OpenAPI, React, TypeScript, and Material UI. I explored RBAC system implementation, alongside frontend concepts like React Routing and reusable components.&lt;/p&gt;

&lt;p&gt;Diverse topics, including SMPT server concepts &amp;amp; setup, Leetcode String problems, advanced JavaScript concepts, and Python basics for Data Structures and Algorithms, were covered. I revisited fundamental concepts like basic Linux commands and delved into Tree data structures, JavaScript topics like Parcel, Babel, and npm, as well as OOP with C++.&lt;/p&gt;

&lt;p&gt;I showcased my skills with a cutting-edge portfolio project using NextJs, Tailwind CSS, Swiper.Js, and Framer motion. Migrating a React app from CRA to Vite and hands-on experience with AWS services (IAM, EC2, S3, ELB, ALB, ECR, ECS, EKS, RDS, AWS Amplify) marked key milestones. Deployments on AWS platforms, Netlify, Vercel, Render, and Docker/Docker Compose for containerization added to the journey.&lt;/p&gt;

&lt;p&gt;This expertise significantly elevated my role as a developer, paving the way for continued growth in the dynamic technology landscape.&lt;/p&gt;




&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://whimsical.com/dsa-in-90-days-EmPkf5utoFGRMnRqJjM6YV"&gt;DSA Roadmap in 90 Days&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://takeuforward.org/strivers-a2z-dsa-course/strivers-a2z-dsa-course-sheet-2/"&gt;DSA A2Z Sheet&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.coursera.org/learn/algorithms-part1"&gt;Algorithms - I&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://namastedev.com/learn/namaste-react"&gt;Namaste React&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.udemy.com/course/complete-python-bootcamp/"&gt;The Complete Python Bootcamp From Zero to Hero in Python&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.udemy.com/course/aws-certified-cloud-practitioner-new/"&gt;Ultimate AWS Certified Cloud Practitioner CLF-C02&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.github.com/en/actions"&gt;Github Actions&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://nextjs.org/"&gt;NextJs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.100daysofcode.com/"&gt;100 Days of Code&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;As the 100DaysOfCode challenge concludes, it leaves behind a trail of accomplishments and a profound sense of achievement. This coding odyssey, though challenging, was undeniably transformative.&lt;/p&gt;

&lt;p&gt;Reflecting on these past hundred days, I'm reminded of the power of consistency and the incremental nature of learning. Beyond the syntax and frameworks, this journey strengthened my technical skills and instilled in me a sense of discipline and continuous learning.&lt;/p&gt;

&lt;p&gt;Through the ups and downs, I've gained proficiency in various technologies and become part of a supportive coding community. The camaraderie with fellow learners added an invaluable layer to this experience, emphasizing that the journey is as important as the destination.&lt;/p&gt;

&lt;p&gt;As this chapter concludes, I look forward to the next set of challenges and opportunities for growth. The 100DaysOfCode challenge was not just a commitment to codeit was a commitment to self-discovery and a reminder that learning is a lifelong journey.&lt;/p&gt;

&lt;p&gt;To anyone considering a similar challenge, embrace the challenges, celebrate the victories, and, most importantly, enjoy the process. Happy coding!&lt;/p&gt;

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