<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Shitanshu's Tech Blog]]></title><description><![CDATA[Shitanshu's Tech Blog]]></description><link>https://shitanshu.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6a8501ae552e968823a1653d/af3f217f-9181-4916-b2cf-77d42fb5aede.png</url><title>Shitanshu&apos;s Tech Blog</title><link>https://shitanshu.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 14 Nov 2026 20:44:41 GMT</lastBuildDate><atom:link href="https://shitanshu.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Beyond Backend Development: Exploring System Design with ShopEase]]></title><description><![CDATA[Explore system design in ShopEase: scalability,availability, CAP theorem,load balancing, caching, microservices, database scaling, and fault tolerance]]></description><link>https://shitanshu.hashnode.dev/beyond-backend-development-exploring-system-design-with-shopease</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/beyond-backend-development-exploring-system-design-with-shopease</guid><category><![CDATA[#backenddevelopment]]></category><category><![CDATA[Microservices]]></category><category><![CDATA[systemdesign]]></category><category><![CDATA[Springboot]]></category><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Sat, 14 Nov 2026 05:38:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/3786c623-5161-4780-95d2-7b7ca806b0a1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Introduction</p>
<p>Building a backend application is not just about writing APIs and connecting databases. As an application grows, important questions start appearing:</p>
<p>How can the system handle increasing traffic?</p>
<p>What happens when a service becomes unavailable?</p>
<p>How should databases and caches be used?</p>
<p>How do different services communicate?</p>
<p>How can we maintain data consistency?</p>
<p>In Module 41 — System Design, I focused on understanding these architectural decisions through the ShopEase e-commerce backend.</p>
<ol>
<li>Scalability: Handling Application Growth</li>
</ol>
<p>Scalability is the ability of a system to handle increasing workloads.</p>
<p>Two common approaches are:</p>
<p>Vertical Scaling: Increasing the resources of an existing server, such as CPU and RAM.</p>
<p>Horizontal Scaling: Adding more instances of an application to distribute incoming requests.</p>
<p>Understanding the difference helps in choosing an appropriate scaling strategy as application traffic grows.</p>
<ol start="2">
<li>Availability and Reliability</li>
</ol>
<p>Availability refers to whether a system is accessible when users need it.</p>
<p>Reliability focuses on whether the system continues to perform its intended operations correctly.</p>
<p>These are important considerations for e-commerce applications, where failures in order processing, payments, or inventory operations can affect users.</p>
<ol start="3">
<li>CAP Theorem and Data Consistency</li>
</ol>
<p>The CAP theorem describes the trade-offs between Consistency, Availability, and Partition Tolerance in distributed data systems.</p>
<p>I explored how these concepts influence architectural decisions, particularly when multiple services and databases are involved.</p>
<p>Data consistency is especially important in operations such as order placement, inventory updates, and payment processing.</p>
<ol start="4">
<li>Load Balancing and Database Scaling</li>
</ol>
<p>Load balancing distributes incoming requests across multiple application instances.</p>
<p>Database scaling focuses on handling growing data volumes and query workloads.</p>
<p>These concepts help explain how backend systems can manage increasing traffic without placing all the workload on a single application instance or database.</p>
<ol start="5">
<li>Caching Architecture</li>
</ol>
<p>Caching helps reduce repeated database queries by temporarily storing frequently accessed data.</p>
<p>In ShopEase, Redis is part of the backend architecture. Understanding caching at the system-design level helped connect cache usage with response time, database workload, and cache consistency.</p>
<ol start="6">
<li>Message Queues and Microservices Architecture</li>
</ol>
<p>Microservices divide an application into smaller services with specific responsibilities.</p>
<p>Message queues support asynchronous communication between services.</p>
<p>In ShopEase, Apache Kafka is used as part of the event-driven architecture. This provides a practical context for understanding asynchronous communication and service coordination.</p>
<ol start="7">
<li>Fault Tolerance and High Availability</li>
</ol>
<p>Fault tolerance focuses on how a system handles component failures.</p>
<p>High availability aims to minimize service interruptions.</p>
<p>I studied these concepts to understand the architectural approaches used to reduce the impact of failures in distributed applications.</p>
<p>Key Learnings</p>
<p>Module 41 helped connect backend development with broader system-design concepts:</p>
<p>Scalability: Vertical and horizontal scaling.</p>
<p>Availability and reliability.</p>
<p>CAP theorem and distributed data consistency.</p>
<p>Load balancing and database scaling.</p>
<p>Redis-based caching architecture.</p>
<p>Kafka and asynchronous messaging.</p>
<p>Microservices communication.</p>
<p>Fault tolerance and high availability.</p>
<p>Conclusion</p>
<p>System design is about understanding the decisions behind an application's architecture, not simply adding more technologies.</p>
<p>Through ShopEase, I am connecting concepts such as caching, microservices, databases, and messaging with the architectural challenges that appear as an application grows.</p>
<p>Module 41 marks an important step in understanding not just how to build backend systems, but also how to think about their architecture.</p>
<p>Project: ShopEase — Full-Stack E-Commerce Application</p>
<p>GitHub Repositories:</p>
<p>Backend: <a href="https://github.com/Shitanshu686/shopping-app-backend">https://github.com/Shitanshu686/shopping-app-backend</a></p>
<p>Frontend: <a href="https://github.com/Shitanshu686/shopping-app">https://github.com/Shitanshu686/shopping-app</a></p>
<p>#Java #SpringBoot #SystemDesign #Microservices #BackendDevelopment</p>
]]></content:encoded></item><item><title><![CDATA[Building Enterprise-Grade Distributed Tracing in a Spring Boot Event-Driven Architecture (ShopEase)]]></title><description><![CDATA[Explore Correlation ID propagation across Kafka producer and consumer threads in Spring Boot microservices using Kafka headers and MDC.]]></description><link>https://shitanshu.hashnode.dev/building-enterprise-grade-distributed-tracing-in-a-spring-boot-event-driven-architecture-shopease</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/building-enterprise-grade-distributed-tracing-in-a-spring-boot-event-driven-architecture-shopease</guid><category><![CDATA[Springboot]]></category><category><![CDATA[kafka]]></category><category><![CDATA[distributedtracing]]></category><category><![CDATA[Microservices]]></category><category><![CDATA[Java]]></category><category><![CDATA[backend]]></category><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Fri, 13 Nov 2026 07:03:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/5f003cda-70cf-48bc-bb04-219e2e8400a9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Modern distributed microservices architectures offer incredible scalability, but they introduce a fundamental observability challenge: The Black Box Problem of Asynchronous Messaging.</p>
<p>When a client makes an HTTP request to an e-commerce platform, tracing the request lifecycle via a unique Correlation ID across REST and gRPC endpoints is relatively straightforward using thread-local storage like SLF4J's MDC (Mapped Diagnostic Context). However, the moment that request triggers a state change—such as placing an order—and pushes an event into an asynchronous message broker like Apache Kafka, that execution jumps across thread boundaries.</p>
<p>The background consumer thread picking up the message from the queue has no native awareness of the incoming HTTP thread's context. Without explicit propagation, your correlation ID is lost, turning distributed log aggregation and debugging in production into a guessing game.</p>
<p>In this article, we look at how we solved this architectural hurdle in ShopEase—a production-grade Spring Boot e-commerce backend—by engineering a seamless, non-intrusive Distributed Tracing and Correlation ID Propagation Pipeline across Kafka thread boundaries.</p>
<p>The Problem: Context Loss Across Asynchronous Boundaries In ShopEase, our order orchestration relies on an event-driven choreography using Apache Kafka. The lifecycle flows roughly as follows:</p>
<p>HTTP/REST Request: A client initiates a checkout request. Our custom CorrelationIdInterceptor intercepts the incoming request header (X-Correlation-ID), generates one if missing, and injects it into the SLF4J MDC context.</p>
<p>Synchronous/Service Execution: Services talk via gRPC and REST, logging actions tied directly to this correlationId.</p>
<p>The Asynchronous Leap (OrderSagaOrchestrator): An order is placed, and an OrderPlacedEvent is published to Kafka.</p>
<p>The Break: The publishing thread finishes. A completely separate consumer thread pool picks up the record from the Kafka broker to update inventory or trigger payment processing. Because standard Kafka template sends and standard listener containers do not automatically map MDC contexts across thread handoffs, the correlationId drops to null.</p>
<p>The Solution: Bridging Kafka Producers and Consumers with MDC To solve this without cluttering our core business logic, we leveraged Kafka’s built-in Headers mechanism combined with Spring Kafka’s interception hooks. We split the solution into two symmetrical pillars: Producer Injection and Consumer Interception.</p>
<ol>
<li>Injecting the Correlation ID at the Producer Level When publishing events via KafkaTemplate, instead of relying on a bare record send, we wrap our payload inside a Kafka ProducerRecord. We read the active correlationId from the thread's MDC context and append it as a Kafka record header:</li>
</ol>
<p>Java public void publishOrderPlacedEvent(OrderPlacedEvent event) { <a href="http://log.info">log.info</a>("Publishing OrderPlacedEvent to Kafka topic '{}': {}", KafkaTopicConfig.ORDER_PLACED_TOPIC, event);</p>
<pre><code class="language-plaintext">// 1. Grab current correlation ID from MDC
String correlationId = MDC.get("correlationId");

ProducerRecord&lt;String, Object&gt; record = new ProducerRecord&lt;&gt;(
    KafkaTopicConfig.ORDER_PLACED_TOPIC, 
    String.valueOf(event.getOrderId()), 
    event
);

// 2. Inject into Kafka headers if present
if (correlationId != null &amp;&amp; !correlationId.isBlank()) {
    record.headers().add("correlationId", correlationId.getBytes(StandardCharsets.UTF_8));
}

this.kafkaTemplate.send(record);
</code></pre>
<p>} 2. Restoring Context via Consumer Record Interceptors On the consuming side, when a worker thread picks up the message from the queue, we intercept the incoming record before it reaches any @KafkaListener method. Using ConcurrentKafkaListenerContainerFactory#setRecordInterceptor, we extract the correlationId header and populate the worker thread's MDC context:</p>
<p>Java @Bean public ConcurrentKafkaListenerContainerFactory&lt;String, Object&gt; kafkaListenerContainerFactory( ConsumerFactory&lt;String, Object&gt; consumerFactory, KafkaTemplate&lt;String, Object&gt; kafkaTemplate) {</p>
<pre><code class="language-plaintext">ConcurrentKafkaListenerContainerFactory&lt;String, Object&gt; factory =
        new ConcurrentKafkaListenerContainerFactory&lt;&gt;();
factory.setConsumerFactory(consumerFactory);

factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);

// Kafka Record Interceptor for Correlation ID Propagation in MDC
factory.setRecordInterceptor(record -&gt; {
    Header header = record.headers().lastHeader("correlationId");
    if (header != null) {
        String correlationId = new String(header.value(), StandardCharsets.UTF_8);
        MDC.put("correlationId", correlationId);
    } else {
        MDC.put("correlationId", UUID.randomUUID().toString());
    }
    return record;
});

