<?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: Anuj Ashok Potdar</title>
    <description>The latest articles on DEV Community by Anuj Ashok Potdar (@anizmo).</description>
    <link>https://dev.to/anizmo</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%2F3085012%2F0959cc2b-9c36-4ddf-8f23-469fcaa9fed0.png</url>
      <title>DEV Community: Anuj Ashok Potdar</title>
      <link>https://dev.to/anizmo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anizmo"/>
    <language>en</language>
    <item>
      <title>Building Fault-Tolerant Order Processing with Paxos in a Distributed E-Commerce Store 🚀</title>
      <dc:creator>Anuj Ashok Potdar</dc:creator>
      <pubDate>Sun, 27 Apr 2025 20:49:06 +0000</pubDate>
      <link>https://dev.to/anizmo/building-fault-tolerant-order-processing-with-paxos-in-a-distributed-e-commerce-store-44a1</link>
      <guid>https://dev.to/anizmo/building-fault-tolerant-order-processing-with-paxos-in-a-distributed-e-commerce-store-44a1</guid>
      <description>&lt;p&gt;In a distributed e-commerce system, ensuring that each order is processed &lt;strong&gt;exactly once&lt;/strong&gt;—even when servers crash or messages get lost—is crucial. In this post, I’ll walk you through how the &lt;strong&gt;Paxos consensus algorithm&lt;/strong&gt; is used in my Spring Boot backend to coordinate order creation across multiple servers and guarantee fault tolerance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Paxos?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistency under failures&lt;/strong&gt;: Even if some nodes crash or the network is unreliable, Paxos ensures that a single, agreed-upon value (here, the “create order” operation) is chosen exactly once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No single point of failure&lt;/strong&gt;: Any node can propose an order, and the algorithm will still reach consensus as long as a majority of nodes are up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous &amp;amp; leaderless&lt;/strong&gt;: There’s no fixed leader; any node can initiate the consensus process.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  High-Level Flow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Proposal&lt;/strong&gt;: A client sends an order request (wrapped in a &lt;code&gt;Proposal&lt;/code&gt;) to a set of Paxos servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Promise&lt;/strong&gt;: Each server replies with a &lt;code&gt;Promise&lt;/code&gt; if it hasn’t promised a higher-numbered proposal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accept&lt;/strong&gt;: Once the proposer gathers a majority of promises, it sends an &lt;code&gt;accept&lt;/code&gt; request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learn&lt;/strong&gt;: After a majority of servers accept, they “learn” the chosen operation and execute it (create the order).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Key Interface
&lt;/h2&gt;

