Skip to content

Simplified Learning Blog

Learning made easy

  • Home
  • Java
    • Core Java Tutorial
    • Java 8
    • What is Rest API in java
    • Spring Framework
    • Type Casting in Java | 2 types Implicit and explicit casting
    • JUnit 5 Tutorial
      • Assertall in JUnit 5
      • Assertions in JUnit 5
  • Java Interview Questions
    • Top 50 Core Java Interview Questions & Answers (2026 Edition)
    • Top 20 Spring Boot Interview Questions for Freshers (2026 Edition): The Ultimate Cheat Sheet
    • Top 40+ Multithreading interview questions
    • Top 10 AWS Lambda interview questions
  • Java Thread Tutorials
    • How to create thread in Java
    • Multithreading in java
    • Daemon Thread in Java | How to create daemon thread in java
    • Java Virtual Threads (Project Loom) in Real Enterprise Applications
    • WebFlux vs. Virtual Threads in 2026: The Senior Architect’s Decision Matrix
  • AWS
    • What is AWS (Amazon Web Services)
    • AWS IAM (Identity and Access Management)
    • AWS SNS | What is SNS
    • What is SQS | AWS SQS (Simple Queue Service)
    • What is AWS Lambda
  • Software Architecture
    • Software Architecture Performance
    • Performance Principles of Software Architecture
    • Practical System Design Examples using Spring Boot, Queues, and Caches (2026 Guide)
    • System Performance Objective
  • Spring Boot Tutorial
    • Spring Boot Rest API Example complete guide
    • Spring MVC vs. Spring Boot in 2026: The Senior Architect’s Definitive Guide
    • Spring Boot Application.properties vs. YAML: The 2026 Architect’s Verdict
  • Core Java Deep Dives
    • Java int to String Conversion: Performance Benchmarks & Memory Pitfalls
    • String to Integer Conversion in Java | Java convert string to int
    • Converting PDF to JSON in Java Top 3 ways to code:
    • Calculate date of birth from age in jquery
    • How to convert excel to PDF using java
    • jcalendar in java swing example
    • Series program in java
  • Tools
    • JSON Formatter & Debugging Guide for Spring Boot Developers
    • Free Character Counter Tool: The Ultimate Guide to Counting Characters, Words, and Text Statistics
  • Tech Blogs
    • Java 21 New Features
    • Is Java Dead? Is java dead, 2023 ?
    • New Features in Java 17
  • Toggle search form

Practical System Design Examples using Spring Boot, Queues, and Caches (2026 Guide)

Posted on January 10, 2026January 14, 2026 By Govind No Comments on Practical System Design Examples using Spring Boot, Queues, and Caches (2026 Guide)

From CRUD to Architecture | Spring Boot System Design Examples

As a Senior Developer, your value isn’t just writing @RestController. It’s knowing how to prevent that controller from crashing when traffic spikes by 500%.

Table of Contents

Toggle
  • From CRUD to Architecture | Spring Boot System Design Examples
  • Pattern 1: The Asynchronous Decoupler (The “Email Service” Problem)
    • Implementation (Spring Boot 3.4 + RabbitMQ)
  • Pattern 2: The Distributed Throttler (The “API Limit” Problem)
    • Implementation (Spring Boot + Redis)
  • Pattern 3: The Look-Aside Cache (The “Hot Data” Problem)
    • Implementation (Spring Cache Abstraction)
  • FAQ – Senior Design Edition
  • Conclusion: Design for Failure

In 2026, “System Design” isn’t just for whiteboard interviews at FAANG. It’s about how you glue Spring Boot together with infrastructure components like RabbitMQ and Redis to build resilient applications.

This guide breaks down three practical, production-grade patterns you can implement today. We move beyond “Hello World” to solve real scalability problems.

Pattern 1: The Asynchronous Decoupler (The “Email Service” Problem)