// Error handler &amp; DLQ recoverer setup...
return factory;
</code></pre>
<p>} Crucial Production Note: Because consumer threads are pooled and reused across messages, failing to clear or overwrite MDC can cause context leaks (where Message B inherits Message A's trace ID). Always ensure your consumers clear or wrap logging execution contexts cleanly.</p>
<p>Architectural Impact &amp; Benefits By implementing this pattern in ShopEase, we achieved:</p>
<p>End-to-End Observability: A single correlationId now flows seamlessly from the initial HTTP entry point, through gRPC service calls, into Kafka topics, and across downstream consumer workers.</p>
<p>Zero Business Logic Pollution: Developers writing event listeners don't need to manually pass or unpack tracing headers; logging frameworks pick up the trace context automatically.</p>
<p>Resilient Debugging: When a transaction fails and hits our Dead Letter Queue (DLQ), we can query our centralized log aggregation tool using the correlation ID and inspect the entire distributed timeline instantly.</p>
<p>Conclusion Distributed tracing isn't just an afterthought or an APM tool feature—it's an architectural requirement. By leveraging Kafka headers and Spring interceptors, we bridged the gap between synchronous web requests and asynchronous event-driven pipelines, making ShopEase truly enterprise-ready.</p>
]]></content:encoded></item><item><title><![CDATA[Engineering a High-Concurrency E-Commerce Backend: Testing, Concurrency & Resilience at Scale]]></title><description><![CDATA[Exploring concurrency testing, load testing, race conditions, and backend resilience in ShopEase using Java and Spring Boot.]]></description><link>https://shitanshu.hashnode.dev/engineering-a-high-concurrency-e-commerce-backend-testing-concurrency-resilience-at-scale</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/engineering-a-high-concurrency-e-commerce-backend-testing-concurrency-resilience-at-scale</guid><category><![CDATA[Java SpringBoot Concurrency LoadTesting StressTesting]]></category><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Thu, 12 Nov 2026 10:58:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/41e82622-bdab-45a2-b2ac-c7bdce65c152.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Introduction Modern e-commerce platforms operate under extreme constraints. During flash sales or peak traffic events, backends face massive spikes in concurrent requests, threatening data integrity, inventory accuracy, and system availability.</p>
<p>In building ShopEase (shopping-app-backend), a Spring Boot 3.x Java 17 e-commerce backend, achieving functional correctness was only half the battle. The true challenge lay in engineering a robust Testing &amp; Quality Engineering pipeline to guarantee thread safety, zero race conditions, and predictable performance under heavy load.</p>
<p>The Testing &amp; Quality Engineering Strategy To ensure zero regressions and high availability, our testing architecture covers every critical tier of the application:</p>
<p>Unit &amp; Slice Testing: Isolating controllers and services using Mockito (OrderControllerTest, ProductControllerTest) to validate HTTP statuses and JSON serialization instantly.</p>
<p>Integration Testing: Verifying cross-component flows and database interactions (OrderServiceIntegrationTest).</p>
<p>Concurrency &amp; Race Condition Testing: Simulating multi-threaded contention to prevent inventory overselling and lost updates (InventoryConcurrencyTest).</p>
<p>Load &amp; Stress Testing: Automating high-traffic simulations via custom Java concurrency harnesses to measure throughput and failure rates under pressure (LoadAndStressTest).</p>
<p>Key Technical Highlights</p>
<ol>
<li>Zero-Mock Concurrency Testing To prevent race conditions during high-demand operations, we validated thread safety using multi-threaded execution services (ExecutorService), ensuring that simultaneous updates maintain strict data consistency.</li>
<li>Lightweight Load &amp; Stress Harness Instead of relying on heavy external CLI tools, we built an integrated Java-based load testing harness using standard HTTP clients to bombard endpoints with concurrent virtual users, measuring real-time latency, success ratios, and system stability under load.</li>
</ol>
<p>Conclusion By implementing this comprehensive testing suite, we achieved 100% build success across all modules, robust thread safety for inventory management, and high test maintainability. Building resilient systems isn't just about clean code—it's about proving that your architecture holds up under real-world pressure.</p>
]]></content:encoded></item><item><title><![CDATA[Is Your E-Commerce Backend Secure? ShopEase's JWT, Token Rotation & Checkout Upgrades]]></title><description><![CDATA[JWT security, refresh token rotation, optimized cart queries, and reliable checkout transactions with Spring Boot.]]></description><link>https://shitanshu.hashnode.dev/is-your-e-commerce-backend-secure-shopease-s-jwt-token-rotation-checkout-upgrades</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/is-your-e-commerce-backend-secure-shopease-s-jwt-token-rotation-checkout-upgrades</guid><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Wed, 11 Nov 2026 02:22:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/0034e0f3-6296-4581-ad11-776ac88ed7f5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>ShopEase Backend Engineering Update | November 6–11, 2026</p>
<p>Most beginner e-commerce projects focus on implementing CRUD operations.</p>
<p>Create a product. Add it to a cart. Place an order. Process a payment.</p>
<p>But building a backend that behaves reliably under real-world conditions requires much more than making APIs work.</p>
<p>What happens when multiple users try to place orders simultaneously?</p>
<p>How do you protect sensitive endpoints?</p>
<p>How should authentication tokens be managed?</p>
<p>And what happens when inefficient database queries start affecting application performance?</p>
<p>These are some of the engineering challenges I have been addressing in my ShopEase E-Commerce Backend.</p>
<p>During November 6–11, I focused on five major improvements involving authentication, security, database performance, and transaction management.</p>
<p>Let's break them down.</p>
<ol>
<li>Refresh Token Rotation: Improving Authentication Lifecycle Date: November 6–8, 2026</li>
</ol>
<p>Authentication doesn't end once a user successfully logs in.</p>
<p>Access tokens have limited lifetimes, and applications need a mechanism to maintain user sessions without repeatedly asking users to authenticate.</p>
<p>This is where refresh tokens become useful.</p>
<p>I worked on implementing a refresh token rotation flow in ShopEase. What changed?</p>
<ul>
<li>Implemented refresh token rotation logic.</li>
<li>Added TokenRefresh request DTO.</li>
<li>Added TokenRefresh response DTO.</li>
<li>Completed endpoint wiring.</li>
<li>Finalized the implementation with a stable clean build. Why does token rotation matter? A refresh token can be used to obtain a new access token.</li>
</ul>
<p>With rotation, the application issues a replacement refresh token when the existing one is used. This helps reduce the risks associated with long-lived reusable refresh tokens.</p>
<p>The implementation also needs appropriate validation, expiration handling, and protection against token reuse.</p>
<p>The goal was to improve session lifecycle management rather than relying only on access-token authentication.</p>
<ol start="2">
<li>Cart Performance Optimization: Eliminating Inefficient Data Retrieval Date: November 8, 2026</li>
</ol>
<p>Performance problems are not always caused by slow servers or insufficient hardware.</p>
<p>Sometimes, inefficient database access is the actual bottleneck.</p>
<p>While working on ShopEase, I focused on optimizing the buildCartResponse method. The problem Cart responses require retrieving relevant data from the database.</p>
<p>Inefficient retrieval patterns can introduce unnecessary database work, especially when the application needs to build responses frequently. What I changed I optimized buildCartResponse by using direct repository queries instead of table-scan-style retrieval.</p>
<p>The objective was to make data retrieval more targeted and reduce unnecessary database operations.</p>
<p>I also finalized the refresh token endpoint wiring during this milestone.</p>
<p>Key takeaway: Performance optimization starts with understanding what your application actually queries, not simply adding more caching infrastructure.</p>
<ol start="3">
<li>Strict JWT Authentication: Protecting Sensitive Endpoints Date: November 9, 2026</li>
</ol>
<p>An e-commerce application contains operations that should never be exposed without appropriate authentication.</p>
<p>Consider these endpoints:</p>
<ul>
<li>/cart</li>
<li>/orders</li>
<li>/payments These operations involve user-specific and sensitive business data.</li>
</ul>
<p>I strengthened JWT authentication enforcement across these areas. What changed?</p>
<ul>
<li>Enforced JWT authentication for cart endpoints.</li>
<li>Enforced JWT authentication for order endpoints.</li>
<li>Enforced JWT authentication for payment endpoints.</li>
<li>Strengthened access protection for sensitive operations. Why is this important? Without appropriate authentication and authorization, an application can expose sensitive operations to unauthorized users.</li>
</ul>
<p>JWT-based authentication provides a mechanism for validating requests using signed tokens.</p>
<p>However, authentication and authorization are different:</p>
<ul>
<li>Authentication: Who is making the request?</li>
<li>Authorization: Is that user allowed to perform the requested operation? Both need to be considered when securing e-commerce APIs.</li>
</ul>
<ol start="4">
<li>Security Hardening: Going Beyond JWT Date: November 10, 2026</li>
</ol>
<p>JWT authentication is only one part of backend security.</p>
<p>Application configuration, browser access policies, and HTTP security headers also matter.</p>
<p>I strengthened the Spring Security configuration in ShopEase. Changes implemented CORS hardening</p>
<p>Improved CORS configuration to establish stricter cross-origin access policies.</p>
<p>Environment validation</p>
<p>Added startup validation for sensitive environment secrets.</p>
<p>This helps identify missing or invalid configuration before the application begins serving requests.</p>
<p>HTTP security headers</p>
<p>Integrated:</p>
<ul>
<li>HSTS (HTTP Strict Transport Security)</li>
<li>Frame Options These headers provide additional browser-side security protections. Why does this matter? Security should not depend on a single authentication mechanism.</li>
</ul>
<p>A more complete approach considers authentication, access control, secure configuration, and browser security policies together.</p>
<ol start="5">
<li>REPEATABLE_READ: Strengthening Checkout Transaction Consistency Date: November 11, 2026</li>
</ol>
<p>This was one of the most interesting database-related improvements in this development cycle.</p>
<p>Imagine multiple customers interacting with an e-commerce system while orders are being created.</p>
<p>The application needs to handle concurrent transactions without allowing inconsistent database operations.</p>
<p>This is where transaction isolation becomes important. What is transaction isolation? Transaction isolation defines how concurrent database transactions interact with each other.</p>
<p>Different isolation levels provide different guarantees regarding the visibility of changes made by other transactions.</p>
<p>Common isolation levels include:</p>
<ol>
<li>READ_UNCOMMITTED</li>
<li>READ_COMMITTED</li>
<li>REPEATABLE_READ</li>
<li>SERIALIZABLE For the ShopEase checkout flow, I configured:</li>
</ol>
<p>REPEATABLE_READ Where did I apply it? The configuration was applied to:</p>
<p>OrderService.createOrder Why REPEATABLE_READ? REPEATABLE_READ helps ensure that repeated reads of the same data within a transaction remain consistent according to the database's isolation semantics.</p>
<p>It helps address non-repeatable read anomalies and provides stronger consistency guarantees than READ_COMMITTED.</p>
<p>Its exact behavior, particularly around phantom rows and locking, depends on the database engine and query type.</p>
<p>The objective was to strengthen transaction consistency during checkout operations, especially when multiple requests interact with order-related data.</p>
<p>Important: Transaction isolation alone does not prevent every possible inventory race condition or overselling scenario. Those requirements may also need atomic stock updates, locking, or optimistic concurrency control.</p>
<p>Complete Development Timeline Here's the progression of the recent ShopEase backend improvements. Date Area Implementation November 6 Authentication Refresh token rotation and DTOs November 8 Performance Cart response optimization and endpoint wiring November 9 Security JWT enforcement across sensitive endpoints November 10 Security CORS hardening, environment validation, HSTS and Frame Options November 11 Database REPEATABLE_READ isolation for order creation What These Improvements Mean for ShopEase These changes address different but interconnected backend engineering requirements. Engineering area Improvement Authentication Refresh token rotation API security JWT enforcement Application security CORS and HTTP security headers Configuration Startup environment validation Performance Optimized cart data retrieval Data consistency REPEATABLE_READ transaction isolationThe bigger lesson is that backend development is not just about adding endpoints.</p>
<p>It is about understanding how authentication, database operations, security policies, and concurrent requests interact with each other.</p>
<p>Final Thoughts ShopEase is gradually evolving from a basic e-commerce application into a more security-conscious and performance-focused backend system.</p>
<p>This development cycle taught me to look beyond whether an API returns the expected response.</p>
<p>I also need to consider:</p>
<ul>
<li>Whether the endpoint is properly protected.</li>
<li>Whether the application handles tokens safely.</li>
<li>Whether database queries are efficient.</li>
<li>Whether concurrent transactions behave consistently.</li>
<li>Whether application configuration is secure. There is still more work ahead, but these improvements represent meaningful progress toward building a reliable backend architecture.</li>
</ul>
<p>The goal isn't just to make the application work. The goal is to understand why it works, where it can fail, and how to make it more reliable. Technologies &amp; Concepts Java | Spring Boot | Spring Security | JWT | Refresh Token Rotation | MySQL | Redis | Transaction Isolation | CORS | REST APIs Project ShopEase — E-Commerce Web Application</p>
<hr />
<h2>About the ShopEase Project</h2>
<p><strong>ShopEase</strong> is a full-stack e-commerce web application that I am developing to explore real-world backend engineering challenges and modern application architecture.</p>
<p>The application covers the core e-commerce workflow, including:</p>
<ul>
<li>Product catalog and product details.</li>
<li>User authentication and authorization.</li>
<li>Shopping cart management.</li>
<li>Order creation and management.</li>
<li>Payment integration.</li>
<li>Audit logging and activity tracking.</li>
<li>Performance optimization and database management.</li>
</ul>
<h3>Technology Stack</h3>
<table>
<thead>
<tr>
<th>Technology</th>
<th>Purpose</th>
</tr>
</thead>
<tbody><tr>
<td>Java</td>
<td>Backend programming</td>
</tr>
<tr>
<td>Spring Boot</td>
<td>Backend application framework</td>
</tr>
<tr>
<td>Spring Security &amp; JWT</td>
<td>Authentication and authorization</td>
</tr>
<tr>
<td>MySQL</td>
<td>Relational business data</td>
</tr>
<tr>
<td>MongoDB</td>
<td>Dynamic product information</td>
</tr>
<tr>
<td>Redis</td>
<td>Caching and low-latency operations</td>
</tr>
<tr>
<td>PostgreSQL</td>
<td>Audit logging</td>
</tr>
<tr>
<td>Docker</td>
<td>Containerization</td>
</tr>
</tbody></table>
<p>The project is an ongoing practical implementation of concepts related to backend development, database architecture, security, concurrency, and system design.</p>
<h3>Explore the Project</h3>
<ul>
<li><strong>Backend Repository:</strong> <a href="https://github.com/Shitanshu686/shopping-app-backend">https://github.com/Shitanshu686/shopping-app-backend</a></li>
<li><strong>Frontend Repository:</strong> <a href="https://github.com/Shitanshu686/shopping-app">https://github.com/Shitanshu686/shopping-app</a></li>
</ul>
<p><strong>ShopEase is more than an e-commerce application for me; it's a practical engineering project where I experiment with backend technologies and solve real application-level challenges.</strong></p>
]]></content:encoded></item><item><title><![CDATA[Inside My Zerocode Open-Source Contribution: Implementing a Configurable Delay Executor
I Fixed the Code, But Who's Going to Fix My Sleep Schedule? 💀]]></title><description><![CDATA[Explored API routing, Guice dependency injection, scenario execution, thread interruption handling, and automated testing while working on Issue #810.]]></description><link>https://shitanshu.hashnode.dev/inside-my-zerocode-open-source-contribution-implementing-a-configurable-delay-executor-i-fixed-the-code-but-who-s-going-to-fix-my-sleep-schedule</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/inside-my-zerocode-open-source-contribution-implementing-a-configurable-delay-executor-i-fixed-the-code-but-who-s-going-to-fix-my-sleep-schedule</guid><category><![CDATA[Java, Open Source, Backend Development, Software Engineering, GitHub, Dependency Injection, Guice, JUnit, Unit Testing, Integration Testing, Zerocode]]></category><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Sun, 08 Nov 2026 05:04:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/837ba293-948d-4098-a038-f2ac350a869e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Issue: #810 Pull Request: #814 Repository: authorjapps/zerocode Status: Open — awaiting review</p>
<p>Introduction</p>
<p>Working on an unfamiliar open-source codebase is different from building a project from scratch.</p>
<p>In your own project, you already understand the architecture, naming conventions, dependencies, and execution flow. In an established project, you first need to understand how everything connects before making a change.</p>
<p>That was my experience while working on Issue #810 in Zerocode: Configurable Delay Executor.</p>
<p>I spent approximately 6–7 hours understanding the framework, tracing its execution flow, implementing the feature, debugging integration points, writing tests, and verifying the result.</p>
<p>In this article, I will explain the problem, the implementation, the technical decisions I made, and what I learned from this contribution.</p>
<ol>
<li>Understanding the Requirement</li>
</ol>
<p>The objective was to introduce a configurable delay executor that could pause execution for a specified number of milliseconds inside a Zerocode test scenario.</p>
<p>For example, consider a scenario where we need to wait for 100 milliseconds:</p>
<p>{ "name": "wait_for_100_ms", "url": "util://DelayExecutor", "operation": "DELAY", "request": 100, "assertions": { "status": "Success", "delayMillis": 100 } }</p>
<p>The expected behavior was straightforward:</p>
<p>Accept a delay value in milliseconds.</p>
<p>Validate the supplied value.</p>
<p>Execute the requested delay.</p>
<p>Return a structured response.</p>
<p>Integrate with the existing Zerocode scenario execution pipeline.</p>
<p>However, implementing this properly required understanding several parts of the framework.</p>
<ol start="2">
<li>Understanding the Existing Execution Architecture</li>
</ol>
<p>Before implementing anything, I needed to understand how Zerocode identifies an API type and executes a scenario step.</p>
<p>The implementation involved multiple layers:</p>
<p>API URL recognition.</p>
<p>API type classification.</p>
<p>Dependency injection.</p>
<p>API service execution.</p>
<p>Multi-step scenario execution.</p>
<p>The important lesson was that a new executor should fit into the framework's existing architecture rather than bypassing it.</p>
<ol start="3">
<li>Creating the Delay Executor</li>
</ol>
<p>I introduced a dedicated DelayExecutor interface:</p>
<p>public interface DelayExecutor {</p>
<pre><code class="language-plaintext">String execute(
    String url,
    String operationName,
    String requestJson
);
</code></pre>
<p>}</p>
<p>Then I implemented DelayExecutorImpl.</p>
<p>The implementation uses Java's Thread.sleep() to execute the requested delay.</p>
<p>The core logic is:</p>
<p>try { Thread.sleep(delayMillis); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new IllegalStateException( "Delay execution was interrupted", e ); }</p>
<p>Why restore the interrupt status?</p>
<p>When Thread.sleep() throws InterruptedException, Java clears the thread's interrupted status.</p>
<p>Restoring it with:</p>
<p>Thread.currentThread().interrupt();</p>
<p>preserves the interruption signal so that higher-level code can detect that the thread was interrupted.</p>
<p>This was an important part of implementing interruption handling correctly.</p>
<ol start="4">
<li>Input Validation</li>
</ol>
<p>A delay executor should not blindly accept every input.</p>
<p>I implemented validation for:</p>
<p>Null values.</p>
<p>Empty values.</p>
<p>Invalid numeric values.</p>
<p>Negative delay values.</p>
<p>The delay is parsed as a long, allowing a wider range of millisecond values than an int.</p>
<p>Zero is accepted as a valid delay.</p>
<p>Invalid inputs are rejected with meaningful exceptions rather than silently producing unexpected behavior.</p>
<ol start="5">
<li>Introducing a Dedicated API Type</li>
</ol>
<p>One of the important integration decisions was introducing:</p>
<p>DELAY_CALL</p>
<p>I added the corresponding routing logic for:</p>
<p>util://DelayExecutor</p>
<p>The placement of this condition mattered.</p>
<p>It needed to be checked before the generic URL routing logic that handles other URL patterns containing ://.</p>
<p>Otherwise, the delay URL could be incorrectly classified as a generic Java call.</p>
<p>This was a useful lesson in understanding routing precedence inside an existing framework.</p>
<ol start="6">
<li>Integrating with Guice Dependency Injection</li>
</ol>
<p>I added the binding in ApplicationMainModule:</p>
<p>bind(DelayExecutor.class) .to(DelayExecutorImpl.class);</p>
<p>This allows Guice to provide the implementation wherever the DelayExecutor interface is injected.</p>
<p>The benefit is separation between the interface and its implementation, while keeping the framework's existing dependency injection approach consistent.</p>
<ol start="7">
<li>Connecting the API Service Executor</li>
</ol>
<p>I extended the API execution layer with a dedicated delay execution method.</p>
<p>The implementation delegates the actual work to the injected DelayExecutor.</p>
<p>This keeps the responsibility separated:</p>
<p>The scenario runner decides which operation should execute.</p>
<p>The API executor delegates the operation.</p>
<p>The delay executor performs the actual delay and creates the response.</p>
<ol start="8">
<li>Updating the Multi-Step Scenario Runner</li>
</ol>
<p>The next step was integrating DELAY_CALL into:</p>
<p>ZeroCodeMultiStepsScenarioRunnerImpl</p>
<p>I added a dedicated case for the new API type.</p>
<p>When the scenario runner encounters a delay operation, it delegates the execution through the API executor instead of treating it as a normal HTTP or Java call.</p>
<p>This completed the connection between the JSON scenario definition and the actual delay implementation.</p>
<ol start="9">
<li>Writing Tests</li>
</ol>
<p>Implementation alone was not enough. I needed to verify both the executor's behavior and its integration with the framework.</p>
<p>I added tests covering:</p>
<p>Unit testing</p>
<p>Successful delay execution and response.</p>
<p>Zero-millisecond delay.</p>
<p>Negative delay rejection.</p>
<p>Invalid numeric input rejection.</p>
<p>Null input rejection.</p>
<p>Interrupted execution and interrupt-status restoration.</p>
<p>Integration and routing testing</p>
<p>Recognition of util://DelayExecutor.</p>
<p>Case-insensitive URL recognition.</p>
<p>Scenario-level execution through the multi-step runner.</p>
<p>I also added an integration scenario fixture and documented the feature in the README.</p>
<ol start="10">
<li>Test Results</li>
</ol>
<p>I executed the targeted Maven tests using:</p>
<p>mvn -pl core<br />-Dmaven.repo.local=E:/Maven/repository<br />-Dproject.build.sourceEncoding=UTF-8<br />-Dtest=DelayExecutorImplTest,ApiTypeUtilsTest,ZeroCodeMultiStepsScenarioRunnerImplVerifyTest<br />test</p>
<p>The result was:</p>
<p>Test class</p>
<p>Tests</p>
<p>Failures</p>
<p>Errors</p>
<p>DelayExecutorImplTest</p>
<p>6</p>
<p>0</p>
<p>0</p>
<p>ApiTypeUtilsTest</p>
<p>6</p>
<p>0</p>
<p>0</p>
<p>ZeroCodeMultiStepsScenarioRunnerImplVerifyTest</p>
<p>7</p>
<p>0</p>
<p>0</p>
<p>Overall:</p>
<p>19 tests executed.</p>
<p>0 failures.</p>
<p>0 errors.</p>
<p>1 skipped.</p>
<p>Maven build successful.</p>
<p>These results refer to the targeted test execution, not the entire Zerocode test suite.</p>
<ol start="11">
<li>What This Contribution Taught Me</li>
</ol>
<p>The biggest learning was not Thread.sleep() itself. It was understanding how to introduce a feature into an established Java framework.</p>
<p>Working with an unfamiliar codebase</p>
<p>I learned to trace how an existing feature moves through different layers before deciding where new functionality belongs.</p>
<p>Dependency Injection</p>
<p>I got practical experience with Guice bindings and interface-based dependency injection.</p>
<p>Execution Flow</p>
<p>I understood how URL classification, API types, service execution, and scenario runners work together.</p>
<p>Thread Interruption</p>
<p>I learned why catching InterruptedException is not enough and why restoring the interrupt flag matters.</p>
<p>Testing at Different Levels</p>
<p>I understood the difference between testing an individual executor and testing whether the scenario runner can actually execute it.</p>
<p>Open-Source Contribution Workflow</p>
<p>I worked through the practical workflow of:</p>
<p>Understanding an issue.</p>
<p>Exploring an existing repository.</p>
<p>Implementing a feature on a separate branch.</p>
<p>Writing and running tests.</p>
<p>Committing and pushing changes.</p>
<p>Creating a pull request for maintainer review.</p>
<ol start="12">
<li>Pull Request</li>
</ol>
<p>I submitted the implementation as:</p>
<p>PR #814 — Add configurable delay executor</p>
<p>The pull request is currently open and awaiting maintainer review.</p>
<p>This means the implementation has been submitted, but it has not yet been accepted or merged into the main repository.</p>
<p>Conclusion</p>
<p>This contribution gave me practical exposure to how an established Java framework is extended.</p>
<p>I started with an issue that appeared to require a simple delay operation. But implementing it properly meant understanding routing, dependency injection, execution layers, thread interruption, testing, and documentation.</p>
<p>The most valuable part of this experience was learning to work within an existing architecture instead of simply writing code that works in isolation.</p>
<p>I plan to continue contributing to open-source projects, improving my understanding of real-world software engineering, and learning from maintainers and other contributors.</p>
<p>One issue, one implementation, multiple layers of integration, and a lot of practical learning.</p>
]]></content:encoded></item><item><title><![CDATA[MySQL Wasn't Enough! Why I Added MongoDB, Redis & PostgreSQL to ShopEase]]></title><description><![CDATA[Discover ShopEase's Polyglot Persistence approach using MySQL, MongoDB, Redis, and PostgreSQL for transactions, product data, caching, and audit logs.]]></description><link>https://shitanshu.hashnode.dev/mysql-wasn-t-enough-why-i-added-mongodb-redis-postgresql-to-shopease</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/mysql-wasn-t-enough-why-i-added-mongodb-redis-postgresql-to-shopease</guid><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Thu, 05 Nov 2026 10:02:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/0b0d655b-3c91-4d9d-8647-b94693fd449e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1>Four Databases, One E-Commerce App: Why I Didn't Put Everything in MySQL!</h1>
<p><em>Understanding Polyglot Persistence through my ShopEase e-commerce backend.</em></p>
<p>When building an e-commerce application, one of the early architectural decisions is choosing a database.</p>
<p>For a basic application, using a single relational database might be enough. But as the application grows, different types of data introduce different requirements.</p>
<p>Think about it:</p>
<ul>
<li>Orders and payments need transactional reliability.</li>
<li>Product specifications need schema flexibility.</li>
<li>Frequently accessed information needs low-latency retrieval.</li>
<li>Audit records need independent tracking and storage.</li>
</ul>
<p>Should all these workloads be handled by the same database?</p>
<p>Technically, they could be. But should they be?</p>
<p>While developing <strong>ShopEase</strong>, my e-commerce application, I explored a different architectural approach: <strong>Polyglot Persistence</strong>.</p>
<p>Instead of forcing every type of data into one database, I integrated four technologies:</p>
<ol>
<li>MySQL</li>
<li>MongoDB</li>
<li>Redis</li>
<li>PostgreSQL</li>
</ol>
<p>Each database has a specific responsibility. Let's understand the reasoning behind this architecture.</p>
<hr />
<h2>What Is Polyglot Persistence?</h2>
<p>Polyglot Persistence is an architectural approach in which an application uses multiple database technologies based on the requirements of different data types and workloads.</p>
<p>The idea is simple:</p>
<p><strong>Don't choose a database just because it's popular. Choose it because it fits the problem.</strong></p>
<p>A relational database may be excellent for transactional operations, while a document database may be more suitable for flexible data structures.</p>
<p>Similarly, an in-memory data store can handle frequently accessed information more efficiently than repeatedly querying a persistent database.</p>
<p>This is the principle I followed while designing ShopEase.</p>
<hr />
<h2>1. MySQL — Protecting the Core Business Transactions</h2>
<p>Let's start with the most important part of an e-commerce application: business transactions.</p>
<p>ShopEase manages data such as:</p>
<ul>
<li>User information</li>
<li>Orders</li>
<li>Payments</li>
<li>Cart records</li>
<li>Other relational entities</li>
</ul>
<p>These operations require reliable data management.</p>
<p>Imagine a customer placing an order. The application must maintain consistency between related records. A payment or order operation cannot be treated like an ordinary cache lookup.</p>
<p>That's why I selected MySQL as the primary relational database.</p>
<h3>Why MySQL?</h3>
<p><strong>ACID properties:</strong> MySQL supports transactional operations that help maintain data consistency and reliability.</p>
<p><strong>Relational integrity:</strong> Foreign keys and constraints help maintain relationships between entities.</p>
<p><strong>Joins:</strong> Relational queries make it easier to retrieve connected business data.</p>
<p><strong>Structured data:</strong> Users, orders, and payments naturally fit relational data models.</p>
<p>MySQL acts as the transactional backbone of ShopEase.</p>
<p>The goal was to keep critical business data in a relational system with well-defined relationships and transaction support.</p>
<hr />
<h2>2. MongoDB — Handling Products That Don't Have Identical Attributes</h2>
<p>Here's an interesting challenge with e-commerce applications.</p>
<p>Consider these two products:</p>
<p><strong>Product A: Smartphone</strong></p>
<ul>
<li>RAM</li>
<li>Internal Storage</li>
<li>Processor</li>
<li>Battery Capacity</li>
<li>Camera</li>
</ul>
<p><strong>Product B: T-shirt</strong></p>
<ul>
<li>Size</li>
<li>Color</li>
<li>Fabric</li>
<li>Fit</li>
<li>Sleeve Type</li>
</ul>
<p>Now imagine storing every possible product attribute in a single rigid relational structure.</p>
<p>As the product catalog grows, new categories introduce new specifications. Maintaining a flexible product model becomes increasingly important.</p>
<p>This is where MongoDB becomes useful.</p>
<h3>Why MongoDB?</h3>
<p>MongoDB stores data in document-oriented structures, making it suitable for product information that varies across categories.</p>
<p>Its advantages include:</p>
<ul>
<li>Flexible document structure.</li>
<li>Different attributes for different product categories.</li>
<li>Easier evolution of product specifications.</li>
<li>Convenient storage and retrieval of category-specific information.</li>
</ul>
<p>In ShopEase, MongoDB is used for dynamic product details, while the core relational data remains in MySQL.</p>
<p>The important distinction is that <strong>not every product needs to follow the same data structure</strong>.</p>
<hr />
<h2>3. Redis — Reducing Repeated Database Queries</h2>
<p>Performance is another important consideration.</p>
<p>Imagine a user repeatedly accessing the same product information or interacting with their cart.</p>
<p>If every frequently repeated operation results in a database query, unnecessary database load can accumulate.</p>
<p>To address low-latency requirements, I integrated Redis.</p>
<p>Redis is an in-memory data store that provides very fast data access.</p>
<h3>Why Redis?</h3>
<p>I use Redis for performance-oriented responsibilities such as:</p>
<ul>
<li>Caching frequently accessed information.</li>
<li>Session-related data management.</li>
<li>Cart-related operations.</li>
<li>Rate limiting.</li>
</ul>
<p>The primary benefit is reducing repeated access to persistent databases when cached data can be reused.</p>
<p>For example:</p>
<ol>
<li>A user requests frequently accessed product information.</li>
<li>The application checks Redis for cached data.</li>
<li>If valid cached data exists, it can be returned directly.</li>
<li>Otherwise, the application retrieves the data from the appropriate persistent database and may cache it for subsequent requests.</li>
</ol>
<p>This is commonly known as the <strong>Cache-Aside Pattern</strong>.</p>
<p>Redis is not replacing MySQL or MongoDB. It is supporting them by handling suitable low-latency workloads.</p>
<hr />
<h2>4. PostgreSQL — Giving Audit Logs Their Own Home</h2>
<p>A reliable application should not only manage business transactions; it should also maintain a record of important activities.</p>
<p>For example:</p>
<ul>
<li>Which user performed an operation?</li>
<li>Which entity was modified?</li>
<li>What action was performed?</li>
<li>When did the activity happen?</li>
</ul>
<p>These records are important for debugging, activity tracking, and security-related investigations.</p>
<p>For ShopEase, I introduced PostgreSQL as a dedicated audit-log database.</p>
<h3>Why PostgreSQL?</h3>
<p>The main architectural reason was separation of concerns.</p>
<p>Instead of mixing audit records with the application's primary business data, I wanted an independent store for audit-related information.</p>
<p>This provides:</p>
<ul>
<li>Dedicated storage for audit records.</li>
<li>Structured querying of activity history.</li>
<li>Separation between business data and audit data.</li>
<li>Independent management of audit-related workloads.</li>
</ul>
<p>The objective is to isolate audit data management from the core transactional data model.</p>
<p>One important distinction: using a separate database alone does not automatically make audit writes asynchronous or guarantee that business transactions will never wait for them. That also depends on how the application implements the audit-writing process.</p>
<hr />
<h2>How All Four Databases Work Together</h2>
<p>Here's the responsibility breakdown in ShopEase:</p>
<table>
<thead>
<tr>
<th>Database</th>
<th>Main responsibility</th>
<th>Why it fits</th>
</tr>
</thead>
<tbody><tr>
<td>MySQL</td>
<td>Users, orders, payments, relational data</td>
<td>ACID transactions and relational integrity</td>
</tr>
<tr>
<td>MongoDB</td>
<td>Dynamic product specifications</td>
<td>Flexible document structure</td>
</tr>
<tr>
<td>Redis</td>
<td>Cache, sessions, cart-related operations, rate limiting</td>
<td>Low-latency in-memory access</td>
</tr>
<tr>
<td>PostgreSQL</td>
<td>Audit logs</td>
<td>Dedicated audit data storage</td>
</tr>
</tbody></table>
<p>Each technology serves a different purpose.</p>
<p>The objective isn't to make every database communicate with every other database. The objective is to assign responsibilities clearly and make the application use the appropriate storage system for each requirement.</p>
<hr />
<h2>Was Using Four Databases Really Necessary?</h2>
<p>This is an important architectural question.</p>
<p>Using multiple databases also introduces complexity.</p>
<p>For example:</p>
<ul>
<li>More configuration and infrastructure to maintain.</li>
<li>Multiple database connections.</li>
<li>Different data models and query mechanisms.</li>
<li>Additional monitoring and debugging requirements.</li>
<li>More complicated backup and recovery planning.</li>
<li>Potential consistency challenges across systems.</li>
</ul>
<p>A single database would have been simpler to operate.</p>
<p>However, ShopEase was also an opportunity to explore how different storage technologies address different application requirements.</p>
<p>I didn't introduce four databases because more databases automatically mean better performance or scalability.</p>
<p>I introduced them to understand and implement <strong>Polyglot Persistence</strong> in a practical e-commerce application.</p>
<p>The trade-off is clear: greater flexibility and workload separation in exchange for additional operational complexity.</p>
<hr />
<h2>Final Thoughts</h2>
<p>Building ShopEase helped me understand that database selection is not just about choosing between SQL and NoSQL.</p>
<p>It's about understanding the nature of the data, the operations performed on it, and the requirements of the system.</p>
<p>MySQL handles transactional business data.</p>
<p>MongoDB handles dynamic product information.</p>
<p>Redis supports low-latency access.</p>
<p>PostgreSQL maintains a dedicated audit trail.</p>
<p><strong>Four databases, one application, and four different responsibilities.</strong></p>
<p>That's how I approached database architecture in ShopEase.</p>
<p>The biggest takeaway?</p>
<p><strong>Good system design isn't about using more technologies. It's about using the right technology for the right problem.</strong></p>
<hr />
<h3>Technologies Used</h3>
<ul>
<li>Java</li>
<li>Spring Boot</li>
<li>MySQL</li>
<li>MongoDB</li>
<li>Redis</li>
<li>PostgreSQL</li>
<li>Spring Data JPA</li>
<li>Spring Data MongoDB</li>
<li>Spring Data Redis</li>
</ul>
<h3>Project</h3>
<p><strong>ShopEase — E-Commerce Web Application</strong></p>
<p>#Java #SpringBoot #MySQL #MongoDB #PostgreSQL #Redis #PolyglotPersistence #BackendDevelopment #SystemDesign #Ecommerce #ShitanshuJha</p>
]]></content:encoded></item><item><title><![CDATA[Beyond a Single Database: Engineering ShopEase with Polyglot Persistence & Event-Driven Microservices 🚀]]></title><description><![CDATA[Explore ShopEase's Spring Boot architecture using MySQL, MongoDB, PostgreSQL, Kafka, and asynchronous audit logging.]]></description><link>https://shitanshu.hashnode.dev/beyond-a-single-database-engineering-shopease-with-polyglot-persistence-event-driven-microservices</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/beyond-a-single-database-engineering-shopease-with-polyglot-persistence-event-driven-microservices</guid><category><![CDATA[Java · Spring Boot · Microservices · Apache Kafka]]></category><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Thu, 29 Oct 2026 06:45:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/0aea312c-2e26-47bb-9aa8-b751cb0d6879.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>What happens when an e-commerce backend needs transactional consistency, flexible product data, asynchronous processing, and reliable audit logging—all at the same time?</strong></p>
<p>A single database and tightly coupled services aren't always the best fit for every workload. While working on <strong>ShopEase</strong>, my full-stack e-commerce project, I worked on a backend architecture that separates storage responsibilities and decouples downstream processing using Apache Kafka.</p>
<p>This engineering work focuses on three areas:</p>
<ul>
<li>Polyglot Persistence Architecture</li>
<li>CQRS-inspired, event-driven processing</li>
<li>An isolated asynchronous compliance and audit pipeline</li>
</ul>
<p>Let's explore the architecture and the problems these patterns are designed to address.</p>
<h2>1. Polyglot Persistence: The Right Database for the Right Job</h2>
<p>Different types of application data have different requirements. Transactional records, dynamic product attributes, and compliance logs don't necessarily benefit from the same storage model.</p>
<p>For ShopEase, I designed a hybrid storage architecture using three database technologies.</p>
<h3>MySQL 8.4 — Core Transactional Data</h3>
<p>Orders, payments, and inventory require reliable transactional operations and consistency.</p>
<p>MySQL handles these core transactional workloads, where ACID transactions are important for maintaining data integrity.</p>
<h3>MongoDB — Dynamic Product Catalog</h3>
<p>Product catalogs can contain different attributes depending on the product category. A laptop, a T-shirt, and a smartphone don't necessarily share the same fields.</p>
<p>MongoDB's BSON document model supports flexible, polymorphic product structures without forcing every product into an identical document shape.</p>
<h3>PostgreSQL — Compliance and Audit Logs</h3>
<p>Audit records have different access and retention requirements from transactional application data.</p>
<p>For this pipeline, PostgreSQL stores append-only compliance logs using JSONB, with GIN indexing to support queries over relevant JSON fields.</p>
<p><strong>The architectural principle:</strong> choose storage based on the workload, data structure, and consistency requirements—not simply because a particular database is popular.</p>
<h2>2. CQRS-Inspired Processing with Apache Kafka</h2>
<p>Checkout workflows can involve several downstream operations: payment processing, inventory updates, notifications, and audit events.</p>
<p>Coupling all of these operations directly to the checkout request can increase dependencies and make failures harder to isolate.</p>
<p>In ShopEase, I worked on an event-driven design using Apache Kafka to decouple primary checkout processing from downstream services.</p>
<p>The high-level flow is:</p>
<ol>
<li>A checkout request enters the order-processing workflow.</li>
<li>The relevant event is published to Kafka.</li>
<li>Downstream consumers process events for their respective responsibilities.</li>
<li>Payment, inventory, and notification processing can proceed independently of the synchronous request path where the workflow permits.</li>
<li>Failed event processing can be routed through retry or Dead Letter Queue (DLQ) handling.</li>
</ol>
<p>The implementation also uses manual Kafka offset acknowledgements, allowing message acknowledgement to be coordinated with processing outcomes.</p>
<p>This requires careful handling of retries and duplicate deliveries. Kafka delivery guarantees alone do not automatically make database updates or external payment operations idempotent.</p>
<h3>Where CQRS fits</h3>
<p>CQRS separates command responsibilities from query responsibilities. It is not synonymous with Kafka or microservices.</p>
<p>In ShopEase, the event-driven decoupling supports a CQRS-inspired architectural approach. The exact command/query separation depends on how individual read and write models are implemented.</p>
<h2>3. An Isolated Asynchronous Audit Pipeline</h2>
<p>Audit logging can introduce additional database writes into application workflows. If those writes share critical request-processing resources, they can contribute to contention and additional latency.</p>
<p>To address this architectural concern, I worked on an asynchronous audit ingestion pipeline using Spring's <code>@Async</code> and dedicated secondary data sources.</p>
<p>The intended processing flow is:</p>
<p><strong>Application events → Audit listener → Asynchronous processing → PostgreSQL audit storage</strong></p>
<p>This separates audit-write responsibilities from the core transaction-processing path.</p>
<p>The design aims to:</p>
<ul>
<li>Reduce coupling between core business operations and audit persistence.</li>
<li>Keep audit ingestion off the main request-processing thread where configured.</li>
<li>Store structured audit data using PostgreSQL JSONB.</li>
<li>Support JSON-field queries using appropriate GIN indexes.</li>
<li>Improve traceability and operational separation.</li>
</ul>
<p>One important engineering consideration is reliability: asynchronous execution alone does not guarantee that every audit event will be persisted. Durable event delivery, retry policies, monitoring, and failure recovery must be considered according to the compliance requirements.</p>
<h2>4. Putting the Architecture Together</h2>
<p>The main components of this ShopEase backend architecture are:</p>
<ul>
<li><strong>Spring Boot:</strong> application services and backend APIs.</li>
<li><strong>MySQL 8.4:</strong> core transactional data.</li>
<li><strong>MongoDB:</strong> flexible product catalog documents.</li>
<li><strong>PostgreSQL:</strong> compliance and audit records.</li>
<li><strong>Apache Kafka:</strong> asynchronous event-driven communication.</li>
<li><strong>Spring</strong> <code>@Async</code><strong>:</strong> asynchronous audit processing.</li>
<li><strong>JSONB and GIN indexes:</strong> structured audit data and JSON-oriented query support.</li>
</ul>
<p>Each component has a distinct responsibility, helping the architecture address different data and processing requirements.</p>
<h2>5. Key Engineering Lessons</h2>
<p>Working on these architectural patterns reinforced several important backend engineering principles:</p>
<p><strong>Database selection is a design decision.</strong> Multiple databases can be useful when workloads have genuinely different requirements, but they also introduce operational complexity.</p>
<p><strong>Asynchronous processing changes failure handling.</strong> Retries, duplicate events, offset acknowledgement, and dead-letter handling need deliberate design.</p>
<p><strong>Audit logging deserves its own reliability strategy.</strong> Separating audit ingestion can reduce coupling, but durability and recovery must be designed explicitly.</p>
<p><strong>Distributed systems require observability.</strong> Logs, metrics, traces, and consumer-lag monitoring help identify problems across service boundaries.</p>
<h2>What's Next?</h2>
<p>The next step is to strengthen this architecture through integration testing, failure-injection tests, end-to-end tracing, and reproducible performance benchmarks. These help validate not just whether individual components work, but whether the complete system behaves correctly under failures and concurrent workloads.</p>
<h2>About ShopEase</h2>
<p><strong>ShopEase</strong> is a full-stack e-commerce application developed by <strong>Shitanshu Jha</strong>, focused on Java backend engineering, database design, secure transactions, performance optimization, and scalable application architecture.</p>
<p><strong>GitHub repositories:</strong></p>
<ul>
<li><a href="https://github.com/Shitanshu686/shopping-app-backend">ShopEase Backend</a></li>
<li><a href="https://github.com/Shitanshu686/shopping-app">ShopEase Frontend</a></li>
</ul>
<p>Building ShopEase has been a practical journey in understanding the trade-offs behind modern backend systems—and turning architectural concepts into implementation work.</p>
]]></content:encoded></item><item><title><![CDATA[94% Faster Reads! How I Optimized the ShopEase Backend]]></title><description><![CDATA[Optimized ShopEase with Redis, Hibernate, HikariCP, and MySQL indexes. Cut read latency by 94% and row-scans by 95%. By Shitanshu Jha.]]></description><link>https://shitanshu.hashnode.dev/94-faster-reads-how-i-optimized-the-shopease-backend</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/94-faster-reads-how-i-optimized-the-shopease-backend</guid><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Sat, 17 Oct 2026 05:45:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/39a33d5b-9d07-4de8-a4e4-49b2f74c75f9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>ShopEase: Optimizing Backend Performance with Redis, Hibernate &amp; MySQL</h3>
<h2>Overview</h2>
<p>As part of my <strong>ShopEase e-commerce project</strong>, I worked on improving backend performance by optimizing the catalog and order data pipelines. The goal was to reduce read latency, eliminate inefficient database queries, improve connection management, and make data retrieval more efficient.</p>
<h2>Key Optimizations</h2>
<h3>1. Redis Cache-Aside</h3>
<p>Implemented a Redis Cache-Aside layer for catalog and order pipelines, along with automated cache invalidation to keep cached data consistent with updates.</p>
<p><strong>Reported result:</strong> Read latency reduced from <strong>1.88 seconds to 110 milliseconds — a 94% reduction.</strong></p>
<h3>2. Hibernate N+1 Query Optimization</h3>
<p>Resolved Hibernate N+1 query bottlenecks using <code>JOIN FETCH</code>, allowing related data to be retrieved more efficiently and reducing unnecessary database round trips.</p>
<h3>3. HikariCP Connection Pool Tuning</h3>
<p>Optimized HikariCP connection pool settings to improve database connection management and resource utilization.</p>
<h3>4. MySQL Composite B-Tree Indexes</h3>
<p>Introduced composite B-Tree indexes to improve query execution efficiency and reduce unnecessary row scans.</p>
<p><strong>Reported result:</strong> MySQL query row-scans reduced by <strong>95%</strong>.</p>
<h2>Technology Stack</h2>
<ul>
<li><p><strong>Language:</strong> Java</p>
</li>
<li><p><strong>Backend:</strong> Spring Boot, Hibernate, Spring Data JPA</p>
</li>
<li><p><strong>Database:</strong> MySQL</p>
</li>
<li><p><strong>Caching:</strong> Redis</p>
</li>
<li><p><strong>Connection Pooling:</strong> HikariCP</p>
</li>
<li><p><strong>Query Optimization:</strong> JOIN FETCH and composite B-Tree indexes</p>
</li>
</ul>
<h2>What I Learned</h2>
<p>This optimization work reinforced that backend performance depends on multiple layers working together. Caching reduces repeated database reads, efficient ORM queries help avoid unnecessary round trips, connection pooling improves resource management, and indexing makes data retrieval more efficient.</p>
<h2>Conclusion</h2>
<p>Improving ShopEase was not just about adding features; it was also about making existing functionality faster and more efficient. These optimizations strengthened my practical understanding of Java backend development, database performance tuning, caching strategies, and scalable application design.</p>
<p>Engineered a multi-tier performance optimization for catalog and order pipelines: implemented a Redis Cache-Aside layer cutting read latency by 94% (1.88s → 110ms) with automated cache invalidation, resolved Hibernate N+1 query bottlenecks via JOIN FETCH, tuned HikariCP connection pooling, and introduced composite B-Tree indexes reducing MySQL query row-scans by 95%.</p>
<p>About ShopEase ShopEase is a full-stack e-commerce application built with Java, Spring Boot, MySQL, and Redis. It focuses on secure shopping, efficient data management, and high-performance backend architecture.</p>
<p>Developed by Shitanshu Jha 💻</p>
<p><strong>Project:</strong> ShopEase — Full-Stack E-Commerce Application</p>
<p><strong>GitHub — Backend:</strong> <a href="https://github.com/Shitanshu686/shopping-app-backend">https://github.com/Shitanshu686/shopping-app-backend</a></p>
<p><strong>GitHub — Frontend:</strong> <a href="https://github.com/Shitanshu686/shopping-app">https://github.com/Shitanshu686/shopping-app</a></p>
]]></content:encoded></item><item><title><![CDATA[How I Fixed a Real Idempotency Bug in My Spring Boot E-Commerce Backend?
What Happens When an Order Succeeds but the HTTP Response Is Lost?]]></title><description><![CDATA[Learn how ShopEase handles duplicate checkout requests using response replay, SHA-256 fingerprinting, and concurrency protection in Spring Boot.]]></description><link>https://shitanshu.hashnode.dev/how-i-fixed-a-real-idempotency-bug-in-my-spring-boot-e-commerce-backend-what-happens-when-an-order-succeeds-but-the-http-response-is-lost</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/how-i-fixed-a-real-idempotency-bug-in-my-spring-boot-e-commerce-backend-what-happens-when-an-order-succeeds-but-the-http-response-is-lost</guid><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Mon, 12 Oct 2026 07:58:38 GMT</pubDate><content:encoded><![CDATA[<p>While building <strong>ShopEase</strong>, my Java Spring Boot e-commerce backend, I was working on high-concurrency handling and idempotency protection for checkout operations.</p>
<p>I initially implemented duplicate <code>Idempotency-Key</code> validation that rejected repeated requests with HTTP <code>409 Conflict</code>. The goal was to prevent duplicate checkout processing.</p>
<p>But there was an important edge case I had not fully accounted for: <strong>What happens when an order is successfully committed to the database, but the client never receives the HTTP response?</strong></p>
<p>A developer, Andrei Ovcharenko, raised this exact concern in a comment on my Dev.to article. His feedback helped me identify a gap in my original implementation.</p>
<p>Today, I addressed that issue by implementing Stripe-style response replay, SHA-256 payload fingerprinting, and protection for in-flight requests.</p>
<p>Here is the problem, why it mattered, and how I approached the fix.</p>
<h2>What Was the Original Problem?</h2>
<p>Imagine a customer purchasing a product through ShopEase.</p>
<ol>
<li><p>The client sends a checkout request with an <code>Idempotency-Key</code>.</p>
</li>
<li><p>The backend processes the checkout and creates the order.</p>
</li>
<li><p>The database transaction commits successfully, including the stock update.</p>
</li>
<li><p>A network issue prevents the client from receiving the HTTP response.</p>
</li>
<li><p>The client retries the same request using the same idempotency key.</p>
</li>
</ol>
<p>The customer does not know whether the order succeeded because the response never arrived.</p>
<p>In my original approach, a repeated key was rejected with <code>409 Conflict</code>.</p>
<p>That prevented the retry from creating another order, but it introduced a different problem: <strong>the client could receive a conflict response even though its original order had already succeeded.</strong></p>
<p>The checkout was successful on the server, but the client could not recover the original result through a retry.</p>
<h2>Why Is This More Difficult Under Multithreading?</h2>
<p>The challenge becomes more complicated when multiple checkout requests arrive concurrently.</p>
<p>Consider two requests using the same idempotency key:</p>
<ul>
<li><p>Thread A starts processing the checkout.</p>
</li>
<li><p>Thread B receives a duplicate request before Thread A finishes.</p>
</li>
<li><p>Both threads attempt to inspect or update the idempotency state.</p>
</li>
</ul>
<p>Without proper synchronization, the application may allow duplicate processing or return an inconsistent result.</p>
<p>There are two separate cases to handle:</p>
<p><strong>Case 1: The original request has completed.</strong></p>
<p>The retry should receive the previously completed result instead of creating another order.</p>
<p><strong>Case 2: The original request is still processing.</strong></p>
<p>A concurrent retry must not start the same checkout independently. The application needs to recognize the in-flight request and apply a defined duplicate-request policy.</p>
<p>This is why idempotency is not just a simple duplicate-key check. It requires careful state management and coordination between concurrent requests.</p>
<h2>How Did I Solve It?</h2>
<p>I updated the idempotency implementation in ShopEase to support completed-response replay and payload validation.</p>
<h3>1. Why Should a Completed Request Be Replayed?</h3>
<p>Instead of treating every repeated key as a conflict, the backend now replays the stored result for an identical retry when the original operation has completed.</p>
<p>The intended flow is:</p>
<ol>
<li><p>Receive the checkout request and idempotency key.</p>
</li>
<li><p>Determine whether the key already exists.</p>
</li>
<li><p>If the original request completed and the payload matches, return its stored result.</p>
</li>
<li><p>If the key is associated with a different payload, reject the request.</p>
</li>
<li><p>If the request is still in progress, prevent another execution from processing the same operation.</p>
</li>
</ol>
<p>This lets the client recover from a lost response without creating a second order.</p>
<h3>2. How Does SHA-256 Payload Fingerprinting Help?</h3>
<p>An idempotency key should not be reused for a different checkout payload.</p>
<p>For example, suppose a key was originally used to purchase Product A. A later request uses the same key but changes the product or quantity.</p>
<p>Simply checking whether the key exists is not sufficient. The backend must also determine whether the retry represents the same request.</p>
<p>I added SHA-256 payload fingerprinting to identify whether the incoming payload matches the one associated with the original request.</p>
<p>The behavior is now designed as follows:</p>
<ul>
<li><p><strong>Same key + same payload + completed request:</strong> replay the original result.</p>
</li>
<li><p><strong>Same key + different payload:</strong> reject with HTTP <code>422 Unprocessable Content</code>.</p>
</li>
<li><p><strong>Same key + request in progress:</strong> prevent duplicate execution and apply the in-flight request policy.</p>
</li>
</ul>
<p>The fingerprint must be computed from a consistent representation of the relevant request fields. Raw JSON can differ in formatting or field order while representing the same logical request, so canonicalization may be necessary.</p>
<h3>3. How Does the <code>Idempotency-Replayed</code> Header Help?</h3>
<p>When returning a previously completed result, the backend includes:</p>
<p><code>Idempotency-Replayed: true</code></p>
<p>This indicates that the response is being replayed rather than produced by a newly executed checkout.</p>
<p>The response also preserves the original <code>orderId</code>, allowing the client to identify the order created by the first request.</p>
<p>For example, if the original request created order <code>ORD-1042</code>, a successful retry should return that same order ID instead of creating a new order.</p>
<h3>4. How Did I Handle In-Flight Requests?</h3>
<p>I added protection around the <code>PROCESSING</code> state to prevent concurrent requests using the same key from independently starting the same checkout operation.</p>
<p>This is particularly important because two threads can arrive almost simultaneously. Both must not be allowed to observe an unclaimed key and start processing.</p>
<p>The claim or state transition needs to be atomic. In a multi-instance production deployment, this generally requires a shared coordination mechanism, such as a database uniqueness constraint and transactional state management, rather than relying only on an in-memory lock.</p>
<p>The goal is to ensure that only one execution owns a given idempotency key at a time.</p>
<h2>What Acceptance Test Should Prove the Fix?</h2>
<p>A useful test is the exact failure scenario raised in the Dev.to discussion.</p>
<ol>
<li><p>Submit a checkout request with a new idempotency key.</p>
</li>
<li><p>Let the backend create the order and commit the transaction.</p>
</li>
<li><p>Suppress or drop the HTTP response to simulate a network failure.</p>
</li>
<li><p>Retry the original request using the same key and payload.</p>
</li>
<li><p>Verify that the response contains the original order ID.</p>
</li>
<li><p>Verify that only one order exists and the stock was decremented only once.</p>
</li>
</ol>
<p>Additional tests should cover concurrent duplicate requests, mismatched payloads, and retries while the original request is still processing.</p>
<p>These tests distinguish genuine idempotency from merely rejecting duplicate keys.</p>
<h2>What Did I Learn From This Bug?</h2>
<p>This fix reinforced an important backend engineering lesson: preventing duplicate requests is not the same as making retries safe.</p>
<p>A reliable idempotency implementation must consider the entire lifecycle of a request, including successful commits, lost responses, concurrent retries, payload mismatches, and failures during processing.</p>
<p>It also showed me the value of developer feedback. A comment on my article highlighted a scenario that deserved a better solution, and I used that feedback to improve my implementation.</p>
<h2>Final Thoughts</h2>
<p>ShopEase is helping me explore real backend engineering challenges beyond basic CRUD operations.</p>
<p>This improvement introduced:</p>
<ul>
<li><p>Completed-response replay for identical retries</p>
</li>
<li><p>SHA-256 payload fingerprinting</p>
</li>
<li><p>HTTP <code>422</code> for mismatched payload reuse</p>
</li>
<li><p>An <code>Idempotency-Replayed</code> response header</p>
</li>
<li><p>Protection against duplicate in-flight checkout processing</p>
</li>
</ul>
<p>The objective is to make checkout retries safer, preserve the original order result, and prevent duplicate order and inventory side effects.</p>
<p><strong>Tech stack:</strong> Java · Spring Boot · Spring Data JPA · MySQL · Multithreading · SQL</p>
<p>Building reliable systems means thinking about not only the successful path, but also what happens when networks fail and requests overlap.</p>
<p>#Java #SpringBoot #BackendDevelopment #Concurrency #Ecommerce #ShopEase #ShitanshuJha</p>
]]></content:encoded></item><item><title><![CDATA[ShopEase Backend: High-Concurrency Handling and Idempotency Protection with Spring Boot]]></title><description><![CDATA[Explore how ShopEase uses atomic SQL updates, pessimistic and optimistic locking, Spring transaction proxies, and idempotency protection]]></description><link>https://shitanshu.hashnode.dev/shopease-backend-high-concurrency-handling-and-idempotency-protection-with-spring-boot</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/shopease-backend-high-concurrency-handling-and-idempotency-protection-with-spring-boot</guid><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Sat, 10 Oct 2026 05:47:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/5fe0e242-dbd0-4cd3-9dce-ecc8026ed262.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Building a reliable e-commerce backend requires more than CRUD APIs.</strong> When multiple users purchase products simultaneously, the system must maintain accurate inventory, handle concurrent database transactions, and prevent duplicate checkout operations.</p>
<p>As part of my ShopEase e-commerce backend project, I worked on improving concurrency handling, transaction management, and checkout reliability using Java, Spring Boot, Spring Data JPA, and MySQL.</p>
<h2>Why This Module Matters</h2>
<p>Imagine a product has only one item left in stock, but two customers attempt to purchase it at almost the same time.</p>
<p>Without proper concurrency control, both requests might read the same stock value and attempt to complete their purchases. This can lead to overselling and inconsistent inventory.</p>
<p>A similar issue can occur when a checkout request is submitted multiple times because of repeated clicks, network delays, or client retries.</p>
<p>This module focuses on addressing these challenges.</p>
<h2>1. Multiple Database Concurrency Strategies</h2>
<p>I implemented different approaches to handle concurrent inventory updates in <code>DatabaseConcurrencyService</code>.</p>
<h3>Atomic SQL Updates</h3>
<p>Atomic SQL updates allow the database to perform stock modifications as a single operation rather than relying on separate read-and-write steps in application code.</p>
<p>For example:</p>
<pre><code class="language-sql">UPDATE products
SET stock = stock - :qty
WHERE id = :id
  AND stock &gt;= :qty;