&lt;p&gt;All Paxos servers implement a simple interface (from &lt;code&gt;com.arm.coordinator.common.PaxosServer&amp;lt;T&amp;gt;&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PaxosServer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;Promise&lt;/span&gt; &lt;span class="nf"&gt;promise&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Proposal&lt;/span&gt; &lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Proposal&lt;/span&gt; &lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nc"&gt;Result&lt;/span&gt; &lt;span class="nf"&gt;learn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Proposal&lt;/span&gt; &lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Promise Phase
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;promise(...)&lt;/code&gt; method ensures that no server will accept proposals older than the one it has already promised. In your &lt;code&gt;PaxosOrderService&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt; &lt;span class="nf"&gt;promise&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Proposal&lt;/span&gt; &lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;serverLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Receive a promise message"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;REJECTED&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accepted&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// If we’ve already accepted something, let the proposer know&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ACCEPTED&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
          &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Proposal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accepted&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;accepted&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOperation&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Otherwise, promise not to accept lower proposals&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PROMISED&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(&lt;a href="https://github.com/anizmo/distributed-ecommerce-store/blob/main/app/src/main/java/com/arm/ecommerce/service/PaxosOrderService.java" rel="noopener noreferrer"&gt;distributed-ecommerce-store/app/src/main/java/com/arm/ecommerce/service/PaxosOrderService.java at main · anizmo/distributed-ecommerce-store · GitHub&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Here, &lt;code&gt;maxId&lt;/code&gt; tracks the highest proposal ID seen so far. If the incoming proposal is newer, we record it and either report back the already-accepted proposal or promise to accept it later.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Accept Phase
&lt;/h2&gt;

&lt;p&gt;Once a proposer has a majority of &lt;code&gt;PROMISED&lt;/code&gt; responses, it issues an &lt;code&gt;accept&lt;/code&gt; request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Proposal&lt;/span&gt; &lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;serverLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received an accept message"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Only accept if this is the latest promised proposal&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// Record that we’ve accepted this proposal&lt;/span&gt;
  &lt;span class="n"&gt;accepted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proposal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOperation&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
  &lt;span class="n"&gt;serverLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Proposal successfully accepted"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(&lt;a href="https://github.com/anizmo/distributed-ecommerce-store/blob/main/app/src/main/java/com/arm/ecommerce/service/PaxosOrderService.java" rel="noopener noreferrer"&gt;distributed-ecommerce-store/app/src/main/java/com/arm/ecommerce/service/PaxosOrderService.java at main · anizmo/distributed-ecommerce-store · GitHub&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;By checking &lt;code&gt;proposal.getId() == maxId&lt;/code&gt;, we ensure consistency: servers will only accept the proposal they most recently promised.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Learn Phase
&lt;/h2&gt;

&lt;p&gt;After a majority of servers accept, the operation is “learned” and executed exactly once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt; &lt;span class="nf"&gt;learn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Proposal&lt;/span&gt; &lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;serverLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received a learn message"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nc"&gt;OrderForm&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOperation&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getOrderForm&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// Validate products, create Order and OrderProducts atomically&lt;/span&gt;
  &lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;(...));&lt;/span&gt;
  &lt;span class="c1"&gt;// ... attach products, set status, etc.&lt;/span&gt;
  &lt;span class="nc"&gt;Result&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ResultCodeEnum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ALL_OKAY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Order created successful"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(&lt;a href="https://github.com/anizmo/distributed-ecommerce-store/blob/main/app/src/main/java/com/arm/ecommerce/service/PaxosOrderService.java" rel="noopener noreferrer"&gt;distributed-ecommerce-store/app/src/main/java/com/arm/ecommerce/service/PaxosOrderService.java at main · anizmo/distributed-ecommerce-store · GitHub&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This final step writes the order to the database only once, even if some servers already crashed after accepting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Solves Fault Tolerance
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Majority Quorum&lt;/strong&gt;: As long as a majority of servers respond, the proposal moves forward. Crashed or slow nodes can catch up later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exactly-Once Semantics&lt;/strong&gt;: By separating promise, accept, and learn phases, you avoid double-creation even if messages are retried.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recovery&lt;/strong&gt;: New or recovering servers can learn the last accepted proposal and apply it to reach the same state.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Next Steps &amp;amp; Extensions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Persistent State&lt;/strong&gt;: Store &lt;code&gt;maxId&lt;/code&gt; and &lt;code&gt;accepted&lt;/code&gt; in durable storage so servers can recover after a full restart.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leader Election&lt;/strong&gt;: Build a distinguished proposer to reduce round-trip latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batching&lt;/strong&gt;: Group multiple operations per consensus round for higher throughput.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;By weaving Paxos into your order service, you transform a vanilla Spring Boot app into a rock-solid, fault-tolerant distributed system. Feel free to explore the full code and fork it here:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://github.com/anizmo/distributed-ecommerce-store" rel="noopener noreferrer"&gt;github.com/anizmo/distributed-ecommerce-store&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy coding! ✨&lt;/p&gt;

</description>
      <category>java</category>
      <category>blockchain</category>
      <category>distributedsystems</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Spring PetClinic Goes Global: Enhancing Accessibility with Multi-Language Support</title>
      <dc:creator>Anuj Ashok Potdar</dc:creator>
      <pubDate>Sun, 27 Apr 2025 19:05:22 +0000</pubDate>
      <link>https://dev.to/anizmo/spring-petclinic-goes-global-enhancing-accessibility-with-multi-language-support-15d5</link>
      <guid>https://dev.to/anizmo/spring-petclinic-goes-global-enhancing-accessibility-with-multi-language-support-15d5</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The Spring PetClinic application is a flagship example of Spring Boot best practices, used by developers worldwide to learn enterprise patterns. Recently, my pull request (&lt;a href="https://github.com/spring-projects/spring-petclinic/commit/0c88f916db87a2a54c3cfa235d635248d400808b" rel="noopener noreferrer"&gt;#0c88f9&lt;/a&gt;) was merged, introducing a critical feature: &lt;strong&gt;full internationalization (i18n) support&lt;/strong&gt; using URL-based language switching. This update not only makes the application accessible to non-English speakers but also modernizes its architecture for scalability. Let’s explore why this matters.  &lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;The Problem: Limited Language Support&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before this change, Spring PetClinic lacked robust support for multiple languages. While it included basic message properties, it didn’t fully leverage Spring’s i18n capabilities, making it difficult to:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Switch Languages Dynamically&lt;/strong&gt;: Users couldn’t change languages via the URL, a common requirement for global applications.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintain Consistency&lt;/strong&gt;: Hardcoded text in HTML files made translations error-prone and fragmented.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale to New Languages&lt;/strong&gt;: Adding a new language required manual updates across HTML templates, violating the DRY (Don’t Repeat Yourself) principle.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a reference application like PetClinic, this was a missed opportunity to demonstrate scalable i18n practices.  &lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;The Solution: URL-Based Localization and Decoupled Text&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The commit introduces three key improvements:  &lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;1. Dynamic Language Switching via URL Parameters&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;A new &lt;code&gt;WebConfiguration&lt;/code&gt; class configures Spring’s &lt;code&gt;LocaleResolver&lt;/code&gt; to read the &lt;code&gt;lang&lt;/code&gt; parameter from URLs (e.g., &lt;code&gt;?lang=es&lt;/code&gt;). This allows users to bookmark or share language-specific links, aligning with RESTful principles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Configuration&lt;/span&gt;  
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WebConfiguration&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;WebMvcConfigurer&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
    &lt;span class="nd"&gt;@Bean&lt;/span&gt;  
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;LocaleResolver&lt;/span&gt; &lt;span class="nf"&gt;localeResolver&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
        &lt;span class="nc"&gt;SessionLocaleResolver&lt;/span&gt; &lt;span class="n"&gt;slr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SessionLocaleResolver&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;  
        &lt;span class="n"&gt;slr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDefaultLocale&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Locale&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ENGLISH&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;slr&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
    &lt;span class="o"&gt;}&lt;/span&gt;  

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;  
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;LocaleChangeInterceptor&lt;/span&gt; &lt;span class="nf"&gt;localeChangeInterceptor&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
        &lt;span class="nc"&gt;LocaleChangeInterceptor&lt;/span&gt; &lt;span class="n"&gt;lci&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LocaleChangeInterceptor&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;  
        &lt;span class="n"&gt;lci&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParamName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"lang"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lci&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  
    &lt;span class="o"&gt;}&lt;/span&gt;  

    &lt;span class="nd"&gt;@Override&lt;/span&gt;  
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;addInterceptors&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InterceptorRegistry&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
        &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addInterceptor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;localeChangeInterceptor&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;  
    &lt;span class="o"&gt;}&lt;/span&gt;  