The Problem: A user registers (POST /users). You need to send a Welcome Email.

  • Naive Approach: You call emailService.send() inside the controller.
  • The Failure Mode: The SMTP server hangs for 5 seconds. The user’s registration request hangs for 5 seconds. If 1000 users register, your Tomcat thread pool is exhausted, and the site goes down.

The Solution: Decouple the “Core Domain” (User Registration) from the “Side Effect” (Email) using a Message Queue (RabbitMQ or Kafka).

Implementation (Spring Boot 3.4 + RabbitMQ)

1. The Producer (Controller) Instead of sending the email, we publish an event. The API responds instantly (201 Created).

@RestController
@RequestMapping("/api/users")
public class UserRegistrationController {

    private final StreamBridge streamBridge; // Spring Cloud Stream

    @PostMapping
    public ResponseEntity<String> register(@RequestBody UserDto user) {
        // 1. Save User to DB (Core Logic)
        userService.save(user);

        // 2. Publish Event (Fire & Forget)
        UserRegisteredEvent event = new UserRegisteredEvent(user.getEmail(), user.getName());
        streamBridge.send("email-out-0", event);

        return ResponseEntity.status(HttpStatus.CREATED).body("User registered successfully");
    }
}

2. The Consumer (Worker Service) This runs in a separate process or thread. If the Email server is down, the message stays in the queue. No data is lost, and the user is never blocked.

@Component
public class EmailWorker {

    @Bean
    public Consumer<UserRegisteredEvent> sendWelcomeEmail() {
        return event -> {
            try {
                // Simulate slow 3rd party call
                emailProvider.send(event.email(), "Welcome!");
            } catch (Exception e) {
                // Message automatically requeued or sent to DLQ (Dead Letter Queue)
                throw new AmqpRejectAndDontRequeueException(e);
            }
        };
    }
}

Pattern 2: The Distributed Throttler (The “API Limit” Problem)

The Problem: You have a public API. One customer writes a script that spams your endpoint 10,000 times a second. Your database CPU hits 100%, taking down the site for everyone.

The Solution: Implement a Token Bucket Rate Limiter using Redis. We check the limit before doing any work.

Implementation (Spring Boot + Redis)

We create a custom Aspect (@RateLimited) to protect specific endpoints.

1. The Aspect (The Gatekeeper)

@Aspect
@Component
public class RateLimitAspect {

    private final StringRedisTemplate redisTemplate;

    @Around("@annotation(rateLimited)")
    public Object checkLimit(ProceedingJoinPoint joinPoint, RateLimited rateLimited) throws Throwable {
        String apiKey = getApiKey(); // Extract from header
        String key = "rate_limit:" + apiKey;
        
        // Atomic Increment in Redis
        Long count = redisTemplate.opsForValue().increment(key);

        if (count == 1) {
            // Set expiration window (e.g., 1 minute)
            redisTemplate.expire(key, 60, TimeUnit.SECONDS);
        }

        if (count > rateLimited.maxRequests()) {
            throw new TooManyRequestsException("Limit exceeded. Try again later.");
        }

        return joinPoint.proceed();
    }
}

2. The Usage Now, protecting an expensive endpoint is trivial.

@RateLimited(maxRequests = 100) // 100 req/min
@GetMapping("/expensive-report")
public Report generateReport() {
    return reportService.crunchBigData();
}

Pattern 3: The Look-Aside Cache (The “Hot Data” Problem)

The Problem: Your e-commerce site has a “Product Details” page. During a flash sale, 50,000 people view the same iPhone page.

  • Naive Approach: Every request hits the SQL database.
  • The Failure Mode: The DB creates 50k connections. The connection pool exhausts. The site crashes.

The Solution: Look-Aside Caching.

  1. Check Redis.
  2. If found (Hit) -> Return.
  3. If missing (Miss) -> Query DB -> Save to Redis -> Return.

Implementation (Spring Cache Abstraction)

Spring Boot makes this declarative via @Cacheable.

@Service
public class ProductService {