</code></pre>
<p>The query checks stock availability and deducts the requested quantity in one database statement.</p>
<p>The application can inspect the affected-row count to determine whether the update succeeded.</p>
<p><strong>Key benefit:</strong> Reduces the risk of race conditions during inventory deduction without requiring the application to read and modify the stock in separate steps.</p>
<h3>Pessimistic Locking</h3>
<p>Pessimistic locking is useful when a transaction needs to lock a database record while performing an operation.</p>
<p>With JPA, a repository method can use <code>LockModeType.PESSIMISTIC_WRITE</code> to request a write lock on the selected entity.</p>
<p>This helps prevent competing transactions from modifying the same locked record simultaneously.</p>
<p><strong>Key benefit:</strong> Provides controlled access to inventory records when conflicting updates are likely.</p>
<h3>Optimistic Locking</h3>
<p>Optimistic locking assumes that conflicting updates are relatively uncommon and detects conflicts when they occur.</p>
<p>I added a version column to support JPA version-based locking:</p>
<pre><code class="language-java">@Version
private Long version;
</code></pre>
<p>Hibernate uses this version information to detect conflicting updates. If another transaction changes the entity first, the stale update can fail instead of silently overwriting newer data.</p>
<p><strong>Key benefit:</strong> Helps maintain data consistency without holding a pessimistic lock for the entire operation.</p>
<p>Each strategy has different trade-offs, so the right choice depends on transaction duration, contention, and workload.</p>
<h2>2. Preserving Transaction Boundaries in Executor Threads</h2>
<p>Asynchronous execution introduces an important Spring transaction-management consideration.</p>
<p>Spring commonly manages <code>@Transactional</code> behavior through proxies. Calling a transactional method through the Spring proxy allows the transaction interceptor to apply.</p>
<p>However, invoking a method through <code>this</code> within the same object can bypass that proxy.</p>
<p>I implemented self-proxy injection so that calls to transactional methods can go through the Spring-managed proxy, including when the work is executed in executor threads.</p>
<p>This helps preserve the intended transaction interception behavior.</p>
<p>It is also important to understand that transactions do not automatically propagate to a newly created executor thread. The transactional method needs to establish its own transaction when invoked through the appropriate proxy.</p>
<p><strong>Key lesson:</strong> Thread management and transaction management must be designed together when implementing concurrent database operations.</p>
<h2>3. Native Atomic Queries in ProductRepository</h2>
<p>I added a native atomic stock-update query in <code>ProductRepository</code>.</p>
<p>Instead of loading an entity, changing its stock in Java, and then saving it, the database can perform the conditional update directly.</p>
<p>This can reduce application-side read-modify-write operations and improve throughput under suitable workloads.</p>
<p>However, atomic SQL updates do not eliminate every form of database locking. Databases still use the locks and concurrency mechanisms required to execute the statement safely.</p>
<p>The important advantage is that the stock condition and deduction happen within one database operation.</p>
<h2>4. Idempotency-Key Validation for Checkout</h2>
<p>A checkout request may be repeated because of network timeouts, retries, or repeated button clicks.</p>
<p>Without duplicate-request protection, the same logical checkout attempt could trigger unwanted repeated processing.</p>
<p>I implemented <code>IdempotencyKey</code> validation to detect duplicate checkout requests and return HTTP <code>409 Conflict</code> when a duplicate is detected.</p>
<p>The intended flow is:</p>
<ol>
<li><p>The client submits a checkout request with an idempotency key.</p>
</li>
<li><p>The backend validates the key.</p>
</li>
<li><p>A new request proceeds through checkout processing.</p>
</li>
<li><p>A duplicate request is rejected with HTTP <code>409 Conflict</code>.</p>
</li>
</ol>
<p>For a production-grade implementation, the key must be stored and checked atomically, usually with a database uniqueness constraint. Its scope, expiration, request association, and behavior after failures must also be defined.</p>
<p>Idempotency protection should cover the actual checkout side effects, not merely the initial request validation.</p>
<p><strong>Key benefit:</strong> Helps prevent duplicate checkout processing caused by repeated requests.</p>
<h2>5. JPA Versioning and Bean Collision Resolution</h2>
<p>I added a version column to support optimistic locking and resolved a Spring bean collision encountered during application configuration.</p>
<p>The <code>@Version</code> field allows JPA/Hibernate to detect stale updates. This is especially important when multiple transactions attempt to update the same product.</p>
<p>Resolving the bean collision also helps the Spring application context initialize with the intended configuration.</p>
<p>These changes strengthen both database consistency and application stability.</p>
<h2>What I Learned</h2>
<p>This module reinforced several important backend engineering concepts:</p>
<ul>
<li><p>Atomic database operations can simplify concurrent inventory updates.</p>
</li>
<li><p>Pessimistic and optimistic locking solve different concurrency problems.</p>
</li>
<li><p>Spring transaction proxies affect how transactional methods are invoked.</p>
</li>
<li><p>Executor threads require deliberate transaction-boundary management.</p>
</li>
<li><p>Idempotency requires reliable duplicate detection around checkout side effects.</p>
</li>
<li><p>JPA versioning helps detect conflicting updates instead of silently overwriting data.</p>
</li>
</ul>
<h2>Conclusion</h2>
<p>The goal of this ShopEase milestone was to improve the reliability of inventory updates and checkout processing under concurrent requests.</p>
<p>By implementing atomic SQL updates, pessimistic and optimistic locking, self-proxy injection, idempotency validation, and JPA versioning, I strengthened the backend's approach to concurrency control and data consistency.</p>
<p>This work is another step toward building a more reliable and production-oriented e-commerce backend with Spring Boot.</p>
<p><strong>Tech Stack:</strong> Java · Spring Boot · Spring Data JPA · Hibernate · MySQL · SQL · Multithreading</p>
<p>#Java #SpringBoot #BackendDevelopment #Concurrency #MySQL #Ecommerce #ShopEase</p>
]]></content:encoded></item><item><title><![CDATA[ShopEase — Module 32: Solving Inventory Overselling with Concurrency Control]]></title><description><![CDATA[Learn how prevents inventory overselling using Java multithreading, optimistic and pessimistic locking, atomic stock updates, and 50-thread testing.]]></description><link>https://shitanshu.hashnode.dev/shopease-module-32-solving-inventory-overselling-with-concurrency-control</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/shopease-module-32-solving-inventory-overselling-with-concurrency-control</guid><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Mon, 05 Oct 2026 04:28:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/7d637158-267e-4257-b9c1-4085d25e18f7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In an e-commerce application, multiple users can try to purchase the same product at exactly the same time. This creates a common backend problem: Race Conditions. The Problem Suppose a product has only 10 items in stock. Now imagine 50 users place an order simultaneously. Without proper concurrency handling, multiple threads may read the same stock value before any of them updates it: Stock = 10 → Thread 1 reads 10 → Thread 2 reads 10 → Thread 3 reads 10 → ... → Multiple threads try to purchase The result can be incorrect stock updates and overselling, where the system accepts more orders than the available inventory. This is a serious problem for an e-commerce backend. The Solution In ShopEase, I implemented Concurrency, Multithreading &amp; Database Locking to handle these situations safely. The inventory update process now uses:</p>
<ul>
<li><p>Multithreading for concurrent request testing</p>
</li>
<li><p>Optimistic Locking using @Version</p>
</li>
<li><p>Pessimistic Locking using @Lock(LockModeType.PESSIMISTIC_WRITE)</p>
</li>
<li><p>Database row locking with FOR UPDATE</p>
</li>
<li><p>Atomic stock deduction</p>
</li>
<li><p>Proper transaction handling With pessimistic locking, when one transaction is updating a product's stock, other transactions have to wait instead of modifying the same row simultaneously. 50-Thread Stress Testing To verify the implementation, I performed concurrent testing with 50 threads trying to purchase the same product. The test focused on: 50 concurrent requests → Stock validation → Locking → Atomic deduction → Order result The important result was: No negative stock. No overselling. Concurrent requests handled safely. What This Module Solved The main problem solved in this module was protecting inventory from race conditions during simultaneous purchases. This makes the ShopEase backend more reliable when multiple users interact with the same product at the same time. Key Takeaway Concurrency isn't just about running multiple threads. In a real e-commerce system, it is about making sure that multiple operations happening simultaneously still produce a correct and consistent result. Tech Stack: Java • Spring Boot • Spring Data JPA • Hibernate • MariaDB • Multithreading • Optimistic Locking • Pessimistic Locking #Java #SpringBoot #Concurrency #Multithreading #DatabaseLocking #Hibernate #MariaDB #BackendDevelopment #ShopEase</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[ShopEase — Module 32: Concurrency, Multithreading & Database Locking]]></title><description><![CDATA[Explore how ShopEase handles concurrent inventory updates using Java multithreading, optimistic and pessimistic locking, and atomic stock deduction.]]></description><link>https://shitanshu.hashnode.dev/shopease-module-32-concurrency-multithreading-database-locking</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/shopease-module-32-concurrency-multithreading-database-locking</guid><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Sun, 04 Oct 2026 03:01:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/477e8073-87e7-4e74-9854-eb23fc202abb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As ShopEase moved toward a more production-oriented backend, one important problem needed to be solved:</p>
<p>What happens when multiple users try to purchase the same product at exactly the same time?</p>
<p>In a normal single-user test, stock deduction looks simple. But in a real e-commerce system, multiple requests can reach the backend simultaneously and try to update the same inventory.</p>
<p>That creates a serious problem: overselling.</p>
<p>For Module 32, I focused on multithreading, concurrent stock updates, optimistic locking, pessimistic locking, and atomic stock deduction.</p>
<p>⚡ The Problem: Concurrent Stock Updates</p>
<p>Suppose a product has:</p>
<p>Stock = 10</p>
<p>Now imagine 50 users trying to purchase that product simultaneously.</p>
<p>Without proper concurrency control, multiple threads could read the same stock value before any of them updates it.</p>
<p>For example:</p>
<p>Thread 1 → reads stock = 1<br />Thread 2 → reads stock = 1</p>
<p>Thread 1 → deducts stock<br />Thread 2 → deducts stock</p>
<p>Both threads may believe that the product is available.</p>
<p>This is a classic race condition, and in an e-commerce system it can result in selling more units than actually exist.</p>
<p>🧵 Multithreading &amp; Stress Testing</p>
<p>To reproduce this situation instead of relying on normal sequential testing, I introduced concurrent stress testing.</p>
<p>I used 50 concurrent threads attempting to perform stock-related operations.</p>
<p>The purpose wasn't simply to make multiple threads run at once.</p>
<p>The real objective was to verify whether the database and application could correctly handle simultaneous requests accessing the same inventory.</p>
<p>This made the concurrency problem much easier to observe and debug.</p>
<p>🔒 Pessimistic Locking</p>
<p>For pessimistic locking, I implemented:</p>
<p>LockModeType.PESSIMISTIC_WRITE</p>
<p>in the ProductRepository.</p>
<p>The idea is straightforward:</p>
<p>When one transaction is modifying a product row, other transactions shouldn't be allowed to simultaneously modify that same row.</p>
<p>I also configured the MariaDB dialect so Hibernate could generate the appropriate database-level row locking behavior using FOR UPDATE.</p>
<p>Conceptually, the flow becomes:</p>
<p>Transaction 1<br />↓<br />Lock Product Row<br />↓<br />Check Stock<br />↓<br />Deduct Stock<br />↓<br />Commit<br />↓<br />Release Lock</p>
<p>Another transaction attempting to modify the same row has to wait until the existing lock is released.</p>
<p>This provides strong protection for critical inventory updates.</p>
<p>🧠 Optimistic Locking</p>
<p>I also tested optimistic locking using JPA's:</p>
<p><a href="https://dev.to/version">@version</a></p>
<p>Instead of immediately locking the database row, optimistic locking assumes that conflicts are relatively uncommon.</p>
<p>Each update checks whether the entity version is still the expected version.</p>
<p>If another transaction has already modified the row, the version changes and the conflicting update can be detected.</p>
<p>Conceptually:</p>
<p>Thread 1 → Version 5 → Update → Version 6 ✅</p>
<p>Thread 2 → Version 5 → Update<br />↓<br />Version mismatch ❌</p>
<p>This approach is useful when you want to detect concurrent modifications without holding database locks throughout the transaction.</p>
<p>🛡️ Atomic Stock Deduction</p>
<p>Locking alone isn't the complete solution.</p>
<p>The actual stock operation also needs to be handled carefully so that the availability check and deduction happen safely within the transaction.</p>
<p>The goal was:</p>
<p>Check stock<br />↓<br />Stock available?<br />↓<br />Atomic deduction<br />↓<br />Commit</p>
<p>If sufficient stock isn't available, the purchase must fail rather than allowing the stock value to become invalid.</p>
<p>🧪 50-Thread Concurrent Testing</p>
<p>After implementing both approaches, I performed concurrent testing with 50 threads.</p>
<p>The tests were used to verify:</p>
<p>Concurrent access to the same product<br />Optimistic locking conflicts<br />Pessimistic row locking<br />Atomic stock deduction<br />Collision handling<br />Prevention of negative stock<br />Prevention of overselling</p>
<p>The final result was zero overselling during the concurrent stress tests.</p>
<p>🔍 What I Learned</p>
<p>This module changed the way I think about backend development.</p>
<p>A piece of code can work perfectly when one request executes at a time and still fail when multiple requests execute simultaneously.</p>
<p>Concurrency introduces problems such as:</p>
<p>Race conditions<br />Lost updates<br />Concurrent modification conflicts<br />Database locking<br />Transaction boundaries<br />Thread synchronization</p>
<p>The important lesson was:</p>
<p>Correctness in a backend isn't only about what happens during one request. It's also about what happens when many requests happen at the same time.</p>
<p>Module 32 gave ShopEase practical protection against one of the most important problems in e-commerce systems: selling inventory that doesn't actually exist.</p>
<p>Tech Stack</p>
<p>Java • Spring Boot • Spring Data JPA • Hibernate • MariaDB • Multithreading • Optimistic Locking • Pessimistic Locking • Transactions</p>
<p><strong>Java #SpringBoot #Concurrency #Multithreading #Database #Hibernate #MariaDB #JPA #OptimisticLocking #PessimisticLocking #BackendDevelopment #ShopEase</strong></p>
]]></content:encoded></item><item><title><![CDATA[ShopEase: Advanced Backend Features with Webhooks, GraphQL, gRPC & Load Balancing]]></title><description><![CDATA[Explore ShopEase’s advanced backend features: Razorpay webhooks, GraphQL, gRPC communication, and load balancing with Spring Boot and Java.]]></description><link>https://shitanshu.hashnode.dev/shopease-advanced-backend-features-with-webhooks-graphql-grpc-load-balancing</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/shopease-advanced-backend-features-with-webhooks-graphql-grpc-load-balancing</guid><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Sat, 03 Oct 2026 16:24:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/220febfe-a4c7-421f-9f90-385720e818d1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In Modules 30.12–30.15, I worked on four advanced backend concepts:</p>
<p>💳 Payment Webhooks<br />🔎 GraphQL Integration<br />⚡ gRPC Communication<br />⚖️ Load Balancing</p>
<p>These modules helped me understand how real-world backend systems handle payment events, flexible data fetching, high-performance service communication, and distributed traffic.</p>
<p>💳 Module 30.12 — Payment Webhooks</p>
<p>Payment processing doesn't end when a user completes a payment.</p>
<p>A payment provider can send asynchronous updates about events such as payment success, failure, or other payment-status changes.</p>
<p>For ShopEase, I implemented the payment webhook flow using Razorpay, including:</p>
<p>Webhook endpoint<br />Payment event handling<br />Webhook signature verification<br />Payment success/failure processing<br />Idempotent webhook handling</p>
<p>One of the important challenges here was avoiding duplicate processing.</p>
<p>A webhook can potentially be delivered more than once, so the backend needs to make sure the same payment event doesn't accidentally update an order multiple times.</p>
<p>This introduced an important production concept:</p>
<p>Receiving an event is not enough — the system must process it safely.</p>
<p>🔎 Module 30.13 — GraphQL Integration</p>
<p>The next challenge was handling situations where clients don't always need the complete response returned by a traditional REST endpoint.</p>
<p>I integrated GraphQL into ShopEase to understand a different approach to API communication.</p>
<p>With GraphQL, the client can request the specific fields it needs instead of depending entirely on predefined response structures.</p>
<p>This helped me understand concepts such as:</p>
<p>Queries<br />Mutations<br />Schemas<br />Resolvers<br />Flexible data fetching</p>
<p>The main learning was understanding the difference between simply creating an API and designing an API that gives the client more control over the requested data.</p>
<p>⚡ Module 30.14 — gRPC Communication</p>
<p>For service-to-service communication, I also explored gRPC.</p>
<p>Unlike traditional REST communication, gRPC uses Protocol Buffers and is designed for efficient communication between distributed services.</p>
<p>I worked with concepts including:</p>
<p>gRPC services<br />Protocol Buffers<br />Service definitions<br />Client-server communication<br />Internal microservice communication</p>
<p>One of the challenges was understanding the complete flow:</p>
<p>Client → Stub → gRPC Service → Response</p>
<p>This gave me a practical understanding of why technologies such as gRPC can be useful for communication between internal microservices.</p>
<p>⚖️ Module 30.15 — Load Balancing</p>
<p>As the number of services and requests increases, sending every request to a single instance can become a bottleneck.</p>
<p>That's where load balancing becomes important.</p>
<p>In this module, I worked with the concept of distributing incoming requests across multiple service instances.</p>
<p>The basic flow becomes:</p>
<p>Client → Load Balancer → Service Instance</p>
<p>instead of:</p>
<p>Client → Single Service Instance</p>
<p>This introduced concepts around:</p>
<p>Multiple service instances<br />Request distribution<br />Service discovery<br />Scalability<br />Handling increased traffic</p>
<p>The biggest takeaway was that scalability isn't simply about making one server more powerful. A distributed system can instead use multiple instances and distribute the workload between them.</p>
<p>🧠 What These Modules Taught Me</p>
<p>Modules 30.12–30.15 moved ShopEase beyond basic backend development and into more advanced distributed-system concepts.</p>
<p>I worked with four different problems:</p>
<p>Problem Technology<br />Handling asynchronous payment updates Razorpay Webhooks<br />Flexible API data fetching GraphQL<br />Efficient service communication gRPC<br />Distributing traffic across instances Load Balancing</p>
<p>Each technology solved a different problem, but together they helped me understand an important part of backend engineering:</p>
<p>A production-ready backend isn't just about making features work. It's also about how the system communicates, handles failures, processes events, and scales under load.</p>
<p>There is still more to explore in ShopEase, particularly around deployment, concurrency, fault tolerance, monitoring, and production configuration.</p>
<p>But completing these modules was another significant step in turning ShopEase from a simple e-commerce backend into a more scalable and distributed system.</p>
<p>Tech Stack</p>
<p>Java • Spring Boot • Microservices • Razorpay • GraphQL • gRPC • Protocol Buffers • Load Balancing</p>
<p><strong>#Java #SpringBoot #Microservices #GraphQL #gRPC #Razorpay #BackendDevelopment #ShopEase #DistributedSystems #Scalability</strong></p>
]]></content:encoded></item><item><title><![CDATA[Module 30.11 — Microservices Integration & Testing | ShopEase

After building different backend modules of ShopEase, the next major challenge was making those independent services work together as a distributed system.]]></title><description><![CDATA[Built and tested Microservices with Spring Boot, API Gateway, and Apache Kafka in ShopEase, solving service communication and event-driven]]></description><link>https://shitanshu.hashnode.dev/module-30-11-microservices-integration-testing-shopease-after-building-different-backend-modules-of-shopease-the-next-major-challenge-was-making-those-independent-services-work-together-as-a-distributed-system</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/module-30-11-microservices-integration-testing-shopease-after-building-different-backend-modules-of-shopease-the-next-major-challenge-was-making-those-independent-services-work-together-as-a-distributed-system</guid><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Sat, 03 Oct 2026 15:16:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/8de5863e-5e93-48be-bafc-5f315032a794.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In Module 30.11, I worked on integrating and testing multiple microservices including:</p>
<p>API Gateway Product Service User Service Cart Service Order Service Payment Service Apache Kafka for event-driven communication</p>
<p>The main objective wasn't just creating separate services. The real challenge was making sure these services could communicate reliably and handle asynchronous events correctly.</p>
<p>🔧 What Problem Did I Solve?</p>
<p>In a monolithic backend, different components can communicate directly within the same application.</p>
<p>With microservices, each service is separated. This creates new problems:</p>
<p>How does a request reach the correct service?</p>
<p>How does one service communicate with another?</p>
<p>What happens when communication is asynchronous?</p>
<p>How can services react to events without being tightly coupled?</p>
<p>For ShopEase, I addressed these problems by introducing an API Gateway for routing and using service-to-service communication along with Apache Kafka for event-driven communication.</p>
<p>For example, instead of making every service directly responsible for handling everything, events can be published and consumed by the services that need them.</p>
<p>⚙️ Challenges I Faced</p>
<p>The biggest learning came from debugging the integration rather than simply writing the code.</p>
<p>Some of the challenges included:</p>
<ol>
<li>API Gateway Routing</li>
</ol>
<p>Making sure requests were correctly routed from the gateway to the appropriate microservice required proper configuration and testing.</p>
<p>A small routing or path configuration issue could make an otherwise working service appear to be unavailable.</p>
<ol>
<li>Service-to-Service Communication</li>
</ol>
<p>Each service worked independently, but integration introduced another layer of complexity.</p>
<p>I had to verify that one service could correctly communicate with another and that the expected request/response flow was working.</p>
<ol>
<li>Kafka Event Communication</li>
</ol>
<p>Working with Kafka was different from normal synchronous REST communication.</p>
<p>Instead of:</p>
<p>Service A → Service B → Response</p>
<p>the flow could become:</p>
<p>Service A → Kafka → Event → Service B</p>
<p>Understanding producers, consumers, topics and asynchronous processing was an important part of this module.</p>
<ol>
<li>Debugging Distributed Systems</li>
</ol>
<p>One of the harder parts was identifying where a problem actually occurred.</p>
<p>When something failed, the issue could be inside the gateway, the calling service, the receiving service, Kafka configuration, or the event-consumer side.</p>
<p>This forced me to stop looking at the application as one single backend and start thinking in terms of multiple independently running services.</p>
<p>🧪 Testing &amp; Verification</p>
<p>After integration, I tested the communication flow between the services and verified that the expected requests and events were reaching their respective destinations.</p>
<p>The goal was not simply:</p>
<p>“The services are running.”</p>
<p>The goal was:</p>
<p>“The services are communicating and behaving as expected.”</p>
<p>This distinction became one of the biggest takeaways from the module.</p>
<p>🧠 What I Learned</p>
<p>This module gave me a much better understanding of what actually changes when moving from a traditional backend architecture toward microservices.</p>
<p>I learned that microservices are not simply about splitting one application into multiple projects.</p>
<p>They introduce challenges around:</p>
<p>Service communication API routing Asynchronous processing Event-driven architecture Failure debugging Distributed system testing Service dependencies</p>
<p>The biggest lesson for me was that distributed systems solve some problems while introducing completely new ones.</p>
<p>🚀 Another ShopEase Milestone</p>
<p>Completing Module 30.11 was an important step in my ShopEase backend journey because I moved beyond developing individual features and started working with distributed backend architecture.</p>
<p>There is still more to explore, especially around deployment, production configuration, scalability, fault tolerance and concurrency.</p>
<p>But this module gave me practical exposure to how multiple backend services can work together as one larger system.</p>
<p>Tech Stack: Java | Spring Boot | Microservices | API Gateway | Apache Kafka | REST APIs | Distributed System</p>
]]></content:encoded></item><item><title><![CDATA[💳 Implementing Razorpay Webhooks in Spring Boot]]></title><description><![CDATA[ShopEase Microservices project with API Gateway, Kafka, service communication, event-driven architecture, and integration testing using Spring Boot]]></description><link>https://shitanshu.hashnode.dev/implementing-razorpay-webhooks-in-spring-boot</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/implementing-razorpay-webhooks-in-spring-boot</guid><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Tue, 15 Sep 2026 04:55:47 GMT</pubDate><content:encoded><![CDATA[<h3>🚀 Real Problems I Faced &amp; How I Solved Them</h3>
<blockquote>
<p><strong>Building a payment integration is easy in a tutorial.<br />Making it actually work is where the real engineering begins.</strong> 😅</p>
</blockquote>
<p>While working on my <strong>ShopEase e-commerce project</strong>, I reached a point where payment creation and verification were already working. The next challenge was handling <strong>Razorpay Webhooks</strong> and processing payment events asynchronously.</p>
<hr />
<h2>🏗️ My Payment Architecture</h2>
<p>ShopEase uses a <strong>microservices-based backend</strong>, with a separate <code>payment-service</code> running on port <code>8085</code>.</p>
<h3>🔄 Before Webhooks</h3>
<pre><code class="language-plaintext">🛒 Frontend
     ↓