&lt;span class="o"&gt;}&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;2. Centralized Message Properties&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;All UI text was moved to &lt;code&gt;messages.properties&lt;/code&gt; files (e.g., &lt;code&gt;messages_es.properties&lt;/code&gt;, &lt;code&gt;messages_fr.properties&lt;/code&gt;), decoupling content from presentation. This ensures translations are maintained in a single location.  &lt;/p&gt;

&lt;p&gt;Example (&lt;code&gt;messages_es.properties&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;welcome&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;¡Bienvenido a PetClinic!  &lt;/span&gt;
&lt;span class="py"&gt;owners&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Propietarios  &lt;/span&gt;
&lt;span class="py"&gt;find_owners&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Buscar Propietarios  &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;3. HTML Template Cleanup&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Hardcoded text in Thymeleaf templates was replaced with Spring’s &lt;code&gt;#{...}&lt;/code&gt; expressions, enabling dynamic resolution based on the user’s locale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Before --&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Find Owners&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;  

&lt;span class="c"&gt;&amp;lt;!-- After --&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;th:text=&lt;/span&gt;&lt;span class="s"&gt;"#{find_owners}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Find Owners&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;Architectural Impact: Why This Matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;1. Scalability for Global Audiences&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;By supporting URL-driven language switching, PetClinic now serves as a blueprint for building globally accessible applications. Developers can easily add new languages by creating a &lt;code&gt;messages_xx.properties&lt;/code&gt; file—no code changes required.  &lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2. Separation of Concerns&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Decoupling text from HTML templates follows the MVC pattern rigorously. Content editors can now manage translations without touching Java code or Thymeleaf markup.  &lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;3. Improved Maintainability&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Centralized message properties reduce duplication and make it easier to spot missing translations. For example, adding support for Japanese (&lt;code&gt;messages_ja.properties&lt;/code&gt;) becomes a trivial task.  &lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;4. Enhanced User Experience&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Users can now share language-specific URLs (e.g., &lt;code&gt;https://petclinic.com?lang=de&lt;/code&gt;), making the app more inclusive and user-friendly.  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Lessons for Developers&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Design for Global Audiences Early&lt;/strong&gt;: Baking i18n into your architecture from day one avoids costly refactors later.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leverage Framework Features&lt;/strong&gt;: Spring’s &lt;code&gt;LocaleResolver&lt;/code&gt; and Thymeleaf’s i18n integration simplify what could otherwise be a complex task.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prioritize Clean Templates&lt;/strong&gt;: Keep text out of HTML/CSS/JS—it belongs in resource files.
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Community Impact&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This commit closes issue &lt;a href="https://github.com/spring-projects/spring-petclinic/issues/1854" rel="noopener noreferrer"&gt;#1854&lt;/a&gt;, addressing a long-standing request from contributors. By merging this, PetClinic:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encourages participation from non-English speakers.
&lt;/li&gt;
&lt;li&gt;Demonstrates Spring’s i18n capabilities as a learning tool.
&lt;/li&gt;
&lt;li&gt;Sets a precedent for other open-source projects to prioritize accessibility.
&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Internationalization isn’t just about translating text—it’s about architecting applications to embrace diversity. This update ensures Spring PetClinic remains a modern, inclusive example for developers worldwide.  &lt;/p&gt;

&lt;p&gt;Check out the full commit &lt;a href="https://github.com/spring-projects/spring-petclinic/commit/0c88f916db87a2a54c3cfa235d635248d400808b" rel="noopener noreferrer"&gt;here&lt;/a&gt; and consider contributing your own translations!  &lt;/p&gt;

</description>
      <category>spring</category>
      <category>java</category>
      <category>opensource</category>
      <category>i18n</category>
    </item>
    <item>
      <title>Building a Complex Dungeon Maze Game with Java Swing: An Architectural Deep Dive 🚀</title>
      <dc:creator>Anuj Ashok Potdar</dc:creator>
      <pubDate>Sun, 27 Apr 2025 18:26:20 +0000</pubDate>
      <link>https://dev.to/anizmo/building-a-complex-dungeon-maze-game-with-java-swing-an-architectural-deep-dive-15ol</link>
      <guid>https://dev.to/anizmo/building-a-complex-dungeon-maze-game-with-java-swing-an-architectural-deep-dive-15ol</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Java Swing is often overlooked for game development — but when used with good architecture patterns like &lt;strong&gt;MVC&lt;/strong&gt; and &lt;strong&gt;TDD&lt;/strong&gt;, it can be a powerful tool.  &lt;/p&gt;

&lt;p&gt;In this post, I’ll explain how I built a modular, extensible Dungeon Maze Game using pure Java Swing and share tips for designing maintainable, testable desktop applications.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why Java Swing for a Game?
&lt;/h2&gt;

&lt;p&gt;Java Swing provides a robust and flexible UI toolkit.&lt;br&gt;&lt;br&gt;
However, it's &lt;em&gt;not&lt;/em&gt; a game engine — so to build games with Swing, you have to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handle &lt;strong&gt;game loops&lt;/strong&gt;, &lt;strong&gt;redraw cycles&lt;/strong&gt;, and &lt;strong&gt;user input&lt;/strong&gt; manually&lt;/li&gt;
&lt;li&gt;Create a &lt;strong&gt;clean separation&lt;/strong&gt; between game logic and UI rendering&lt;/li&gt;
&lt;li&gt;Solve challenges related to &lt;strong&gt;real-time responsiveness&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This made Swing an interesting challenge — and a perfect playground to apply &lt;strong&gt;software engineering principles&lt;/strong&gt; like &lt;strong&gt;MVC architecture&lt;/strong&gt; and &lt;strong&gt;Test-Driven Development&lt;/strong&gt;. Java being a language that I have been using for a while it is easy to learn and apply this framework.&lt;/p&gt;


&lt;h2&gt;
  
  
  About the Project: Java Swing Dungeon Maze Game
&lt;/h2&gt;

&lt;p&gt;The project is a &lt;strong&gt;grid-based&lt;/strong&gt; dungeon crawler where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The player navigates caves and tunnels&lt;/li&gt;
&lt;li&gt;Encounters obstacles&lt;/li&gt;
&lt;li&gt;Solves mazes generated randomly&lt;/li&gt;
&lt;li&gt;Moves using keyboard input (Arrow keys)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The game architecture is designed to be &lt;strong&gt;extensible&lt;/strong&gt;, &lt;strong&gt;testable&lt;/strong&gt;, and &lt;strong&gt;easy to modify&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can find the full code here: &lt;a href="https://github.com/anizmo/JavaSwingDungeonGame" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Key Architectural Decisions
&lt;/h2&gt;
&lt;h3&gt;
  
  
  🧩 Model-View-Controller (MVC)
&lt;/h3&gt;

&lt;p&gt;Following MVC was critical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model&lt;/strong&gt;: Game logic — player movement, maze generation, obstacles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;View&lt;/strong&gt;: The Swing &lt;code&gt;GamePanel&lt;/code&gt; — renders the maze, player, and tiles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Controller&lt;/strong&gt;: Handles keyboard input and updates the model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
Handling player movement through the &lt;code&gt;GameController&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;KeyEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;VK_UP&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;movePlayer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Direction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;NORTH&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;KeyEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;VK_DOWN&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;movePlayer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Direction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SOUTH&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;GamePanel&lt;/code&gt; listens to the controller and updates the visual state accordingly.&lt;/p&gt;

&lt;p&gt;One of the unique propositions of this application is that we are updating 2 major views, the top-down view and the first-person view. This concept can be used to create complex game elements like Heads-up-displays, Race Maps, etc.&lt;/p&gt;




&lt;h3&gt;
  
  
  🧪 Test-Driven Development (TDD)
&lt;/h3&gt;

&lt;p&gt;The project is equipped with &lt;strong&gt;JUnit 5&lt;/strong&gt; tests covering the core game logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Player movement&lt;/li&gt;
&lt;li&gt;Maze generation&lt;/li&gt;
&lt;li&gt;Tile obstacle placement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: Testing if player movement respects walls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testPlayerCannotWalkThroughWalls&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Player&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Player&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;startLocation&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Maze&lt;/span&gt; &lt;span class="n"&gt;maze&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Maze&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;assertFalse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;move&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Direction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;NORTH&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maze&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Assuming wall exists&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having tests made it easy to refactor or add new features without fear of breaking existing logic.&lt;/p&gt;




&lt;h3&gt;
  
  
  🎲 Randomized Maze Generation
&lt;/h3&gt;

&lt;p&gt;One fun challenge was making the maze &lt;strong&gt;always connected&lt;/strong&gt; but still random. I have used &lt;strong&gt;Kruskal's algorithm&lt;/strong&gt; for the generation of the maze, which ensures generating unique mazes each time, and also that  each location is reachable from every location. &lt;/p&gt;

&lt;p&gt;To make the game more interesting, it is important to have a certain distance between the finish location and the player's spawn location. This is ensured by BFS, where the player is not "randomly" spawned but rather backtracked from the finish location and placed at &lt;code&gt;n&lt;/code&gt; steps away.&lt;/p&gt;

&lt;p&gt;I encapsulated random logic inside a dedicated class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RandomGenerator&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Random&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Random&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;nextInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;bound&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;nextInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bound&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the randomness easily mockable for &lt;strong&gt;unit testing&lt;/strong&gt;!&lt;/p&gt;




&lt;h3&gt;
  
  
  🖼️ Swing Graphics Rendering
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;GamePanel&lt;/code&gt; is where the game world comes alive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;paintComponent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Graphics&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;paintComponent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;maze&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;draw&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;TILE_SIZE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;draw&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;TILE_SIZE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each game tick:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clears the panel&lt;/li&gt;
&lt;li&gt;Redraws the maze&lt;/li&gt;
&lt;li&gt;Redraws the player&lt;/li&gt;
&lt;li&gt;Redraws obstacles or items&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This manual cycle makes sure the UI stays &lt;strong&gt;responsive&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Challenges Faced 💡
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Managing real-time updates without a game engine&lt;/li&gt;
&lt;li&gt;Designing reusable components while working within Swing's event-driven model&lt;/li&gt;
&lt;li&gt;Balancing &lt;strong&gt;testability&lt;/strong&gt; with &lt;strong&gt;performance&lt;/strong&gt; (e.g., avoiding unnecessary redraws)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each challenge forced me to think like a &lt;strong&gt;software engineer&lt;/strong&gt;, not just a game developer.&lt;/p&gt;




&lt;h2&gt;
  
  
  How You Can Use This Template
&lt;/h2&gt;

&lt;p&gt;Because the project is built modularly, you can easily &lt;strong&gt;extend it&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add new enemy types&lt;/li&gt;
&lt;li&gt;Create different dungeon themes&lt;/li&gt;
&lt;li&gt;Add player inventory, health bars, and abilities&lt;/li&gt;
&lt;li&gt;Experiment with different maze generation algorithms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the &lt;a href="https://github.com/anizmo/JavaSwingDungeonGame" rel="noopener noreferrer"&gt;repo&lt;/a&gt; if you want to fork and create your own dungeon adventure!&lt;/p&gt;




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

&lt;p&gt;Building a complex, polished game without a "game engine" using Java Swing was a rewarding experience.&lt;br&gt;&lt;br&gt;
It helped me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sharpen my understanding of MVC&lt;/li&gt;
&lt;li&gt;Deepen my appreciation for TDD&lt;/li&gt;
&lt;li&gt;Create a reusable framework for future Java-based games&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're looking to solidify your Java skills, I highly recommend trying a Swing-based project yourself!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feel free to fork &lt;a href="https://github.com/anizmo/JavaSwingDungeonGame" rel="noopener noreferrer"&gt;Java Swing Dungeon Game&lt;/a&gt; and build something amazing. 🚀&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Follow me&lt;/strong&gt; if you want more deep dives into Java development, architecture patterns, and game design! 🙌&lt;/p&gt;

</description>
      <category>java</category>
      <category>gamedev</category>
      <category>opensource</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
