Full-Stack Architecture Patterns

Master architecture patterns for building scalable full-stack applications.

intermediate Backend Development 6 hours

Chapter 9: Caching Strategies

Chapter 9 of 15

Chapter 9: Caching Strategies

9.1 Caching Layers

Caching stores frequently accessed data in fast storage to improve performance. Multiple caching layers work together to optimize application speed.

Browser Caching:

  • Stores static assets (CSS, JS, images) in browser
  • Reduces server requests
  • Controlled by Cache-Control headers
  • Fastest access but limited to user's browser
// Cache-Control headers
Cache-Control: public, max-age=31536000  // Cache for 1 year
Cache-Control: no-cache                  // Revalidate each time
Cache-Control: no-store                  // Don't cache

CDN Caching:

  • Content Delivery Network caches content at edge locations
  • Serves content from nearest location
  • Reduces latency globally
  • Good for static assets and API responses

Application Caching:

  • In-memory cache (Redis, Memcached)
  • Stores computed results and database queries
  • Very fast access
  • Shared across application instances
// Redis caching example
const redis = require('redis');
const client = redis.createClient();

// Cache data
await client.setex('user:123', 3600, JSON.stringify(userData));

// Retrieve from cache
const cached = await client.get('user:123');
if (cached) {
    return JSON.parse(cached);
}
// Otherwise fetch from database

Database Caching:

  • Query result caching
  • Reduces database load
  • Faster repeated queries
  • Can be at database level or application level

9.2 Cache Invalidation

Cache invalidation ensures cached data stays current. Choose strategies based on data characteristics.

Time-Based Expiration (TTL):

  • Cache expires after set time
  • Simple to implement
  • May serve stale data until expiration
  • Good for data that changes infrequently
// Set TTL
await cache.set('key', 'value', 'EX', 3600); // Expires in 1 hour

Event-Based Invalidation:

  • Invalidate cache when data changes
  • Ensures fresh data
  • More complex to implement
  • Requires tracking data changes
// Invalidate on update
async function updateUser(id, data) {
    await db.update('users', id, data);
    await cache.del(`user:${id}`); // Remove from cache
}

Cache-Aside Pattern:

  • Application checks cache first
  • If miss, fetch from database and cache
  • Application manages cache
  • Common pattern

Write-Through Pattern:

  • Write to cache and database simultaneously
  • Cache always up to date
  • Slower writes
  • Good for critical data

9.3 Caching Best Practices

Follow best practices for effective caching.

  • Cache expensive operations (database queries, API calls)
  • Use appropriate TTL values
  • Implement cache warming for critical data
  • Monitor cache hit rates
  • Handle cache failures gracefully
  • Use cache keys consistently
  • Consider cache size limits