🌐 API Gateway
     ↓
💳 Payment Service
     ↓
💰 Razorpay
</code></pre>
<h3>⚡ After Adding Webhooks</h3>
<pre><code class="language-plaintext">💰 Razorpay
     ↓
🔔 Webhook
     ↓
💳 Payment Service
     ↓
🔐 Verify Signature
     ↓
🔎 Identify Event
     ↓
🗄️ Update Payment
     ↓
💾 Store Event ID
</code></pre>
<hr />
<h2>😵‍💫 Challenge #1 — Duplicate Webhooks</h2>
<p>One of the first things I realized was that a webhook <strong>may be delivered more than once</strong>.</p>
<p>If I process the same event twice, I could accidentally execute the same business logic multiple times.</p>
<p>So I introduced a <code>webhook_events</code> table and stored the webhook's <code>eventId</code>.</p>
<h3>🔐 Why?</h3>
<pre><code class="language-plaintext">Webhook received
      ↓
Already processed?
   ↙       ↘
 YES        NO
  ↓          ↓
Ignore     Process
             ↓
        Save Event ID
</code></pre>
<p>This gave my webhook processing <strong>idempotency</strong>.</p>
<hr />
<h2>🔑 Challenge #2 — Webhook Secret ≠ API Secret</h2>
<p>This one was important.</p>
<p>I learned that the <strong>Razorpay Webhook Secret is different from the Razorpay API Key Secret</strong>.</p>
<p>Instead of hardcoding the secret, I kept it in an environment variable:</p>
<pre><code class="language-plaintext">razorpay.webhook.secret=${RAZORPAY_WEBHOOK_SECRET}
</code></pre>
<p>🔒 <strong>Never put actual secrets directly into your GitHub repository.</strong></p>
<hr />
<h2>🛡️ Challenge #3 — Verifying the Webhook Signature</h2>
<p>I couldn't simply trust every request that reached my endpoint.</p>
<p>The request contains:</p>
<pre><code class="language-plaintext">X-Razorpay-Signature
</code></pre>
<p>I used Razorpay's signature verification mechanism with the <strong>raw request payload</strong> and my webhook secret.</p>
<pre><code class="language-plaintext">📨 Incoming Request
        ↓