    @Cacheable(value = "products", key = "#id", unless = "#result == null")
    public Product getProduct(String id) {
        // This line only executes if Redis does NOT have the data
        System.out.println("Fetching from Database..."); 
        return productRepository.findById(id).orElse(null);
    }

    // Critical: Cache Eviction
    @CacheEvict(value = "products", key = "#product.id")
    public void updateProduct(Product product) {
        productRepository.save(product);
        // Next 'get' will re-fetch from DB automatically
    }
}

Architectural Warning (Cache Stampede): In 2026, the standard @Cacheable isn’t enough for high concurrency. If the cache expires and 10,000 requests hit simultaneously, they will all go to the DB (The “Thundering Herd” problem).

  • Fix: Enable “Sync” locking in Spring: @Cacheable(value = "products", sync = true) This ensures only one thread queries the DB; the other 9,999 wait for that one thread to populate the cache.

FAQ – Senior Design Edition

Q: Why use RabbitMQ instead of Java’s CompletableFuture? A: CompletableFuture is in-memory. If your server restarts, the pending tasks are lost forever. RabbitMQ persists messages to disk. If the consumer crashes, the message remains in the queue and is retried later.

Q: Should I use Redis or Caffeine for caching? A:

  • Caffeine: Local (In-Memory). Fast (nanoseconds). Good for single-instance monoliths.
  • Redis: Distributed. Slower (milliseconds). Required for Microservices where multiple instances need to see the same cache state.

Q: How do I handle “Dead Letters” in RabbitMQ? A: Always configure a DLQ (Dead Letter Queue). If a message fails processing 3 times (e.g., due to a bug), move it to a generic error-queue. Do not let it block the main queue indefinitely.

Conclusion: Design for Failure

Practical system design is about assuming things will break.

  • The Email server will time out -> Use Queues.
  • The Database will get overloaded -> Use Caches.
  • The Users will abuse the API -> Use Rate Limiters.

By applying these three patterns in Spring Boot, you transform fragile code into a resilient system capable of handling the unexpected.

Govind

For over 15 years, I have worked as a hands-on Java Architect and Senior Engineer, specializing in building and scaling high-performance, enterprise-level applications. My career has been focused primarily within the FinTech, Telecommunications, or E-commerce sector, where I’ve led teams in designing systems that handle millions of transactions per day.

Checkout my profile here : AUTHOR https://simplifiedlearningblog.com/author/

Related

Software Architecture Tags:Spring Boot System Design Examples

Post navigation

Previous Post: Spring Boot Application.properties vs. YAML: The 2026 Architect’s Verdict
Next Post: How to Handle Errors in Spring Boot REST APIs (2026 Guide): The Global Exception Handling Pattern

More Related Articles

Performance Principles of Software Architecture Software Architecture
Software Architecture Performance Software Architecture
System Performance Objective Software Architecture

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recent Posts

  • How to Handle Errors in Spring Boot REST APIs (2026 Guide): The Global Exception Handling Pattern
  • Practical System Design Examples using Spring Boot, Queues, and Caches (2026 Guide)
  • Spring Boot Application.properties vs. YAML: The 2026 Architect’s Verdict
  • Top 20 Spring Boot Interview Questions for Freshers (2026 Edition): The Ultimate Cheat Sheet
  • Spring MVC vs. Spring Boot in 2026: The Senior Architect’s Definitive Guide

Recent Comments

  1. Govind on Performance Principles of Software Architecture
  2. Gajanan Pise on Performance Principles of Software Architecture
Simplified Learning

Demystifying complex enterprise architecture for senior engineers. Practical guides on Java, Spring Boot, and Cloud Native systems.

Explore

  • Home
  • About Us
  • Author Profile: Govind
  • Contact Us

Legal

  • Privacy Policy
  • Terms and Conditions
  • Disclaimer
© 2026 Simplified Learning Blog. All rights reserved.
We use cookies to improve your experience and personalize ads. By continuing, you agree to our Privacy Policy and use of cookies.

Powered by PressBook Green WordPress theme