Spring Boot patterns for REST API design, layered controller-service-repository structure, Spring Data JPA, caching, async processing, and logging. USE WHEN building Java Spring Boot backends, structuring services, configuring data access or caching, or adding validation, exception handling, or event-driven patterns.
Spring Boot patterns for REST API design, layered controller-service-repository structure, Spring Data JPA, caching, async processing, and logging. USE WHEN building Java Spring Boot backends, structuring services, configuring data access or caching, or adding validation, exception handling, or event-driven patterns.
cluster
jvm
version
1.0.0
Spring Boot Development Patterns
Spring Boot architecture and API patterns for scalable, production-grade services.
When to Activate
Building REST APIs with Spring MVC or WebFlux
Structuring controller → service → repository layers
Configuring Spring Data JPA, caching, or async processing
Adding validation, exception handling, or pagination
Setting up profiles for dev/staging/production environments
Implementing event-driven patterns with Spring Events or Kafka
publicinterfaceMarketRepositoryextendsJpaRepository<MarketEntity, Long> {
@Query("select m from MarketEntity m where m.status = :status order by m.volume desc")
List<MarketEntity> findActive(@Param("status") MarketStatus status, Pageable pageable);
}
public <T> T withRetry(Supplier<T> supplier, int maxRetries) {
intattempts=0;
while (true) {
try {
return supplier.get();
} catch (Exception ex) {
attempts++;
if (attempts >= maxRetries) {
throw ex;
}
try {
Thread.sleep((long) Math.pow(2, attempts) * 100L);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw ex;
}
}
}
}
Rate Limiting (Filter + Bucket4j)
Security Note: The X-Forwarded-For header is untrusted by default because clients can spoof it.
Only use forwarded headers when:
Your app is behind a trusted reverse proxy (nginx, AWS ALB, etc.)
You have registered ForwardedHeaderFilter as a bean
You have configured server.forward-headers-strategy=NATIVE or FRAMEWORK in application properties
Your proxy is configured to overwrite (not append to) the X-Forwarded-For header
When ForwardedHeaderFilter is properly configured, request.getRemoteAddr() will automatically
return the correct client IP from the forwarded headers. Without this configuration, use
request.getRemoteAddr() directly—it returns the immediate connection IP, which is the only
trustworthy value.
@ComponentpublicclassRateLimitFilterextendsOncePerRequestFilter {
privatefinal Map<String, Bucket> buckets = newConcurrentHashMap<>();
/*
* SECURITY: This filter uses request.getRemoteAddr() to identify clients for rate limiting.
*
* If your application is behind a reverse proxy (nginx, AWS ALB, etc.), you MUST configure
* Spring to handle forwarded headers properly for accurate client IP detection:
*
* 1. Set server.forward-headers-strategy=NATIVE (for cloud platforms) or FRAMEWORK in
* application.properties/yaml
* 2. If using FRAMEWORK strategy, register ForwardedHeaderFilter:
*
* @Bean
* ForwardedHeaderFilter forwardedHeaderFilter() {
* return new ForwardedHeaderFilter();
* }
*
* 3. Ensure your proxy overwrites (not appends) the X-Forwarded-For header to prevent spoofing
* 4. Configure server.tomcat.remoteip.trusted-proxies or equivalent for your container
*
* Without this configuration, request.getRemoteAddr() returns the proxy IP, not the client IP.
* Do NOT read X-Forwarded-For directly—it is trivially spoofable without trusted proxy handling.
*/@OverrideprotectedvoiddoFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain)throws ServletException, IOException {
// Use getRemoteAddr() which returns the correct client IP when ForwardedHeaderFilter// is configured, or the direct connection IP otherwise. Never trust X-Forwarded-For// headers directly without proper proxy configuration.StringclientIp= request.getRemoteAddr();
Bucketbucket= buckets.computeIfAbsent(clientIp,
k -> Bucket.builder()
.addLimit(Bandwidth.classic(100, Refill.greedy(100, Duration.ofMinutes(1))))
.build());
if (bucket.tryConsume(1)) {
filterChain.doFilter(request, response);
} else {
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
}
}
}
Background Jobs
Use Spring’s @Scheduled or integrate with queues (e.g., Kafka, SQS, RabbitMQ). Keep handlers idempotent and observable.
Observability
Structured logging (JSON) via Logback encoder
Metrics: Micrometer + Prometheus/OTel
Tracing: Micrometer Tracing with OpenTelemetry or Brave backend
Production Defaults
Prefer constructor injection, avoid field injection
Enable spring.mvc.problemdetails.enabled=true for RFC 7807 errors (Spring Boot 3+)
Configure HikariCP pool sizes for workload, set timeouts
Use @Transactional(readOnly = true) for queries
Enforce null-safety via @NonNull and Optional where appropriate
Remember: Keep controllers thin, services focused, repositories simple, and errors handled centrally. Optimize for maintainability and testability.