🔐 Extract Signature
        ↓
🧮 Generate/Verify HMAC
        ↓
   Valid? ────── No → ❌ Reject
        ↓
       Yes
        ↓
     ✅ Process
</code></pre>
<hr />
<h2>🚫 Challenge #4 — The Mysterious <code>403 Forbidden</code></h2>
<p>This was one of those errors where I initially thought:</p>
<blockquote>
<p>"Controller mein hi kuch problem hai." 😅</p>
</blockquote>
<p>But the controller wasn't the problem.</p>
<p><strong>Spring Security was blocking the request.</strong></p>
<p>My normal APIs require authentication, but Razorpay's webhook doesn't have my application's JWT.</p>
<p>So I specifically permitted:</p>
<pre><code class="language-plaintext">.requestMatchers("/payments/webhook").permitAll()
</code></pre>
<p>and disabled CSRF for the API.</p>
<h3>💡 Lesson</h3>
<blockquote>
<p>A perfectly written controller is useless if security blocks the request before it reaches the controller.</p>
</blockquote>
<hr />
<h2>🧪 Challenge #5 — Testing Without Razorpay</h2>
<p>While testing locally through Postman, I initially didn't have the real Razorpay signature.</p>
<p>A fake signature such as:</p>
<pre><code class="language-plaintext">test-signature
</code></pre>
<p>correctly resulted in:</p>
<pre><code class="language-plaintext">401 Unauthorized
</code></pre>
<p>And honestly, that was <strong>good news</strong>. 😂</p>
<p>It meant my signature verification was actually doing its job.</p>
<p>So I created a <strong>Postman Pre-request Script</strong> to generate the HMAC-SHA256 signature automatically whenever I changed the webhook payload.</p>
<hr />
<h2>🫠 Challenge #6 — "Payment Record Not Found"</h2>
<p>This was probably one of my most interesting debugging moments.</p>
<p>The signature was valid ✅  </p>
<p>The event was recognized ✅</p>
<p>But:</p>
<pre><code class="language-plaintext">{
  "success": false,
  "message": "Payment record not found"
}
</code></pre>
<h3>🔍 Root Cause</h3>
<p>The Razorpay <strong>Order ID didn't match</strong> the Order ID stored in my database.</p>
<p>It was literally a tiny character difference. 😭</p>
<pre><code class="language-plaintext">Postman:
order_TT9CFIQhKkyXwR

