Back to challenges
Challenge

Reducing slow backend responses

In an enterprise application, some operations began showing high response times. Optimizations were applied to queries, response depth, HTTP caching and internal caching.

JavaSpring BootSQLHibernateSpring CacheCaffeine
Query Optimization with Cache Layers Client HTTP Request HTTP Cache ShallowETag · Last-Modified · Cache-Control 304 Not Modified / 200 OK Request REST API (Controller) @Cacheable lookup Internal Cache (Spring @Cacheable) Caffeine · TTL · @CacheEvict on write hit → return / miss → proceed Service call Service Optimized queries Optimized Data Access JOIN FETCH / EntityGraph DTO Projections No N+1 Database

Context

An enterprise application had several operations that began showing high response times. The issue was related to the amount of data being retrieved and processed for each request.

Problem

Certain backend operations were taking too long to respond, affecting user experience and system performance. The operations involved multiple database queries and large result sets.

Approach

I analyzed individual timing of each operation to identify bottlenecks. The analysis covered:

  • Database query profiling (identifying N+1 problems)
  • Data access pattern review
  • Response payload size analysis
  • Cacheability assessment for read-heavy endpoints

Solution

1. Query Optimization & N+1 Prevention

Replaced lazy-loading collections with @EntityGraph and JOIN FETCH to eliminate N+1 queries. Added batch fetching for collections where JOIN FETCH wasn’t feasible. Optimized query structure to fetch only required columns.

2. Response Depth Reduction

Introduced DTO projections to limit serialization depth. Removed unnecessary nested relationships from API responses. Implemented field selection for list endpoints vs detail endpoints.

3. HTTP Caching (ShallowETag & Last-Modified)

Added ShallowEtagHeaderFilter for automatic ETag generation on responses. Implemented Last-Modified headers via WebRequest inspection. Configured Cache-Control headers per endpoint (public/private, max-age).

4. Internal Caching (Spring Cache)

Added @Cacheable on repository methods for frequently-read reference data. Cached processed/aggregated results in service layer with TTL-based eviction. Used @CacheEvict on write operations to maintain consistency. Configured Caffeine as cache provider.

Result

The combined optimizations significantly reduced response times:

  • Query optimization eliminated N+1, reducing DB roundtrips
  • Response depth reduction cut payload size
  • HTTP cache enabled 304 responses for unchanged resources
  • Internal cache reduced DB load for hot reference data