Database:
order_TT9CFlQkHkyXwR
</code></pre>
<p>For humans, they look almost identical.</p>
<p>For a database?</p>
<p><strong>Completely different values.</strong> 💀</p>
<p>After correcting the Order ID, the webhook successfully found the payment.</p>
<hr />
<h2>🐳 Challenge #7 — XAMPP vs Docker MySQL</h2>
<p>This one caused a lot of confusion.</p>
<p>My application was using:</p>
<pre><code class="language-plaintext">localhost:3308
</code></pre>
<p>while I was checking MySQL through XAMPP/phpMyAdmin on:</p>
<pre><code class="language-plaintext">localhost:3306
</code></pre>
<p>I eventually discovered that port <code>3308</code> was actually mapped to my <strong>Docker MySQL container</strong>:</p>
<pre><code class="language-plaintext">Windows :3308
      ↓
🐳 Docker
      ↓
MySQL :3306
</code></pre>
<p>The container was:</p>
<pre><code class="language-plaintext">shopease-mysql
mysql:8.4
0.0.0.0:3308 → 3306
</code></pre>
<p>So instead of trying to move XAMPP to port <code>3308</code>, I connected directly to the Docker MySQL container.</p>
<h3>🧠 Biggest Lesson Here</h3>
<blockquote>
<p><strong>Before changing a port, first find out which process is already using it.</strong></p>
</blockquote>
<hr />
<h2>✅ Testing <code>payment.captured</code></h2>
<p>After fixing the database issue, I tested:</p>
<pre><code class="language-plaintext">payment.captured
</code></pre>
<p>The webhook returned:</p>
<pre><code class="language-plaintext">{
  "success": true,
  "message": "Webhook signature verified",
  "event": "payment.captured"
}
</code></pre>
<p>with:</p>
<pre><code class="language-plaintext">HTTP 200 OK
</code></pre>
<p>More importantly, I didn't stop at the API response.</p>
<p>I checked the database and confirmed:</p>
<pre><code class="language-plaintext">CREATED
   ↓
SUCCESS
</code></pre>
<p>That confirmed the <strong>actual business logic worked</strong>, not just the HTTP response.</p>
<hr />
<h2>🔁 Testing Idempotency</h2>
<p>Then came the real test.</p>
<p>I sent the <strong>exact same webhook again</strong> using the same event ID.</p>
<p>Instead of processing it again, the application returned:</p>
<pre><code class="language-plaintext">{
  "success": true,
  "message": "Webhook already processed",
  "event": "payment.captured"
}
</code></pre>
<p>🎯 <strong>Duplicate processing successfully prevented!</strong></p>
<hr />
<h2>❌ Testing <code>payment.failed</code></h2>
<p>I also tested:</p>
<pre><code class="language-plaintext">payment.failed
</code></pre>
<p>The webhook was successfully processed and the database payment status changed to:</p>
<pre><code class="language-plaintext">FAILED
</code></pre>
<p>So both major payment events were working:</p>
<table>
<thead>
<tr>
<th>Event</th>
<th>Result</th>
</tr>
</thead>
<tbody><tr>
<td>💰 <code>payment.captured</code></td>
<td>✅ SUCCESS</td>
</tr>
<tr>
<td>❌ <code>payment.failed</code></td>
<td>✅ FAILED</td>
</tr>
<tr>
<td>🔁 Duplicate event</td>
<td>✅ Blocked</td>
</tr>
<tr>
<td>🔐 Invalid signature</td>
<td>✅ Rejected</td>
</tr>
</tbody></table>
<hr />
<h1>🏁 Final Architecture</h1>
<pre><code class="language-plaintext">                    💰 Razorpay
                         │
                         │ 🔔 Webhook
                         ↓
                POST /payments/webhook
                         │
                         ↓
                🛡️ Spring Security
                         │
                         ↓
                🔐 Signature Check
                         │
                         ↓
                 🔎 Event Detection
                    ↙           ↘
           payment.captured   payment.failed
                  ↓                 ↓
             Find Payment      Find Payment
                  ↓                 ↓
               SUCCESS           FAILED
                    ↘           ↙
                         ↓
                  🔁 Check Event ID
                         ↓
                  Already Processed?
                    ↙          ↘
                  YES           NO
                   ↓             ↓
                Ignore       💾 Save Event
                                 ↓
                              ✅ 200 OK
</code></pre>
<hr />
<h1>🧠 What This Actually Taught Me</h1>
<p>The biggest lesson wasn't just <strong>"how to implement Razorpay Webhooks."</strong></p>
<p>It was learning how to debug a <strong>complete backend system</strong>.</p>
<h3>🔐 1. Security matters</h3>
<p>A controller can't help if Spring Security blocks the request.</p>
<h3>🎯 2. IDs must match exactly</h3>
<p>A single character difference can break a database lookup.</p>
<h3>🐳 3. Understand your infrastructure</h3>
<p>Docker port mapping can completely change where your application is actually connecting.</p>
<h3>🔁 4. Webhooks must be idempotent</h3>
<p>The same event shouldn't execute your business logic multiple times.</p>
<h3>🗄️ 5. Verify the database</h3>
<p><code>200 OK</code> doesn't necessarily mean your business logic worked.</p>
<h3>🐛 6. Debugging is part of development</h3>
<pre><code class="language-plaintext">💻 Code
   ↓
🧪 Test
   ↓
❌ Error
   ↓
🔎 Find Root Cause
   ↓
🔧 Fix
   ↓
🧪 Test Again
   ↓
🗄️ Verify Database
   ↓
✅ Done
</code></pre>
<hr />
<h1>🚀 Current Status</h1>
<p>My Razorpay Webhook implementation currently supports:</p>
<ul>
<li><p>  ✅ Webhook endpoint  </p>
</li>
<li><p>  ✅ Signature verification  </p>
</li>
<li><p>  ✅ <code>payment.captured</code>  </p>
</li>
<li><p>  ✅ <code>payment.failed</code>  </p>
</li>
<li><p>  ✅ Payment status updates  </p>
</li>
<li><p>  ✅ Duplicate webhook detection  </p>
</li>
<li><p>  ✅ Idempotent processing  </p>
</li>
<li><p>  ✅ Database verification</p>
</li>
</ul>
<p>The next step is to extend the webhook flow beyond payment status updates and connect it with the <strong>complete post-payment business logic</strong>, such as order confirmation and cart clearing.</p>
<hr />
<h1>💡 Final Takeaway</h1>
<p>Implementing Razorpay Webhooks taught me something important:</p>
<blockquote>
<p><strong>Real-world backend development isn't just about writing code. It's about understanding what happens when that code meets security, databases, infrastructure, external APIs, and unexpected errors.</strong></p>
</blockquote>
<p>The most valuable part of this implementation wasn't simply making the webhook work.</p>
<p>It was debugging:</p>
<p><strong>403 errors → invalid signatures → incorrect Order IDs → Docker/MySQL conflicts → duplicate events → database verification.</strong> 😎🔥</p>
<p>And that's what made this feature much more realistic than simply following an integration tutorial.</p>
<hr />
<h2>🛒 ShopEase — Payment Service</h2>
<h3>💳 Razorpay Webhook Integration 🚀</h3>
<p><strong>Learn → Build → Break → Debug → Fix → Improve.</strong> 💻🔥</p>
]]></content:encoded></item><item><title><![CDATA[From Monolith to Microservices: My ShopEase Backend Journey]]></title><description><![CDATA[From Monolith to Microservices: My ShopEase Backend Journey]]></description><link>https://shitanshu.hashnode.dev/from-monolith-to-microservices-my-shopease-backend-journey</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/from-monolith-to-microservices-my-shopease-backend-journey</guid><category><![CDATA[java springboot microservices backend]]></category><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Wed, 09 Sep 2026 08:26:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/475488d2-b953-4c2e-a725-50686e21e3b3.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1>From Monolith to Microservices: My ShopEase Backend Journey</h1>
<p>When I started building <strong>ShopEase</strong>, my main goal was to learn Java backend development by building a real e-commerce application instead of learning concepts only through small examples.</p>
<p>As the project grew, the backend started containing multiple business responsibilities such as products, users, authentication, carts, orders, payments, and more.</p>
<p>At that point, I started exploring an important question</p>
<blockquote>
<p><strong>What happens when a growing monolithic application needs to become more scalable and maintainable?</strong></p>
</blockquote>
<p>That led me to <strong>Microservices Architecture</strong>.</p>
<h2>What I Had Before</h2>
<p>ShopEase initially followed a <strong>monolithic backend architecture</strong>.</p>
<p>Different business modules were part of the same Spring Boot application:</p>
<p>ShopEase Backend</p>
<p>│</p>
<p>├── User</p>
<p>├── Product</p>
<p>├── Cart</p>
<p>├── Order</p>
<p>├── Authentication</p>
<p>├── Payment</p>
<p>└── Other Modules</p>
<p>Everything was running inside one application.</p>
<p>This architecture is perfectly useful while developing and learning, but as an application grows, managing every business domain inside one large codebase can become increasingly difficult.</p>
<p>So I decided to start exploring a microservices-based architecture.</p>
<h2>Starting the Microservices Migration</h2>
<p>For Module 30, I started transforming ShopEase from a monolithic backend toward a <strong>Microservices Architecture</strong>.</p>
<p>My current progress is:</p>
<p>30.1 Microservices Architecture ✅</p>
<p>30.2 Product Service ✅</p>
<p>30.3 User Service ⏳</p>
<p>The first major step was separating the <strong>Product Service</strong> from the main backend.</p>
<h2>Product Service</h2>
<p>The Product Service is responsible for product-related functionality.</p>
<p>Instead of keeping product functionality tightly coupled with every other business module, it can now be treated as an independent service.</p>
<p>Conceptually:</p>
<pre><code class="language-plaintext">            ShopEase
               │
      ┌────────┴────────┐
      │                 │
Product Service     User Service
      │
   Product DB
</code></pre>
<p>The idea is that each service should have a clearly defined responsibility.</p>
<p>For example:</p>
<p><strong>Product Service</strong></p>
<p>Products Categories Product Details Product Operations</p>
<p>while the future <strong>User Service</strong> will handle responsibilities related to users.</p>
<h2>Why Microservices?</h2>
<p>The biggest thing I'm learning is that microservices are not simply about splitting one project into multiple folders.</p>
<p>The architecture introduces completely different engineering considerations:</p>
<ul>
<li><p>Service boundaries</p>
</li>
<li><p>Independent deployment</p>
</li>
<li><p>Service-to-service communication</p>
</li>
<li><p>Data ownership</p>
</li>
<li><p>Failure handling</p>
</li>
<li><p>Scalability</p>
</li>
<li><p>API design</p>
</li>
<li><p>Authentication between services</p>
</li>
<li><p>Monitoring and debugging</p>
</li>
</ul>
<p>This is one of the reasons I wanted to implement the architecture inside ShopEase rather than learning it only theoretically.</p>
<h2>What I Learned So Far</h2>
<p>One important lesson from this module is that <strong>architecture changes introduce new problems</strong>.</p>
<p>In a monolith, calling another module may simply mean calling a Java method.</p>
<p>With microservices, that communication may happen through a network request.</p>
<p>That changes the way we think about:</p>
<p>Method Call</p>
<p>↓</p>
<p>HTTP/API Communication</p>
<p>↓</p>
<p>Another Service</p>
<p>This introduces concepts that I am now starting to explore more deeply.</p>
<h2>What's Next?</h2>
<p>The next step in my ShopEase microservices journey is:</p>
<p>Product Service ✅</p>
<p>↓</p>
<p>User Service ⏳</p>
<p>↓</p>
<p>Service Communication</p>
<p>↓</p>
<p>API Gateway</p>
<p>↓</p>
<p>Service Discovery</p>
<p>↓</p>
<p>Event-Driven Architecture</p>
<p>↓</p>
<p>Kafka / RabbitMQ</p>
<p>These are the areas I want to explore as the architecture evolves.</p>
<h2>Why I'm Building This</h2>
<p>ShopEase started as a project to learn Java backend development.</p>
<p>Now it has become a practical way for me to understand how real-world backend systems evolve.</p>
<p>Instead of only learning:</p>
<blockquote>
<p>"What is Microservices Architecture?"</p>
</blockquote>
<p>I'm trying to understand:</p>
<blockquote>
<p><strong>"How would I actually migrate an existing application toward microservices?"</strong></p>
</blockquote>
<p>That difference is making the learning experience much more practical.</p>
<h2>Final Thoughts</h2>
<p>The biggest takeaway from this module is that <strong>building a system teaches you things that theory alone cannot fully explain</strong>.</p>
<p>Every new architectural decision introduces new challenges, and solving those challenges is becoming part of my learning process.</p>
<p>ShopEase is still evolving, and I am documenting the journey as I continue learning Java, Spring Boot, backend engineering, and system design.</p>
<p><strong>Learn → Build → Solve → Improve.</strong> 🚀</p>
]]></content:encoded></item><item><title><![CDATA[🐳 Dockerizing ShopEase: From Local Development to a Containerized Application
How I containerized my Spring Boot + MySQL e-commerce application using Docker, Docker Compose, networking, and environment variables.

By Shitanshu Jha]]></title><description><![CDATA[How Shitanshu Jha containerized ShopEase using Docker, Docker Compose, Spring Boot, MySQL, networking, and environment variables]]></description><link>https://shitanshu.hashnode.dev/dockerizing-shopease-from-local-development-to-a-containerized-application-how-i-containerized-my-spring-boot-mysql-e-commerce-application-using-docker-docker-compose-networking-and-environment-variables-by-shitanshu-jha</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/dockerizing-shopease-from-local-development-to-a-containerized-application-how-i-containerized-my-spring-boot-mysql-e-commerce-application-using-docker-docker-compose-networking-and-environment-variables-by-shitanshu-jha</guid><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Thu, 03 Sep 2026 03:23:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/cfad5e62-a0ce-4799-b5b6-89e11cf50e2a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi, I'm <strong>Shitanshu Jha</strong>, a BCA student and Java Backend Developer focused on building real-world applications using <strong>Java, Spring Boot, REST APIs, Spring Security, JPA/Hibernate, and MySQL</strong>.</p>
<p>One of the major projects I am currently developing is <strong>ShopEase</strong>, a full-stack e-commerce application.</p>
<p>ShopEase wasn't built in one go.</p>
<p>I have been developing it <strong>step by step and module by module</strong>. With every new module, I have faced new problems, debugged issues, understood why they occurred, and then implemented better solutions.</p>
<p>Throughout this journey, I have worked on different parts of an e-commerce system, including authentication, products, cart, wishlist, checkout, orders, validation, exception handling, security, and database integration.</p>
<p>After getting the core application working, I reached another important question:</p>
<blockquote>
<p><strong>How can I make ShopEase run consistently without depending entirely on the configuration of my local machine?</strong></p>
</blockquote>
<p>This question led me to <strong>Docker</strong>.</p>
<p>In <strong>Module 28</strong>, I successfully containerized ShopEase and tested the application with <strong>Spring Boot and MySQL running inside Docker containers</strong>.</p>
<hr />
<h1>🚀 Why I Added Docker to ShopEase</h1>
<p>Before Docker, ShopEase depended heavily on my local development environment.</p>
<p>I needed the correct:</p>
<ul>
<li><p>Java environment</p>
</li>
<li><p>MySQL installation</p>
</li>
<li><p>Database configuration</p>
</li>
<li><p>Application configuration</p>
</li>
<li><p>Ports</p>
</li>
<li><p>Credentials and other environment-specific values</p>
</li>
</ul>
<p>My application might work perfectly on my computer, but that doesn't automatically mean it will behave exactly the same way on another machine.</p>
<p>That is the classic:</p>
<blockquote>
<p><strong>“It works on my machine.”</strong></p>
</blockquote>
<p>problem.</p>
<p>I wanted ShopEase to move beyond that kind of setup.</p>
<p>Instead of thinking only about writing backend code, I wanted to understand how an actual application can be <strong>packaged, configured, and run as multiple services</strong>.</p>
<p>That's why I decided to containerize it.</p>
<hr />
<h1>🧱 Step 1 — Understanding What Needed to Be Containerized</h1>
<p>Before writing Docker configuration, I first looked at the basic ShopEase backend architecture.</p>
<p>It was essentially:</p>
<pre><code class="language-plaintext">Frontend
   │
   ▼
REST APIs
   │
   ▼
Spring Boot Backend
   │
   ▼
MySQL Database
</code></pre>
<p>The backend depends on MySQL.</p>
<p>So simply putting the Spring Boot application inside a container wasn't enough.</p>
<p>I needed two main services:</p>
<pre><code class="language-plaintext">┌─────────────────────┐
│ Spring Boot Backend │
└──────────┬──────────┘
           │
           │ Database Connection
           ▼
┌─────────────────────┐
│       MySQL         │
└─────────────────────┘
</code></pre>
<p>Both services needed to run independently while still being able to communicate.</p>
<p>That became the foundation of my Docker implementation.</p>
<hr />
<h1>🐳 Step 2 — Creating a Dockerfile for Spring Boot</h1>
<p>The next step was creating a <strong>Dockerfile</strong> for the ShopEase backend.</p>
<p>A Dockerfile defines how Docker should create an image for an application.</p>
<p>The basic flow became:</p>
<pre><code class="language-plaintext">ShopEase Backend
       │
       ▼
   Build Project
       │
       ▼
     JAR File
       │
       ▼
  Docker Image
       │
       ▼
Docker Container
       │
       ▼
Spring Boot Running
</code></pre>
<p>This was an important change in how I thought about the application.</p>
<p>Previously, I was mainly thinking:</p>
<pre><code class="language-plaintext">Write Code → Run Spring Boot
</code></pre>
<p>Now I had to think:</p>
<pre><code class="language-plaintext">Write Code
   ↓
Build Application
   ↓
Package Application
   ↓
Build Docker Image
   ↓
Create Container
   ↓
Run Application
</code></pre>
<p>It gave me a better understanding of how backend applications move from development toward deployment.</p>
<hr />
<h1>🗄️ Step 3 — Containerizing MySQL</h1>
<p>ShopEase stores application data in <strong>MySQL</strong>.</p>
<p>So the next task was running MySQL as a separate container.</p>
<p>Now instead of thinking about MySQL only as a database installed on my Windows machine, I could treat it as an independent service.</p>
<p>The architecture started looking like this:</p>
<pre><code class="language-plaintext">Docker
│
├── ShopEase Backend Container
│       │
│       │
│       ▼
│
└── MySQL Container
</code></pre>
<p>But this introduced one of the most important challenges.</p>
<hr />
<h1>⚠️ Challenge 1 — Connecting Spring Boot to MySQL</h1>
<p>When both applications are running directly on the same machine, using <code>localhost</code> feels natural.</p>
<p>But containers change this concept.</p>
<p>Inside the Spring Boot container:</p>
<pre><code class="language-plaintext">localhost
</code></pre>
<p>refers to the <strong>Spring Boot container itself</strong>.</p>
<p>It does not automatically mean the MySQL container.</p>
<p>That was an important concept for me to understand.</p>
<p>I needed the backend container to find and communicate with the database container correctly.</p>
<p>This led directly to the next part of the implementation.</p>
<hr />
<h1>🌐 Step 4 — Docker Networking</h1>
<p>To allow the Spring Boot and MySQL containers to communicate, I configured them to work through Docker networking.</p>
<p>Conceptually, the architecture became:</p>
<pre><code class="language-plaintext">             Docker Network
                   │
       ┌───────────┴───────────┐
       │                       │
       ▼                       ▼
┌──────────────┐        ┌──────────────┐
│ Spring Boot  │ &lt;────&gt; │    MySQL     │
│  Container   │        │  Container   │
└──────────────┘        └──────────────┘
</code></pre>
<p>Instead of treating the database as something running on the host machine, the backend can communicate with the database service inside the Docker environment.</p>
<p>This helped me understand an important Docker concept:</p>
<blockquote>
<p><strong>Containers are isolated, but they can communicate through properly configured networks.</strong></p>
</blockquote>
<hr />
<h1>⚠️ Challenge 2 — Application Configuration</h1>
<p>Getting the containers running was only part of the work.</p>
<p>The Spring Boot application also needed the correct database configuration.</p>
<p>The application needed values such as:</p>
<pre><code class="language-plaintext">Database Host
Database Port
Database Name
Database Username
Database Password
</code></pre>
<p>Hardcoding all of these values directly into the application would make the configuration difficult to manage across different environments.</p>
<p>So I worked with <strong>environment variables</strong>.</p>
<hr />
<h1>🔐 Step 5 — Using Environment Variables</h1>
<p>Environment variables allowed me to separate configuration from application code.</p>
<p>For example, configuration can conceptually be represented as:</p>
<pre><code class="language-plaintext">DB_HOST
DB_PORT
DB_NAME
DB_USERNAME
DB_PASSWORD
</code></pre>
<p>The application can then read these values from its environment.</p>
<p>This means the same application can work with different configurations.</p>
<p>For example:</p>
<pre><code class="language-plaintext">Development Environment
        │
        ├── Development DB
        │
Testing Environment
        │
        ├── Testing DB
        │
Production Environment
        │
        └── Production DB
</code></pre>
<p>without requiring the actual application logic to be rewritten.</p>
<p>This also taught me an important backend engineering principle:</p>
<blockquote>
<p><strong>Configuration should be separated from application logic whenever possible.</strong></p>
</blockquote>
<p>Sensitive credentials should also not be committed directly to a public Git repository.</p>
<hr />
<h1>🧩 Step 6 — Bringing Everything Together with Docker Compose</h1>
<p>At this point, ShopEase had more than one container.</p>
<p>I had:</p>
<pre><code class="language-plaintext">Spring Boot Container
MySQL Container
</code></pre>
<p>Starting and managing every service separately would quickly become inconvenient.</p>
<p>That's where <strong>Docker Compose</strong> became useful.</p>
<p>With Docker Compose, I could describe the services required by ShopEase together.</p>
<p>Conceptually:</p>
<pre><code class="language-plaintext">docker compose
      │
      ├── Spring Boot Service
      │
      ├── MySQL Service
      │
      ├── Environment Variables
      │
      └── Networking
</code></pre>
<p>Then the complete environment could be managed as one multi-container application.</p>
<p>The startup process became much cleaner:</p>
<pre><code class="language-plaintext">docker compose up
        │
        ▼
Create / Start Services
        │
        ├───────────────┐
        ▼               ▼
  Spring Boot         MySQL
   Container         Container
        │               │
        └──── Network ──┘
</code></pre>
<p>This was one of the biggest improvements Docker brought to ShopEase.</p>
<hr />
<h1>⚠️ Challenge 3 — Moving from Localhost Thinking to Container Thinking</h1>
<p>One of my biggest learnings during this module was that containerized applications require a different way of thinking.</p>
<p>Previously, my environment was mostly:</p>
<pre><code class="language-plaintext">My Computer
   ├── Java
   ├── Spring Boot
   └── MySQL
</code></pre>
<p>After Docker:</p>
<pre><code class="language-plaintext">My Computer
     │
   Docker
     │
     ├── Spring Boot Container
     │
     └── MySQL Container
</code></pre>
<p>The application and database were no longer simply two programs running directly beside each other.</p>
<p>They were independent containers.</p>
<p>That affected:</p>
<ul>
<li><p>Hostnames</p>
</li>
<li><p>Networking</p>
</li>
<li><p>Ports</p>
</li>
<li><p>Database configuration</p>
</li>
<li><p>Environment variables</p>
</li>
<li><p>Startup dependencies</p>
</li>
</ul>
<p>Understanding these differences was one of the most valuable parts of this module.</p>
<hr />
<h1>🛠️ My Problem-Solving Approach</h1>
<p>I have followed a similar development process throughout ShopEase.</p>
<p>Whenever something doesn't work, I try not to randomly change code until the error disappears.</p>
<p>My approach is generally:</p>
<pre><code class="language-plaintext">Implement Feature
      ↓
Run Application
      ↓
Test Feature
      ↓
Observe Error
      ↓
Understand the Cause
      ↓
Change Configuration / Code
      ↓
Test Again
      ↓
Verify Complete Flow
</code></pre>
<p>I followed the same approach while implementing Docker.</p>
<p>Containerization introduced new problems because the environment was different from my normal local setup.</p>
<p>I had to understand how the Spring Boot container sees MySQL, how the services communicate, and how configuration should reach the application.</p>
<p>Solving those problems step by step made Docker much easier to understand than simply memorizing Docker commands.</p>
<hr />
<h1>🧪 Step 7 — Testing the Containerized Application</h1>
<p>I didn't want to consider the module complete just because the containers started successfully.</p>
<p>The important question was:</p>
<blockquote>
<p><strong>Does ShopEase actually work correctly inside this environment?</strong></p>
</blockquote>
<p>So I tested the setup to verify that:</p>
<ul>
<li><p>  ✅ Docker image builds correctly  </p>
</li>
<li><p>  ✅ Spring Boot container starts  </p>
</li>
<li><p>  ✅ MySQL container starts  </p>
</li>
<li><p>  ✅ Spring Boot can communicate with MySQL  </p>
</li>
<li><p>  ✅ Docker networking works  </p>
</li>
<li><p>  ✅ Environment variables are passed correctly  </p>
</li>
<li><p>  ✅ Database connectivity works  </p>
</li>
<li><p>  ✅ Backend APIs continue functioning in the containerized environment</p>
</li>
</ul>
<p>After testing the complete flow, I marked:</p>
<h2>✅ Module 28 — Docker: Completed &amp; Tested</h2>
<hr />
<h1>🏗️ ShopEase Docker Architecture</h1>
<p>The final containerized backend architecture can be visualized like this:</p>
<pre><code class="language-plaintext">                    ShopEase
                       │
                       ▼
                Docker Compose
                       │
              Docker Network
                       │
           ┌───────────┴───────────┐
           │                       │
           ▼                       ▼
┌─────────────────────┐   ┌─────────────────┐
│     Spring Boot     │   │      MySQL      │
│      Container      │◄─►│    Container    │
│                     │   │                 │
│   ShopEase Backend  │   │ ShopEase Data   │
└─────────────────────┘   └─────────────────┘
           │
           ▼
       REST APIs
</code></pre>
<hr />
<h1>💡 What I Learned From Module 28</h1>
<p>Before implementing this module, Docker was mostly another technology on my learning roadmap.</p>
<p>After actually integrating it into ShopEase, I understood why containerization matters.</p>
<p>I gained practical experience with:</p>
<p><strong>Dockerfile</strong> — packaging the Spring Boot application.</p>
<p><strong>Docker Images</strong> — understanding the blueprint used to create containers.</p>
<p><strong>Docker Containers</strong> — running isolated application services.</p>
<p><strong>Docker Compose</strong> — managing multiple services together.</p>
<p><strong>Docker Networking</strong> — enabling communication between Spring Boot and MySQL.</p>
<p><strong>Environment Variables</strong> — separating configuration from application code.</p>
<p><strong>Containerized Databases</strong> — running MySQL as an independent service.</p>
<p>But the biggest learning wasn't a Docker command.</p>
<p>It was understanding that:</p>
<blockquote>
<p><strong>Building an application is not only about writing code. You also need to think about how that application will be configured, packaged, connected, run, tested, and eventually deployed.</strong></p>
</blockquote>
<hr />
<h1>👨‍💻 Building ShopEase Step by Step</h1>
<p>ShopEase has been a learning-by-building project for me.</p>
<p>I didn't start by knowing everything required to build the complete application.</p>
<p>Instead, I have been developing it incrementally.</p>
<pre><code class="language-plaintext">Learn
  ↓
Implement
  ↓
Face Problems
  ↓
Debug
  ↓
Understand
  ↓
Improve
  ↓
Move to Next Module
</code></pre>
<p>Every new feature has introduced something new for me to understand.</p>
<p>Some challenges have been related to backend logic, some to databases, some to security and API integration, and now some to infrastructure and containerization.</p>
<p>That process is exactly why I continue developing ShopEase.</p>
<p>The goal isn't just to add a long list of technologies to the project.</p>
<p>The goal is to understand <strong>why they are needed and how they work together in a real application.</strong></p>
<hr />
<h1>🚀 What's Next for ShopEase?</h1>
<p>Completing Docker doesn't mean ShopEase is finished.</p>
<p>Containerization is another step toward making the project more deployment-oriented.</p>
<p>My upcoming areas of focus include further improving:</p>
<ul>
<li><p>Automated testing</p>
</li>
<li><p>CI/CD</p>
</li>
<li><p>Production configuration</p>
</li>
<li><p>Cloud deployment</p>
</li>
<li><p>Monitoring and logging</p>
</li>
<li><p>Backend reliability and optimization</p>
</li>
</ul>
<p>I want to continue evolving ShopEase while learning the engineering practices used beyond local development.</p>
<hr />
<h1>🎯 Conclusion</h1>
<p><strong>Module 28 was more than simply adding a Dockerfile to ShopEase.</strong></p>
<p>It changed how I think about running an application.</p>
<p>I moved from running Spring Boot and MySQL as locally configured components to understanding how they can operate as <strong>separate but connected containerized services</strong>.</p>
<p>During the implementation, I had to understand networking, configuration, environment variables, service communication, and multi-container management.</p>
<p>I faced problems, debugged them step by step, tested the complete setup, and finally got <strong>Spring Boot and MySQL running together successfully using Docker Compose</strong>.</p>
<p>There is still much more for me to learn and improve, but that's exactly how I am building ShopEase:</p>
<blockquote>
<p><strong>One module. One challenge. One solution at a time. 🚀</strong></p>
</blockquote>
<h3>🐳 Module 28 — Docker</h3>
<p><strong>Status: ✅ Completed &amp; Tested</strong></p>
<p><strong>Developed by Shitanshu Jha</strong></p>
]]></content:encoded></item><item><title><![CDATA[6. 🚀 Making ShopEase Production-Ready: Pagination, Search, Filtering & Image Upload
👨‍💻 About Me

I'm Shitanshu Jha, a BCA student and aspiring Java Backend Developer focused on building real-world applications with Java and Spring Boot.
I enjoy learning by building projects rather than only studying individual technologies. My current project, ShopEase, is a full-stack e-commerce application where I'm working with Java, Spring Boot, Spring Data JPA, Hibernate, Spring Security, JWT, MySQL and REST APIs.

With ShopEase, my goal is not just to make an application that works, but to gradually move it toward a more production-oriented architecture.]]></title><description><![CDATA[Explore how I enhanced ShopEase with Spring Boot pagination, sorting, search, filtering, and image upload while moving the e-commerce application.]]></description><link>https://shitanshu.hashnode.dev/6-making-shopease-production-ready-pagination-search-filtering-image-upload-about-me-i-m-shitanshu-jha-a-bca-student-and-aspiring-java-backend-developer-focused-on-building-real-world-applications-with-java-and-spring-boot-i-enjoy-learning-by-building-projects-rather-than-only-studying-individual-technologies-my-current-project-shopease-is-a-full-stack-e-commerce-application-where-i-m-working-with-java-spring-boot-spring-data-jpa-hibernate-spring-security-jwt-mysql-and-rest-apis-with-shopease-my-goal-is-not-just-to-make-an-application-that-works-but-to-gradually-move-it-toward-a-more-production-oriented-architecture</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/6-making-shopease-production-ready-pagination-search-filtering-image-upload-about-me-i-m-shitanshu-jha-a-bca-student-and-aspiring-java-backend-developer-focused-on-building-real-world-applications-with-java-and-spring-boot-i-enjoy-learning-by-building-projects-rather-than-only-studying-individual-technologies-my-current-project-shopease-is-a-full-stack-e-commerce-application-where-i-m-working-with-java-spring-boot-spring-data-jpa-hibernate-spring-security-jwt-mysql-and-rest-apis-with-shopease-my-goal-is-not-just-to-make-an-application-that-works-but-to-gradually-move-it-toward-a-more-production-oriented-architecture</guid><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Mon, 31 Aug 2026 06:58:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/63c3845a-bfd5-4315-ad2b-900bf9d5c64e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>🛒 What is ShopEase?</p>
<p>ShopEase is a full-stack e-commerce application designed to simulate the functionality of a real-world shopping platform.</p>
<p>Its architecture currently follows:</p>
<p>Frontend HTML / CSS / JavaScript ↓ Fetch API ↓ Spring Boot REST API ↓ Spring Security + JWT ↓ Spring Data JPA / Hibernate ↓ MySQL</p>
<p>The project already includes authentication, authorization, products, cart, wishlist, orders, payments, admin functionality, inventory management, validation, exception handling and logging.</p>
<p>After completing the major business features, I started focusing on an important question:</p>
<p>How can I make the application handle real-world data and usage more efficiently?</p>
<p>That led to the next three modules.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/328a3e42-c9fa-4518-b175-0d136ee6933e.png" alt="" style="display:block;margin:0 auto" />

<p>🚀 Module 25 — Pagination &amp; Sorting The Problem</p>
<p>Imagine an e-commerce application has 10,000 products.</p>
<p>If the API returns every product whenever the user opens the product page, the application has to:</p>
<p>Fetch a large amount of data. Transfer unnecessary data to the client. Process more records than required. Potentially increase response time.</p>
<p>Users don't need 10,000 products at once.</p>
<p>They might only need 10 or 20 products per page.</p>
<p>The Solution</p>
<p>I implemented Pagination and Sorting using Spring Data JPA.</p>
<p>The important concepts are:</p>
<p>Pageable Page Sorting</p>
<p>Instead of requesting everything:</p>
<p>GET /products</p>
<p>the application can request a specific page and size.</p>
<p>Conceptually:</p>
<p>Page 1 → Products 1–10 Page 2 → Products 11–20 Page 3 → Products 21–30</p>
<p>Sorting can also be applied according to requirements such as:</p>
<p>Price → Low to High Price → High to Low Name → A-Z Rating → Highest First Why this matters</p>
<p>Pagination reduces the amount of data processed and returned in a single request.</p>
<p>This becomes increasingly important as the number of products grows.</p>
<p>🔎 Module 26 — Search &amp; Filtering The Problem</p>
<p>Pagination solves the problem of too many results, but there is another problem.</p>
<p>Suppose a customer wants:</p>
<p>"Show me highly-rated mobile products from a particular brand within my budget."</p>
<p>Simply displaying pages of products isn't enough.</p>
<p>The user needs to find relevant products quickly.</p>
<p>The Solution</p>
<p>I implemented product Search &amp; Filtering based on:</p>
<p>Product Name Category Brand Price Rating Multiple filters</p>
<p>This allows the application to narrow down the product dataset according to the user's requirements.</p>
<p>For example:</p>
<p>Category = Mobile Brand = Samsung Price = ₹20,000 – ₹40,000 Rating = 4★+</p>
<p>Instead of returning the complete product catalogue, the backend returns only products matching the requested conditions.</p>
<p>Multiple Filters</p>
<p>One important part is combining multiple conditions.</p>
<p>For example:</p>
<p>Category AND Brand AND Price Range AND Rating</p>
<p>This makes the product catalogue much more useful from a customer's perspective.</p>
<p>🖼️ Module 27 — Image Upload The Problem</p>
<p>A product catalogue isn't useful if products don't have proper images.</p>
<p>Initially, product data can simply contain text such as:</p>
<p>name price category brand rating</p>
<p>But an actual e-commerce platform also needs product images.</p>
<p>The backend therefore needs to accept an uploaded file, store/manage it and provide a URL that can be used by the frontend.</p>
<p>The Solution</p>
<p>I implemented product image handling using:</p>
<p>MultipartFile Image Upload/Storage Image URLs Product Image Management</p>
<p>The basic flow is:</p>
<p>Admin selects image ↓ MultipartFile ↓ Spring Boot Backend ↓ Image Storage ↓ Image URL ↓ Product ↓ Frontend displays image</p>
<p>This separates the concept of product data from the actual image file while allowing the product to reference its image through a URL.</p>
<h2>🧩 What Problems Do These Modules Solve?</h2>
<p>These three modules address very different problems:</p>
<p>Feature Problem Solved Pagination Too many products being returned at once Sorting Users need results in a useful order Search Users need to find specific products Filtering Users need to narrow down results Image Upload Products need visual representation</p>
<p>Together, they make the application much closer to how users expect a modern e-commerce platform to behave.</p>
<h2><strong>⚙️ Challenges &amp; Engineering Considerations</strong></h2>
<p>While implementing these modules, the main engineering challenges are not simply writing a few methods. The bigger challenge is designing the functionality so that it works correctly as the application grows.</p>
<ol>
<li>Handling large datasets</li>
</ol>
<p>Pagination needs to be applied at the database/query level, rather than loading every product into memory and manually splitting the list.</p>
<ol>
<li>Combining filters</li>
</ol>
<p>When several filters are provided together, the backend needs to correctly combine the conditions instead of treating each filter independently.</p>
<p>For example:</p>
<p>Category + Brand + Price + Rating</p>
<p>should produce one meaningful result set.</p>
<ol>
<li>Optional filters</li>
</ol>
<p>Not every request will contain every filter.</p>
<p>A user might search only by:</p>
<p>brand</p>
<p>while another request might contain:</p>
<p>brand + category + price + rating</p>
<p>The backend therefore needs to handle different combinations cleanly.</p>
<ol>
<li>File handling</li>
</ol>
<p>Image uploads are different from normal JSON requests.</p>
<p>The backend needs to handle:</p>
<p>multipart/form-data</p>
<p>and process the uploaded file through MultipartFile.</p>
<ol>
<li>Maintaining clean API design</li>
</ol>
<p>Adding features shouldn't turn the controller into a large block of business logic.</p>
<p>Keeping responsibilities separated between the Controller, Service and Repository layers makes the application easier to maintain.</p>
<p>📈 What These Modules Taught Me</p>
<p>These modules helped me understand an important difference between:</p>
<p>“An application that works”</p>
<p>and</p>
<p>“An application designed to handle real-world usage.”</p>
<p>A basic product API may work perfectly with 20 products.</p>
<p>But what happens when there are:</p>
<p>20 products? ↓ 2,000 products? ↓ 20,000 products?</p>
<p>That's where decisions such as pagination, filtering, efficient querying and proper file handling become important.</p>
<p>This is the direction I'm trying to take ShopEase in.</p>
<p>🛠️ Technology Stack</p>
<p>Backend</p>
<p>Java Spring Boot Spring MVC Spring Data JPA Hibernate</p>
<p>Database</p>
<p>MySQL</p>
<p>Security</p>
<p>Spring Security JWT BCrypt</p>
<p>Frontend</p>
<p>HTML CSS JavaScript Fetch API</p>
<p>Tools</p>
<p>Eclipse Postman Git GitHub XAMPP 🔮 What's Next?</p>
<p>With Modules 25–27 completed and tested, my next focus is continuing the production-hardening phase of ShopEase.</p>
<p>The upcoming roadmap includes:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/3a5a9c03-6bea-45bf-aef6-39414fd3ab77.png" alt="" style="display:block;margin:0 auto" />

<p>Module 28 — Docker</p>
<p>Containerizing the application.</p>
<p>Module 29 — CI/CD</p>
<p>Building an automated build, test and deployment workflow.</p>
<p>Module 30 — Deployment</p>
<p>Deploying the application to a cloud platform.</p>
<p>Module 31 — Monitoring &amp; Logging</p>
<p>Application monitoring, centralized logging and error tracking.</p>
<p>🎯 Final Thoughts</p>
<p>ShopEase started as a project for learning Java and Spring Boot, but it has gradually become a way for me to understand how different parts of a real software system fit together.</p>
<p>Pagination taught me about handling growing datasets.</p>
<p>Search and filtering taught me about making large datasets useful.</p>
<p>Image upload introduced another type of data that a backend application needs to handle.</p>
<p>The next goal is to take the same approach toward testing, documentation, containerization, CI/CD and deployment.</p>
<p>The goal isn't just to keep adding features. The goal is to keep making the application better engineered.</p>
<p>#<code>Java</code> #<code>Spring Boot</code> #<code>Spring Data #JPA</code> #<code>Hibernate</code> #<code>MySQL</code> #<code>REST API</code> #<code>Backend Development</code> #<code>E-Commerce</code> #<code>Pagination</code> #<code>Search</code> #<code>Filtering</code> #<code>Image Upload</code> #<code>Software Development</code></p>
]]></content:encoded></item><item><title><![CDATA[🚀 Building the ShopEase Admin Dashboard — Challenges, Solutions & What I Learned

By Shitanshu Jha
]]></title><description><![CDATA[Built ShopEase’s Admin Dashboard with Spring Boot, JPA, MySQL, inventory alerts, order management, and real-world debugging.]]></description><link>https://shitanshu.hashnode.dev/building-the-shopease-admin-dashboard-challenges-solutions-what-i-learned-by-shitanshu-jha</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/building-the-shopease-admin-dashboard-challenges-solutions-what-i-learned-by-shitanshu-jha</guid><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Wed, 26 Aug 2026 06:39:31 GMT</pubDate><content:encoded><![CDATA[<p>While developing ShopEase, my goal was not just to create a basic e-commerce website. I wanted to build it as a complete application while learning how real-world Java backend systems are designed.</p>
<p>I am currently developing ShopEase as a full-stack e-commerce application using Java, Spring Boot, Spring Data JPA, Hibernate, MySQL, HTML, CSS and JavaScript.</p>
<p>One of the major milestones in this journey was Module 23 — Admin Dashboard.</p>
<p>The dashboard started as a simple admin page, but gradually became a proper management system for products, users, orders, order statuses and inventory.</p>
<p>🛠️ What I Built</p>
<p>The ShopEase Admin Dashboard currently provides:</p>
<p>📊 Dashboard statistics 📦 Product Management 👥 User Management 🛒 Order Management 🔄 Order Status Management 📋 Inventory Management ⚠️ Low Stock Alerts ❌ Out-of-Stock Alerts 🕐 Recent Orders</p>
<p>The dashboard communicates with my Spring Boot backend through REST APIs and retrieves the required data from MySQL using Spring Data JPA.</p>
<p>😵 The Challenges I Faced</p>
<p>Building the dashboard wasn't as straightforward as I initially expected.</p>
<p>The biggest challenges were not creating the HTML cards or buttons. The difficult part was making sure that the frontend, backend, database and business logic were all connected correctly.</p>
<ol>
<li>Calculating Dashboard Statistics</li>
</ol>
<p>Initially, the dashboard needed to display information such as:</p>
<p>Total Products Total Users Total Orders Total Revenue</p>
<p>The challenge was deciding where these values should actually come from.</p>
<p>Instead of calculating everything on the frontend, I implemented the logic on the backend.</p>
<p>For example, product and user counts are retrieved using repository operations:</p>
<p>productRepository.count(); userRepository.count();</p>
<p>Similarly, total revenue required a database query that considers only confirmed orders.</p>
<p>This taught me an important lesson:</p>
<p>Dashboard statistics should come from reliable backend data rather than being calculated or trusted on the client side.</p>
<ol>
<li>Implementing Inventory Alerts</li>
</ol>
<p>One of the more interesting parts was implementing inventory monitoring.</p>
<p>I wanted the dashboard to show:</p>
<p>Low Stock Out of Stock</p>
<p>I implemented repository queries such as:</p>
<p>long lowStockProducts = productRepository.countByStockBetween(1, 10);</p>
<p>long outOfStockProducts = productRepository.countByStock(0);</p>
<p>This allowed the backend to directly determine the inventory status.</p>
<p>The dashboard could then display something like:</p>
<p>⚠️ Low Stock 13</p>
<p>❌ Out of Stock 1</p>
<p>This was a good example of how a simple UI feature actually requires proper backend and database logic behind it.</p>
<ol>
<li>Fetching Recent Orders</li>
</ol>
<p>Another requirement was displaying the latest orders on the Admin Dashboard.</p>
<p>Instead of returning every order and filtering it on the frontend, I created a repository-level query to retrieve recent orders.</p>
<p>The backend then converts the Order entities into a dedicated DTO:</p>
<p>AdminRecentOrderDTO</p>
<p>The response contains information such as:</p>
<p>Order ID Customer name Total amount Order status Order creation time</p>
<p>This helped me understand why DTOs are useful when exposing database entities through APIs.</p>
<ol>
<li>Connecting Everything Through a Dashboard Service</li>
</ol>
<p>I didn't want the controller to contain all the dashboard business logic.</p>
<p>So I created:</p>
<p>AdminDashboardService</p>
<p>The service is responsible for collecting:</p>
<p>Products Users Orders Revenue Inventory Recent Orders</p>
<p>and combining them into:</p>
<p>AdminDashboardResponseDTO</p>
<p>This gave me a much cleaner architecture:</p>
<p>Frontend ↓ Controller ↓ Service ↓ Repository ↓ MySQL</p>
<p>Working through this helped me understand the importance of separating responsibilities instead of putting everything inside one class.</p>
<p>🐛 Debugging Was a Major Part of the Work</p>
<p>One thing I learned while developing ShopEase is that writing code is only half the job.</p>
<p>A lot of my time went into debugging.</p>
<p>For example, I encountered issues where repository methods were not recognized correctly, constructors didn't match DTO parameters, and entity fields differed from what the service expected.</p>
<p>There were also situations where the dashboard displayed incorrect inventory numbers.</p>
<p>Instead of randomly changing code, I started checking the complete flow:</p>
<p>Database ↓ Repository ↓ Service ↓ DTO ↓ Controller ↓ API Response ↓ Frontend</p>
<p>That approach made debugging much easier.</p>
<p>📊 Testing the Dashboard</p>
<p>After implementing the features, I tested the dashboard with actual application data.</p>
<p>For example, the backend returned dashboard information similar to:</p>
<p>Total Products: 58 Total Users: 9 Total Orders: 21 Total Revenue: 7798 Low Stock Products: 13 Out of Stock Products: 1</p>
<p>I also tested inventory changes by changing product stock values in the database and verifying that the dashboard updated accordingly.</p>
<p>This helped confirm that the values weren't hardcoded and were actually coming from the database.</p>
<p>🔐 Admin-Specific Functionality</p>
<p>Another important part was making sure that administrative functionality was separated from normal user functionality.</p>
<p>ShopEase already has role-based behavior for:</p>
<p>USER ADMIN</p>
<p>The Admin Dashboard is therefore intended for administrative operations such as:</p>
<p>Managing products Managing users Managing orders Updating order status Monitoring inventory</p>
<p>This made me think more seriously about authorization and access control, rather than treating an admin page as just another frontend page.</p>
<p>🚀 From Admin Page to Production-Ready Module</p>
<p>The biggest takeaway from this module is that a dashboard isn't just a collection of cards.</p>
<p>A proper admin dashboard requires:</p>
<p>UI + REST APIs + Business Logic + Database Queries + DTOs + Validation + Authorization + Error Handling + Testing</p>
<p>That is what made this module much more valuable for me than simply designing an admin interface.</p>
<p>🧠 What I Learned</p>
<p>While working on this module, I learned and practiced:</p>
<p>Spring Boot service-layer architecture Spring Data JPA repository queries DTO-based API responses Database aggregation Inventory management logic Order management Role-based functionality Debugging backend/frontend integration API testing Separating business logic from controllers Designing features around actual application data</p>
<p>More importantly, I learned that production-ready development is mostly about handling the problems that appear between different parts of the system.</p>
<p>👨‍💻 About Me</p>
<p>I'm Shitanshu Jha, a BCA student and developer currently building ShopEase as a hands-on project to strengthen my skills in Java backend and full-stack development.</p>
<p>Instead of only learning technologies theoretically, I'm trying to understand them by building a complete application step by step.</p>
<p>With ShopEase, I'm working with:</p>
<p>Java Spring Boot Spring MVC Spring Data JPA Hibernate MySQL REST APIs JWT Spring Security HTML CSS JavaScript Git &amp; GitHub Postman</p>
<p>ShopEase is still an ongoing project, and I'm continuing to improve it module by module.</p>
<p>The objective isn't simply to finish another college project.</p>
<p>I'm using ShopEase to understand how real software is designed, debugged, tested and gradually made production-ready.</p>
<p>🚀 What's Next?</p>
<p>With the Admin Dashboard completed, ShopEase has moved into Phase 6 — Production Ready.</p>
<p>The next focus is improving the application's production-level capabilities, including:</p>
<p>Logging Better error tracking Reliability Security improvements Performance Deployment considerations</p>
<p>There is still a lot to build, but every module is teaching me something that a tutorial alone couldn't.</p>
<p>ShopEase is not finished yet — and that's the point.</p>
<p>#java #springboot #backend #webdev #mysql</p>
]]></content:encoded></item><item><title><![CDATA[Who is Shitanshu Jha?
]]></title><description><![CDATA[Shitanshu Jha, a BCA student and Java Backend Developer building real-world applications with Java, Spring Boot, Spring Security, REST APIs, MySQL]]></description><link>https://shitanshu.hashnode.dev/who-is-shitanshu-jha</link><guid isPermaLink="true">https://shitanshu.hashnode.dev/who-is-shitanshu-jha</guid><dc:creator><![CDATA[Shitanshu Jha]]></dc:creator><pubDate>Wed, 26 Aug 2026 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a8501ae552e968823a1653d/6e786be3-2223-47f0-9d65-0140126cd9ae.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>By Shitanshu Jha · 5 min read</strong></p>
<p>I’m Shitanshu Jha, a BCA student at Guru Gobind Singh Indraprastha University with a growing focus on <strong>Java backend development</strong>.</p>
<p>My journey started with learning programming fundamentals and gradually moved toward building real applications. Instead of limiting myself to tutorials and small examples, I wanted to understand how the different pieces of a real application actually work together.</p>
<p>That led me to my biggest project so far — <strong>ShopEase</strong>.</p>
<h2>From Learning Java to Building Real Applications</h2>
<p>Java became the foundation of my backend development journey.</p>
<p>As I progressed, I moved from Core Java and OOP into technologies such as <strong>JDBC, Spring Boot, Spring MVC, Spring Data JPA, Hibernate, REST APIs, Spring Security and MySQL</strong>.</p>
<p>The important part wasn't simply learning these technologies individually.</p>
<p>It was learning how they fit together.</p>
<p>A request from a frontend application can travel through a REST controller, reach the service layer, interact with the database through JPA/Hibernate, pass through security mechanisms, and finally return a structured response to the client.</p>
<p>Understanding that flow changed the way I approached backend development.</p>
<h2>Building ShopEase</h2>
<p>ShopEase is my full-stack e-commerce application built using:</p>
<p><strong>Frontend → HTML, CSS, JavaScript, Fetch API</strong></p>
<p><strong>Backend → Java, Spring Boot, Spring MVC, Spring Data JPA, Hibernate</strong></p>
<p><strong>Security → Spring Security, JWT, BCrypt</strong></p>
<p><strong>Database → MySQL</strong></p>
<p><strong>Payments → Razorpay</strong></p>
<p>The architecture follows a simple but practical flow:</p>
<p><strong>Frontend → Fetch API → Spring Boot REST API → Spring Security + JWT → Service/Repository Layer → MySQL</strong></p>
<p>Building this application helped me move beyond isolated coding exercises and start thinking about application architecture, security, validation, database relationships and business logic.</p>
<h2>What I Have Built</h2>
<p>ShopEase has grown from a basic product application into a much larger e-commerce system.</p>
<p>The application currently includes:</p>
<ul>
<li><p>User signup and login</p>
</li>
<li><p>JWT authentication</p>
</li>
<li><p>BCrypt password hashing</p>
</li>
<li><p>Role-based authorization</p>
</li>
<li><p>Product management</p>
</li>
<li><p>Product search and categories</p>
</li>
<li><p>Product details and specifications</p>
</li>
<li><p>Similar products</p>
</li>
<li><p>Shopping cart and persistent cart</p>
</li>
<li><p>Wishlist</p>
</li>
<li><p>Checkout and shipping address</p>
</li>
<li><p>Orders and order history</p>
</li>
<li><p>Order status management</p>
</li>
<li><p>Product feedback and moderation</p>
</li>
<li><p>Razorpay payments</p>
</li>
<li><p>Payment verification and signature validation</p>
</li>
<li><p>Admin dashboard</p>
</li>
<li><p>User, product and order management</p>
</li>
<li><p>Inventory management</p>
</li>
<li><p>Low-stock and out-of-stock alerts</p>
</li>
<li><p>Application logging</p>
</li>
<li><p>Standard API responses</p>
</li>
<li><p>DTO-based validation and exception handling</p>
</li>
</ul>
<p>The project has also given me practical experience with tools such as <strong>Git, GitHub, Eclipse, Postman and XAMPP</strong>.</p>
<h2>What I Learned From Building It</h2>
<p>The biggest lesson hasn't been learning another framework.</p>
<p>It has been learning <strong>how different parts of software interact</strong>.</p>
<p>For example, implementing authentication taught me about JWTs and Spring Security. Building the cart and order systems forced me to think about relationships between entities and persistent data. Razorpay integration introduced payment verification and security concerns.</p>
<p>Admin functionality made me think beyond the customer's experience and consider inventory, order management and authorization.</p>
<p>Logging also showed me why knowing that an application failed isn't enough — developers need useful information about <strong>where and why</strong> it failed.</p>
<h2>My Current Focus</h2>
<p>I am now moving from feature development toward <strong>production hardening</strong>.</p>
<p>My next areas of focus are:</p>
<ul>
<li><p>Automated testing</p>
</li>
<li><p>API documentation with OpenAPI/Swagger</p>
</li>
<li><p>Pagination and sorting</p>
</li>
<li><p>Advanced search</p>
</li>
<li><p>Image upload</p>
</li>
<li><p>Docker</p>
</li>
<li><p>CI/CD</p>
</li>
<li><p>Deployment</p>
</li>
<li><p>Cloud deployment</p>
</li>
</ul>
<p>The goal is no longer just to make an application that works.</p>
<p>The goal is to understand how to make an application that is <strong>testable, maintainable, secure, deployable and closer to production standards</strong>.</p>
<h2>What's Next?</h2>
<p>I am still at the beginning of my career, and I know there is a lot more to learn.</p>
<p>My current direction is clear:</p>
<p><strong>Java → Spring Boot → Backend Engineering → Production Systems</strong></p>
<p>I want to keep building projects, solving problems, improving my understanding of backend architecture and gradually working with larger and more complex systems.</p>
<p>ShopEase is not the final destination.</p>
<p>It is the project through which I am learning how to become a better software developer.</p>
<p>#Shitanshu Jha #ShopEase #ShopEaseShitanshuJha #Backend #Software Engineer #SpringBoot #Java</p>
]]></content:encoded></item></channel></rss>