🚀 Oferta especial: 60% OFF no CrazyStack - Últimas vagas!Garantir vaga →
Módulo 3 - Aula 3

Otimização e Cache

Domine técnicas avançadas de otimização e cache no tRPC: React Query, invalidação inteligente, cache distribuído e performance para SaaS de alto tráfego.

90 min
Avançado
Performance

🎯 Por que otimização e cache são críticos em SaaS?

Performance = Conversão: Cada segundo adicional de carregamento reduz conversões em 20% e afeta diretamente a receita.

Escalabilidade: Cache inteligente permite suportar milhares de usuários simultâneos sem degradar performance.

⚡ React Query Avançado

src/lib/trpc/client.ts
// 📁 src/lib/trpc/client.ts
import { createTRPCReact } from '@trpc/react-query';
import { QueryClient } from '@tanstack/react-query';
import { persistQueryClient } from '@tanstack/react-query-persist-client-core';
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister';
import { compress, decompress } from 'lz-string';
import type { AppRouter } from '@/server/trpc/router';

export const trpc = createTRPCReact<AppRouter>();

// 🔧 Configuração avançada do QueryClient
export const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      // 🚀 Configurações de performance
      staleTime: 5 * 60 * 1000, // 5 minutos
      cacheTime: 30 * 60 * 1000, // 30 minutos
      retry: (failureCount, error) => {
        // 🔄 Retry inteligente baseado no tipo de erro
        if (error?.data?.httpStatus === 404) return false;
        if (error?.data?.httpStatus === 403) return false;
        if (error?.data?.httpStatus >= 500) return failureCount < 3;
        return failureCount < 1;
      },
      retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
      
      // 📊 Configurações de refetch
      refetchOnWindowFocus: false,
      refetchOnMount: true,
      refetchOnReconnect: true,
      
      // 🎯 Configurações de background updates
      refetchInterval: (data, query) => {
        // 🔄 Refetch baseado no tipo de dados
        if (query.queryKey[0] === 'dashboard') return 30 * 1000; // 30s
        if (query.queryKey[0] === 'analytics') return 60 * 1000; // 1min
        if (query.queryKey[0] === 'user') return 5 * 60 * 1000; // 5min
        return false; // Não refetch automático
      },
      
      // 🎨 Configurações de placeholder
      placeholderData: (previousData) => previousData,
      
      // 📱 Configurações de suspense
      suspense: false,
      useErrorBoundary: (error) => {
        // 🚨 Enviar apenas erros críticos para error boundary
        return error?.data?.httpStatus >= 500;
      },
    },
    mutations: {
      // 🔄 Configurações de retry para mutations
      retry: (failureCount, error) => {
        // 💡 Só retry em erros de rede/servidor
        if (error?.data?.httpStatus >= 500) return failureCount < 2;
        return false;
      },
      
      // 🎯 Configurações de optimistic updates
      onMutate: async (variables) => {
        // 🔄 Cancelar queries relacionadas
        await queryClient.cancelQueries();
        
        // 📊 Snapshot do estado anterior
        const previousData = queryClient.getQueryData(['related-data']);
        
        return { previousData };
      },
      
      onError: (error, variables, context) => {
        // 🔄 Rollback em caso de erro
        if (context?.previousData) {
          queryClient.setQueryData(['related-data'], context.previousData);
        }
        
        // 📊 Log de erro
        console.error('Mutation error:', error);
      },
      
      onSettled: () => {
        // 🔄 Invalidar queries relacionadas
        queryClient.invalidateQueries(['related-data']);
      },
    },
  },
});

// 💾 Configuração de persistência
const persister = createSyncStoragePersister({
  storage: typeof window !== 'undefined' ? window.localStorage : undefined,
  serialize: (data) => compress(JSON.stringify(data)),
  deserialize: (data) => JSON.parse(decompress(data)),
  key: 'trpc-cache-v1',
});

// 🔧 Inicializar persistência
if (typeof window !== 'undefined') {
  persistQueryClient({
    queryClient,
    persister,
    maxAge: 24 * 60 * 60 * 1000, // 24 horas
    buster: 'v1.0.0', // Versão do cache
  });
}

// 🎯 Hooks customizados para otimização
export function useOptimizedQuery<T>(
  queryKey: string[],
  queryFn: () => Promise<T>,
  options: {
    staleTime?: number;
    cacheTime?: number;
    enabled?: boolean;
    suspense?: boolean;
    keepPreviousData?: boolean;
    refetchInterval?: number;
    select?: (data: T) => any;
  } = {}
) {
  return trpc.useQuery(queryKey, queryFn, {
    // 🚀 Configurações de performance específicas
    staleTime: options.staleTime ?? 5 * 60 * 1000,
    cacheTime: options.cacheTime ?? 30 * 60 * 1000,
    keepPreviousData: options.keepPreviousData ?? true,
    
    // 📊 Transformação de dados
    select: options.select,
    
    // 🔄 Configurações de refetch
    refetchInterval: options.refetchInterval,
    
    // 🎯 Configurações de estado
    enabled: options.enabled,
    suspense: options.suspense,
    
    // 🔧 Configurações de retry específicas
    retry: (failureCount, error) => {
      // 💡 Queries críticas têm mais tentativas
      if (queryKey.includes('critical')) return failureCount < 5;
      return failureCount < 2;
    },
    
    // 📱 Configurações de loading
    placeholderData: queryClient.getQueryData(queryKey),
  });
}

// 🔄 Hook para invalidação inteligente
export function useSmartInvalidate() {
  return {
    // 🎯 Invalidar queries específicas
    invalidateQueries: (patterns: string[]) => {
      patterns.forEach(pattern => {
        queryClient.invalidateQueries({
          predicate: (query) => {
            return query.queryKey.some(key => 
              typeof key === 'string' && key.includes(pattern)
            );
          }
        });
      });
    },
    
    // 🔄 Invalidar por tags
    invalidateByTags: (tags: string[]) => {
      tags.forEach(tag => {
        queryClient.invalidateQueries({
          predicate: (query) => {
            const queryTags = query.meta?.tags as string[] || [];
            return queryTags.includes(tag);
          }
        });
      });
    },
    
    // 📊 Invalidar dados relacionados
    invalidateRelated: (entityId: string, entityType: string) => {
      const patterns = [
        `${entityType}.get`,
        `${entityType}.list`,
        `${entityType}.search`,
        `related-${entityType}`,
      ];
      
      patterns.forEach(pattern => {
        queryClient.invalidateQueries({
          predicate: (query) => {
            const queryKey = query.queryKey;
            return queryKey.some(key => 
              typeof key === 'string' && key.includes(pattern)
            ) || (
              typeof queryKey[1] === 'object' && 
              queryKey[1]?.id === entityId
            );
          }
        });
      });
    },
    
    // 🎯 Refetch background queries
    refetchBackground: () => {
      queryClient.refetchQueries({
        type: 'active',
        stale: true,
      });
    },
  };
}

// 📊 Hook para monitoramento de performance
export function useQueryPerformance() {
  return {
    // 🎯 Estatísticas de cache
    getCacheStats: () => {
      const cache = queryClient.getQueryCache();
      const queries = cache.getAll();
      
      return {
        total: queries.length,
        stale: queries.filter(q => q.isStale()).length,
        fresh: queries.filter(q => !q.isStale()).length,
        loading: queries.filter(q => q.isFetching()).length,
        error: queries.filter(q => q.isError()).length,
      };
    },
    
    // 📈 Métricas de performance
    getPerformanceMetrics: () => {
      const cache = queryClient.getQueryCache();
      const queries = cache.getAll();
      
      const metrics = queries.map(query => ({
        queryKey: query.queryKey,
        fetchTime: query.state.dataUpdateCount,
        lastFetch: query.state.dataUpdatedAt,
        errorCount: query.state.errorUpdateCount,
        isStale: query.isStale(),
        isFetching: query.isFetching(),
      }));
      
      return metrics;
    },
    
    // 🧹 Limpeza de cache
    cleanupCache: () => {
      // 🗑️ Remover queries antigas
      queryClient.getQueryCache().clear();
      
      // 🔄 Garbage collection
      queryClient.getQueryCache().forEach((query) => {
        if (query.getObserversCount() === 0) {
          query.destroy();
        }
      });
    },
  };
}

🌐 Cache Distribuído com Redis

src/lib/cache/redis.ts
// 📁 src/lib/cache/redis.ts
import { Redis } from 'ioredis';
import { TRPCError } from '@trpc/server';
import { createHash } from 'crypto';
import { compress, decompress } from 'lz-string';

// 🔧 Configuração do Redis
const redis = new Redis({
  host: process.env.REDIS_HOST || 'localhost',
  port: parseInt(process.env.REDIS_PORT || '6379'),
  password: process.env.REDIS_PASSWORD,
  retryDelayOnFailover: 100,
  maxRetriesPerRequest: 3,
  lazyConnect: true,
  keepAlive: 30000,
  
  // 🚀 Configurações de performance
  enableReadyCheck: true,
  maxLoadingTimeout: 3000,
  
  // 🔄 Configurações de retry
  retryStrategy: (times) => {
    if (times > 3) return null;
    return Math.min(times * 50, 2000);
  },
  
  // 📊 Configurações de conexão
  family: 4,
  connectTimeout: 10000,
  commandTimeout: 5000,
});

// 🎯 Interface para cache
interface CacheOptions {
  ttl?: number; // Time to live em segundos
  compress?: boolean; // Compressão
  tags?: string[]; // Tags para invalidação
  namespace?: string; // Namespace para organização
}

// 🔧 Classe de cache distribuído
export class DistributedCache {
  private defaultTTL = 3600; // 1 hora
  private keyPrefix = 'trpc:cache:';
  
  // 🎯 Gerar chave única
  private generateKey(key: string, namespace?: string): string {
    const hash = createHash('sha256').update(key).digest('hex').slice(0, 16);
    return `${this.keyPrefix}${namespace ? namespace + ':' : ''}${hash}`;
  }
  
  // 💾 Salvar no cache
  async set<T>(
    key: string, 
    value: T, 
    options: CacheOptions = {}
  ): Promise<void> {
    try {
      const cacheKey = this.generateKey(key, options.namespace);
      const ttl = options.ttl || this.defaultTTL;
      
      // 📊 Preparar dados para cache
      const data = {
        value,
        timestamp: Date.now(),
        tags: options.tags || [],
        compressed: options.compress || false,
      };
      
      // 🗜️ Compressão opcional
      let serializedData = JSON.stringify(data);
      if (options.compress) {
        serializedData = compress(serializedData);
      }
      
      // 💾 Salvar no Redis
      await redis.setex(cacheKey, ttl, serializedData);
      
      // 🏷️ Indexar por tags
      if (options.tags && options.tags.length > 0) {
        await this.indexByTags(cacheKey, options.tags, ttl);
      }
      
      console.log(`📦 Cache SET: ${key} (TTL: ${ttl}s)`);
    } catch (error) {
      console.error('❌ Cache SET error:', error);
      // 🚨 Não bloquear aplicação por erro de cache
    }
  }
  
  // 🔍 Buscar do cache
  async get<T>(key: string, namespace?: string): Promise<T | null> {
    try {
      const cacheKey = this.generateKey(key, namespace);
      const cachedData = await redis.get(cacheKey);
      
      if (!cachedData) {
        console.log(`🔍 Cache MISS: ${key}`);
        return null;
      }
      
      // 📊 Deserializar dados
      let data;
      try {
        // 🗜️ Tentar decomprimir primeiro
        const decompressed = decompress(cachedData);
        data = JSON.parse(decompressed || cachedData);
      } catch {
        // 📊 Fallback para dados não comprimidos
        data = JSON.parse(cachedData);
      }
      
      console.log(`✅ Cache HIT: ${key}`);
      return data.value;
    } catch (error) {
      console.error('❌ Cache GET error:', error);
      return null;
    }
  }
  
  // 🗑️ Remover do cache
  async del(key: string, namespace?: string): Promise<void> {
    try {
      const cacheKey = this.generateKey(key, namespace);
      await redis.del(cacheKey);
      console.log(`🗑️ Cache DEL: ${key}`);
    } catch (error) {
      console.error('❌ Cache DEL error:', error);
    }
  }
  
  // 🏷️ Invalidar por tags
  async invalidateByTags(tags: string[]): Promise<void> {
    try {
      const pipeline = redis.pipeline();
      
      for (const tag of tags) {
        const tagKey = `${this.keyPrefix}tags:${tag}`;
        const keys = await redis.smembers(tagKey);
        
        // 🗑️ Remover todas as chaves da tag
        if (keys.length > 0) {
          pipeline.del(...keys);
        }
        
        // 🧹 Limpar a tag
        pipeline.del(tagKey);
      }
      
      await pipeline.exec();
      console.log(`🏷️ Cache invalidated by tags: ${tags.join(', ')}`);
    } catch (error) {
      console.error('❌ Cache invalidation error:', error);
    }
  }
  
  // 🔄 Invalidar por padrão
  async invalidateByPattern(pattern: string): Promise<void> {
    try {
      const keys = await redis.keys(`${this.keyPrefix}${pattern}`);
      
      if (keys.length > 0) {
        await redis.del(...keys);
        console.log(`🔄 Cache invalidated by pattern: ${pattern} (${keys.length} keys)`);
      }
    } catch (error) {
      console.error('❌ Cache pattern invalidation error:', error);
    }
  }
  
  // 📊 Estatísticas do cache
  async getStats(): Promise<{
    totalKeys: number;
    memoryUsage: string;
    hitRate: number;
    missRate: number;
  }> {
    try {
      const info = await redis.info('memory');
      const stats = await redis.info('stats');
      
      const totalKeys = await redis.dbsize();
      const memoryUsage = this.parseMemoryUsage(info);
      const { hitRate, missRate } = this.parseHitRate(stats);
      
      return {
        totalKeys,
        memoryUsage,
        hitRate,
        missRate,
      };
    } catch (error) {
      console.error('❌ Cache stats error:', error);
      return {
        totalKeys: 0,
        memoryUsage: 'N/A',
        hitRate: 0,
        missRate: 0,
      };
    }
  }
  
  // 🧹 Limpeza de cache
  async cleanup(): Promise<void> {
    try {
      // 🗑️ Remover chaves expiradas
      const keys = await redis.keys(`${this.keyPrefix}*`);
      const pipeline = redis.pipeline();
      
      for (const key of keys) {
        const ttl = await redis.ttl(key);
        if (ttl === -1) {
          // 🕰️ Chave sem TTL, aplicar TTL padrão
          pipeline.expire(key, this.defaultTTL);
        }
      }
      
      await pipeline.exec();
      console.log(`🧹 Cache cleanup completed (${keys.length} keys processed)`);
    } catch (error) {
      console.error('❌ Cache cleanup error:', error);
    }
  }
  
  // 🏷️ Indexar por tags (método privado)
  private async indexByTags(key: string, tags: string[], ttl: number): Promise<void> {
    const pipeline = redis.pipeline();
    
    for (const tag of tags) {
      const tagKey = `${this.keyPrefix}tags:${tag}`;
      pipeline.sadd(tagKey, key);
      pipeline.expire(tagKey, ttl);
    }
    
    await pipeline.exec();
  }
  
  // 📊 Parse de uso de memória
  private parseMemoryUsage(info: string): string {
    const match = info.match(/used_memory_human:(.+)/);
    return match ? match[1].trim() : 'N/A';
  }
  
  // 📈 Parse de hit rate
  private parseHitRate(stats: string): { hitRate: number; missRate: number } {
    const hitsMatch = stats.match(/keyspace_hits:(d+)/);
    const missesMatch = stats.match(/keyspace_misses:(d+)/);
    
    if (!hitsMatch || !missesMatch) {
      return { hitRate: 0, missRate: 0 };
    }
    
    const hits = parseInt(hitsMatch[1]);
    const misses = parseInt(missesMatch[1]);
    const total = hits + misses;
    
    if (total === 0) {
      return { hitRate: 0, missRate: 0 };
    }
    
    return {
      hitRate: (hits / total) * 100,
      missRate: (misses / total) * 100,
    };
  }
}

// 🎯 Instância global do cache
export const cache = new DistributedCache();

// 🔧 Decorator para cache automático
export function cached<T extends any[], R>(
  options: CacheOptions & { keyGenerator?: (...args: T) => string } = {}
) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    const originalMethod = descriptor.value;
    
    descriptor.value = async function (...args: T): Promise<R> {
      // 🔑 Gerar chave do cache
      const cacheKey = options.keyGenerator 
        ? options.keyGenerator(...args)
        : `${propertyKey}:${JSON.stringify(args)}`;
      
      // 🔍 Tentar buscar do cache
      const cachedResult = await cache.get<R>(cacheKey, options.namespace);
      if (cachedResult !== null) {
        return cachedResult;
      }
      
      // 🔄 Executar método original
      const result = await originalMethod.apply(this, args);
      
      // 💾 Salvar no cache
      await cache.set(cacheKey, result, options);
      
      return result;
    };
    
    return descriptor;
  };
}

// 🎯 Middleware para cache de tRPC
export function createCacheMiddleware(options: CacheOptions = {}) {
  return async (opts: any) => {
    const { next, type, path, input } = opts;
    
    // 🔍 Apenas cachear queries
    if (type !== 'query') {
      return next();
    }
    
    // 🔑 Gerar chave do cache
    const cacheKey = `${path}:${JSON.stringify(input)}`;
    
    // 🔍 Tentar buscar do cache
    const cachedResult = await cache.get(cacheKey, options.namespace);
    if (cachedResult !== null) {
      return cachedResult;
    }
    
    // 🔄 Executar query
    const result = await next();
    
    // 💾 Salvar no cache
    await cache.set(cacheKey, result, options);
    
    return result;
  };
}

🔄 Invalidação Inteligente

src/server/trpc/middlewares/cache.ts
// 📁 src/server/trpc/middlewares/cache.ts
import { TRPCError } from '@trpc/server';
import { middleware } from '../trpc';
import { cache } from '@/lib/cache/redis';

// 🔧 Middleware para invalidação automática
export const cacheInvalidationMiddleware = middleware(async (opts) => {
  const { next, type, path, input, ctx } = opts;
  
  // 🔄 Executar operação
  const result = await next();
  
  // 🎯 Invalidar cache após mutations
  if (type === 'mutation') {
    await invalidateRelatedCache(path, input, ctx);
  }
  
  return result;
});

// 🎯 Função para invalidação inteligente
async function invalidateRelatedCache(path: string, input: any, ctx: any) {
  const invalidationRules = {
    // 👤 Invalidações relacionadas a usuário
    'user.update': ['user.get', 'user.profile'],
    'user.delete': ['user.*'],
    
    // 📦 Invalidações relacionadas a produtos
    'product.create': ['product.list', 'product.search', 'dashboard.stats'],
    'product.update': ['product.get', 'product.list', 'product.search'],
    'product.delete': ['product.*', 'dashboard.*'],
    
    // 🛒 Invalidações relacionadas a pedidos
    'order.create': ['order.list', 'dashboard.stats', 'analytics.sales'],
    'order.update': ['order.get', 'order.list', 'dashboard.stats'],
    'order.cancel': ['order.*', 'dashboard.*', 'analytics.*'],
    
    // 💾 Invalidações relacionadas a uploads
    'upload.uploadFile': ['upload.getFiles'],
    'upload.deleteFile': ['upload.getFiles', 'dashboard.storage'],
    
    // 📊 Invalidações relacionadas a analytics
    'analytics.track': ['analytics.dashboard', 'analytics.reports'],
  };
  
  // 🔍 Buscar regras de invalidação
  const patterns = invalidationRules[path as keyof typeof invalidationRules] || [];
  
  if (patterns.length === 0) return;
  
  // 🔄 Invalidar por padrões
  for (const pattern of patterns) {
    if (pattern.includes('*')) {
      // 🌟 Invalidação por wildcard
      await cache.invalidateByPattern(pattern.replace('*', ''));
    } else {
      // 🎯 Invalidação específica
      await cache.del(pattern);
    }
  }
  
  // 🏷️ Invalidar por tags específicas
  await invalidateByEntity(path, input, ctx);
}

// 🏷️ Invalidação por entidade
async function invalidateByEntity(path: string, input: any, ctx: any) {
  const entityInvalidations = {
    // 👤 Invalidações por usuário
    user: (input: any) => [
      `user:${input.id || ctx.session?.user?.id}`,
      `profile:${input.id || ctx.session?.user?.id}`,
    ],
    
    // 📦 Invalidações por produto
    product: (input: any) => [
      `product:${input.id}`,
      `category:${input.categoryId}`,
      'product-list',
    ],
    
    // 🛒 Invalidações por pedido
    order: (input: any) => [
      `order:${input.id || input.orderId}`,
      `user-orders:${ctx.session?.user?.id}`,
      'order-list',
    ],
    
    // 🏢 Invalidações por organização
    organization: (input: any) => [
      `org:${input.id || input.organizationId}`,
      `org-users:${input.id || input.organizationId}`,
    ],
  };
  
  // 🔍 Extrair entidade do path
  const entity = path.split('.')[0];
  const invalidationFn = entityInvalidations[entity as keyof typeof entityInvalidations];
  
  if (invalidationFn) {
    const tags = invalidationFn(input);
    await cache.invalidateByTags(tags);
  }
}

// 🔧 Decorator para cache inteligente
export function smartCache(options: {
  ttl?: number;
  tags?: string[];
  dependsOn?: string[];
  invalidateOn?: string[];
} = {}) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    const originalMethod = descriptor.value;
    
    descriptor.value = async function (...args: any[]): Promise<any> {
      // 🔑 Gerar chave única
      const cacheKey = `${propertyKey}:${JSON.stringify(args)}`;
      
      // 🔍 Verificar dependências
      if (options.dependsOn) {
        const dependencyValid = await checkDependencies(options.dependsOn);
        if (!dependencyValid) {
          // 🗑️ Invalidar cache se dependências mudaram
          await cache.del(cacheKey);
        }
      }
      
      // 🔍 Buscar do cache
      const cachedResult = await cache.get(cacheKey);
      if (cachedResult !== null) {
        return cachedResult;
      }
      
      // 🔄 Executar método
      const result = await originalMethod.apply(this, args);
      
      // 💾 Salvar com tags
      await cache.set(cacheKey, result, {
        ttl: options.ttl || 3600,
        tags: options.tags || [],
      });
      
      return result;
    };
    
    return descriptor;
  };
}

// 🔍 Verificar dependências
async function checkDependencies(dependencies: string[]): Promise<boolean> {
  for (const dependency of dependencies) {
    const lastModified = await cache.get(`lastModified:${dependency}`);
    if (lastModified && Date.now() - lastModified < 5 * 60 * 1000) {
      return false; // Dependência foi modificada nos últimos 5 minutos
    }
  }
  return true;
}

// 🎯 Hook para invalidação manual
export function useInvalidateCache() {
  const utils = trpc.useUtils();
  
  return {
    // 🔄 Invalidar queries específicas
    invalidateQueries: async (patterns: string[]) => {
      for (const pattern of patterns) {
        await utils.invalidate(pattern as any);
      }
    },
    
    // 🏷️ Invalidar por tags
    invalidateByTags: async (tags: string[]) => {
      await cache.invalidateByTags(tags);
      // 🔄 Também invalidar no React Query
      await utils.invalidate();
    },
    
    // 🎯 Invalidar dados relacionados
    invalidateRelated: async (entity: string, entityId: string) => {
      const tags = [
        `${entity}:${entityId}`,
        `${entity}-list`,
        `related-${entity}`,
      ];
      
      await cache.invalidateByTags(tags);
      await utils.invalidate();
    },
    
    // 🧹 Limpeza completa
    clearAll: async () => {
      await cache.cleanup();
      await utils.invalidate();
    },
  };
}

// 📊 Sistema de cache em camadas
export class LayeredCache {
  private memoryCache = new Map<string, { data: any; expiry: number }>();
  private readonly maxMemoryItems = 1000;
  private readonly memoryTTL = 60 * 1000; // 1 minuto
  
  async get<T>(key: string): Promise<T | null> {
    // 🔍 Camada 1: Memória
    const memoryResult = this.getFromMemory<T>(key);
    if (memoryResult !== null) {
      return memoryResult;
    }
    
    // 🔍 Camada 2: Redis
    const redisResult = await cache.get<T>(key);
    if (redisResult !== null) {
      // 💾 Salvar na memória para próximas consultas
      this.setInMemory(key, redisResult);
      return redisResult;
    }
    
    return null;
  }
  
  async set<T>(key: string, value: T, options: { ttl?: number } = {}): Promise<void> {
    // 💾 Salvar nas duas camadas
    this.setInMemory(key, value);
    await cache.set(key, value, options);
  }
  
  async invalidate(key: string): Promise<void> {
    // 🗑️ Remover das duas camadas
    this.memoryCache.delete(key);
    await cache.del(key);
  }
  
  private getFromMemory<T>(key: string): T | null {
    const item = this.memoryCache.get(key);
    if (!item) return null;
    
    if (Date.now() > item.expiry) {
      this.memoryCache.delete(key);
      return null;
    }
    
    return item.data;
  }
  
  private setInMemory<T>(key: string, value: T): void {
    // 🧹 Limpar cache se muito cheio
    if (this.memoryCache.size >= this.maxMemoryItems) {
      const oldestKey = this.memoryCache.keys().next().value;
      this.memoryCache.delete(oldestKey);
    }
    
    this.memoryCache.set(key, {
      data: value,
      expiry: Date.now() + this.memoryTTL,
    });
  }
}

// 🎯 Instância global do cache em camadas
export const layeredCache = new LayeredCache();

📊 Monitoramento de Performance

src/lib/monitoring/performance.ts
// 📁 src/lib/monitoring/performance.ts
import { TRPCError } from '@trpc/server';
import { middleware } from '../trpc';

// 📊 Interface para métricas
interface PerformanceMetrics {
  operationName: string;
  duration: number;
  timestamp: number;
  userId?: string;
  cacheHit: boolean;
  errorCount: number;
  inputSize: number;
  outputSize: number;
}

// 🎯 Classe para monitoramento
export class PerformanceMonitor {
  private metrics: PerformanceMetrics[] = [];
  private readonly maxMetrics = 10000;
  
  // 📊 Registrar métrica
  recordMetric(metric: PerformanceMetrics): void {
    this.metrics.push(metric);
    
    // 🧹 Limpar métricas antigas
    if (this.metrics.length > this.maxMetrics) {
      this.metrics.shift();
    }
    
    // 🚨 Alertar sobre performance crítica
    if (metric.duration > 5000) { // 5 segundos
      console.warn(`🚨 Slow operation detected: ${metric.operationName} took ${metric.duration}ms`);
    }
  }
  
  // 📈 Obter estatísticas
  getStats(timeRange: number = 60 * 60 * 1000): {
    avgDuration: number;
    maxDuration: number;
    minDuration: number;
    totalOperations: number;
    cacheHitRate: number;
    errorRate: number;
    operationStats: Record<string, any>;
  } {
    const cutoff = Date.now() - timeRange;
    const recentMetrics = this.metrics.filter(m => m.timestamp > cutoff);
    
    if (recentMetrics.length === 0) {
      return {
        avgDuration: 0,
        maxDuration: 0,
        minDuration: 0,
        totalOperations: 0,
        cacheHitRate: 0,
        errorRate: 0,
        operationStats: {},
      };
    }
    
    const durations = recentMetrics.map(m => m.duration);
    const cacheHits = recentMetrics.filter(m => m.cacheHit).length;
    const errors = recentMetrics.reduce((sum, m) => sum + m.errorCount, 0);
    
    // 📊 Estatísticas por operação
    const operationStats: Record<string, any> = {};
    recentMetrics.forEach(metric => {
      if (!operationStats[metric.operationName]) {
        operationStats[metric.operationName] = {
          count: 0,
          totalDuration: 0,
          errors: 0,
          cacheHits: 0,
        };
      }
      
      const stats = operationStats[metric.operationName];
      stats.count++;
      stats.totalDuration += metric.duration;
      stats.errors += metric.errorCount;
      if (metric.cacheHit) stats.cacheHits++;
    });
    
    // 📈 Calcular médias
    Object.keys(operationStats).forEach(op => {
      const stats = operationStats[op];
      stats.avgDuration = stats.totalDuration / stats.count;
      stats.cacheHitRate = (stats.cacheHits / stats.count) * 100;
      stats.errorRate = (stats.errors / stats.count) * 100;
    });
    
    return {
      avgDuration: durations.reduce((a, b) => a + b, 0) / durations.length,
      maxDuration: Math.max(...durations),
      minDuration: Math.min(...durations),
      totalOperations: recentMetrics.length,
      cacheHitRate: (cacheHits / recentMetrics.length) * 100,
      errorRate: (errors / recentMetrics.length) * 100,
      operationStats,
    };
  }
  
  // 🔍 Operações mais lentas
  getSlowestOperations(limit: number = 10): PerformanceMetrics[] {
    return [...this.metrics]
      .sort((a, b) => b.duration - a.duration)
      .slice(0, limit);
  }
  
  // 📊 Relatório de performance
  generateReport(): string {
    const stats = this.getStats();
    const slowest = this.getSlowestOperations(5);
    
    return `
📊 Performance Report
=====================

📈 General Stats:
- Total Operations: ${stats.totalOperations}
- Average Duration: ${stats.avgDuration.toFixed(2)}ms
- Max Duration: ${stats.maxDuration}ms
- Cache Hit Rate: ${stats.cacheHitRate.toFixed(1)}%
- Error Rate: ${stats.errorRate.toFixed(1)}%

🐌 Slowest Operations:
${slowest.map(m => `- ${m.operationName}: ${m.duration}ms`).join('\n')}

📋 Operation Details:
${Object.entries(stats.operationStats).map(([op, stats]) => 
  `- ${op}: ${stats.count} calls, ${stats.avgDuration.toFixed(2)}ms avg`
).join('\n')}
    `.trim();
  }
}

// 🎯 Instância global do monitor
export const performanceMonitor = new PerformanceMonitor();

// 🔧 Middleware de monitoramento
export const monitoringMiddleware = middleware(async (opts) => {
  const startTime = Date.now();
  const { type, path, input, ctx } = opts;
  
  let cacheHit = false;
  let errorCount = 0;
  let result: any;
  
  try {
    // 🔍 Verificar se é hit de cache
    const cacheKey = `${path}:${JSON.stringify(input)}`;
    const cachedResult = await cache.get(cacheKey);
    
    if (cachedResult) {
      cacheHit = true;
      result = cachedResult;
    } else {
      // 🔄 Executar operação
      result = await next();
    }
  } catch (error) {
    errorCount = 1;
    throw error;
  } finally {
    // 📊 Registrar métrica
    const duration = Date.now() - startTime;
    const inputSize = JSON.stringify(input).length;
    const outputSize = result ? JSON.stringify(result).length : 0;
    
    performanceMonitor.recordMetric({
      operationName: path,
      duration,
      timestamp: Date.now(),
      userId: ctx.session?.user?.id,
      cacheHit,
      errorCount,
      inputSize,
      outputSize,
    });
  }
  
  return result;
});

// 📊 Hook para métricas em tempo real
export function usePerformanceMetrics() {
  return {
    // 📈 Obter estatísticas
    getStats: () => performanceMonitor.getStats(),
    
    // 🔍 Operações mais lentas
    getSlowestOperations: (limit?: number) => 
      performanceMonitor.getSlowestOperations(limit),
    
    // 📊 Relatório completo
    generateReport: () => performanceMonitor.generateReport(),
    
    // 🎯 Métricas específicas
    getOperationMetrics: (operationName: string) => {
      const stats = performanceMonitor.getStats();
      return stats.operationStats[operationName] || null;
    },
  };
}

// 🚨 Sistema de alertas
export class AlertSystem {
  private alerts: Array<{
    type: 'warning' | 'error' | 'info';
    message: string;
    timestamp: number;
    acknowledged: boolean;
  }> = [];
  
  // 🔔 Criar alerta
  createAlert(
    type: 'warning' | 'error' | 'info',
    message: string
  ): void {
    this.alerts.push({
      type,
      message,
      timestamp: Date.now(),
      acknowledged: false,
    });
    
    // 🚨 Log baseado no tipo
    if (type === 'error') {
      console.error(`🚨 ALERT: ${message}`);
    } else if (type === 'warning') {
      console.warn(`⚠️ ALERT: ${message}`);
    } else {
      console.info(`ℹ️ ALERT: ${message}`);
    }
  }
  
  // 📊 Verificar condições de alerta
  checkConditions(): void {
    const stats = performanceMonitor.getStats();
    
    // 🐌 Performance alerts
    if (stats.avgDuration > 1000) {
      this.createAlert('warning', `Average response time is ${stats.avgDuration}ms`);
    }
    
    if (stats.maxDuration > 5000) {
      this.createAlert('error', `Maximum response time is ${stats.maxDuration}ms`);
    }
    
    // 💾 Cache alerts
    if (stats.cacheHitRate < 50) {
      this.createAlert('warning', `Cache hit rate is low: ${stats.cacheHitRate}%`);
    }
    
    // 🚨 Error alerts
    if (stats.errorRate > 5) {
      this.createAlert('error', `Error rate is high: ${stats.errorRate}%`);
    }
  }
  
  // 📋 Obter alertas
  getAlerts(unacknowledgedOnly: boolean = false): Array<any> {
    return this.alerts.filter(alert => 
      !unacknowledgedOnly || !alert.acknowledged
    );
  }
  
  // ✅ Confirmar alerta
  acknowledgeAlert(index: number): void {
    if (this.alerts[index]) {
      this.alerts[index].acknowledged = true;
    }
  }
}

// 🎯 Instância global do sistema de alertas
export const alertSystem = new AlertSystem();

// 🔄 Verificar alertas periodicamente
setInterval(() => {
  alertSystem.checkConditions();
}, 60 * 1000); // A cada minuto

🎨 Otimizações Frontend

src/components/optimized/VirtualizedList.tsx
// 📁 src/components/optimized/VirtualizedList.tsx
import { useState, useCallback, useMemo } from 'react';
import { FixedSizeList as List } from 'react-window';
import { trpc } from '@/lib/trpc/client';

interface VirtualizedListProps<T> {
  queryKey: string;
  itemHeight: number;
  renderItem: (item: T, index: number) => React.ReactNode;
  searchTerm?: string;
  filters?: Record<string, any>;
}

export function VirtualizedList<T>({
  queryKey,
  itemHeight,
  renderItem,
  searchTerm,
  filters,
}: VirtualizedListProps<T>) {
  const [visibleRange, setVisibleRange] = useState({ start: 0, end: 10 });
  
  // 📊 Query com paginação infinita
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
    isLoading,
    isError,
  } = trpc.useInfiniteQuery(
    [queryKey, { searchTerm, filters }],
    ({ pageParam = 0 }) => ({
      limit: 50,
      offset: pageParam,
      searchTerm,
      filters,
    }),
    {
      getNextPageParam: (lastPage, pages) => {
        if (lastPage.length < 50) return undefined;
        return pages.length * 50;
      },
      // 🚀 Configurações de performance
      keepPreviousData: true,
      staleTime: 5 * 60 * 1000, // 5 minutos
      cacheTime: 30 * 60 * 1000, // 30 minutos
    }
  );
  
  // 📊 Memoizar items combinados
  const allItems = useMemo(() => {
    return data?.pages.flatMap(page => page) || [];
  }, [data]);
  
  // 🔄 Callback para mudança de range visível
  const handleVisibleRangeChange = useCallback(({ visibleStartIndex, visibleStopIndex }) => {
    setVisibleRange({ start: visibleStartIndex, end: visibleStopIndex });
    
    // 📈 Carregar mais dados quando chegando perto do fim
    if (
      visibleStopIndex >= allItems.length - 10 &&
      hasNextPage &&
      !isFetchingNextPage
    ) {
      fetchNextPage();
    }
  }, [allItems.length, hasNextPage, isFetchingNextPage, fetchNextPage]);
  
  // 🎨 Renderizar item da lista
  const ListItem = useCallback(({ index, style }) => {
    const item = allItems[index];
    
    if (!item) {
      return (
        <div style={style} className="flex items-center justify-center">
          <div className="animate-pulse bg-gray-800 h-8 w-full rounded"></div>
        </div>
      );
    }
    
    return (
      <div style={style}>
        {renderItem(item, index)}
      </div>
    );
  }, [allItems, renderItem]);
  
  if (isLoading) {
    return (
      <div className="space-y-4">
        {Array.from({ length: 10 }).map((_, i) => (
          <div key={i} className="animate-pulse bg-gray-800 h-16 rounded"></div>
        ))}
      </div>
    );
  }
  
  if (isError) {
    return (
      <div className="text-red-400 text-center py-8">
        Erro ao carregar dados
      </div>
    );
  }
  
  return (
    <div className="h-96">
      <List
        height={384} // 96 * 4
        itemCount={allItems.length}
        itemSize={itemHeight}
        onItemsRendered={handleVisibleRangeChange}
        overscanCount={5}
        className="scrollbar-thin scrollbar-thumb-gray-600 scrollbar-track-gray-800"
      >
        {ListItem}
      </List>
      
      {isFetchingNextPage && (
        <div className="text-center py-4">
          <div className="animate-spin w-6 h-6 border-2 border-lime-400 border-t-transparent rounded-full mx-auto"></div>
        </div>
      )}
    </div>
  );
}

// 📊 Hook para dados otimizados
export function useOptimizedData<T>(
  queryKey: string,
  options: {
    enabled?: boolean;
    searchTerm?: string;
    filters?: Record<string, any>;
    selectFields?: string[];
  } = {}
) {
  const { enabled = true, searchTerm, filters, selectFields } = options;
  
  return trpc.useQuery(
    [queryKey, { searchTerm, filters, selectFields }],
    {
      enabled,
      // 🚀 Configurações otimizadas
      keepPreviousData: true,
      staleTime: 5 * 60 * 1000,
      cacheTime: 30 * 60 * 1000,
      
      // 📊 Transformar dados apenas se necessário
      select: useCallback((data: any) => {
        if (!selectFields) return data;
        
        // 🎯 Selecionar apenas campos necessários
        if (Array.isArray(data)) {
          return data.map(item => 
            selectFields.reduce((acc, field) => {
              acc[field] = item[field];
              return acc;
            }, {} as any)
          );
        }
        
        return selectFields.reduce((acc, field) => {
          acc[field] = data[field];
          return acc;
        }, {} as any);
      }, [selectFields]),
      
      // 🔄 Refetch inteligente
      refetchInterval: (data, query) => {
        // 📊 Refetch baseado no tipo de dados
        if (queryKey.includes('realtime')) return 5000; // 5s
        if (queryKey.includes('dashboard')) return 30000; // 30s
        return false;
      },
      
      // 🎯 Retry inteligente
      retry: (failureCount, error) => {
        if (error?.data?.httpStatus === 404) return false;
        return failureCount < 3;
      },
    }
  );
}

// 🔄 Component para preload inteligente
export function SmartPreloader({ routes }: { routes: string[] }) {
  const utils = trpc.useUtils();
  
  // 🎯 Preload em hover
  const handleHover = useCallback(async (route: string) => {
    await utils.prefetch(route as any);
  }, [utils]);
  
  // 📊 Preload crítico no mount
  useEffect(() => {
    const criticalRoutes = routes.filter(route => 
      route.includes('dashboard') || route.includes('user')
    );
    
    criticalRoutes.forEach(route => {
      utils.prefetch(route as any);
    });
  }, [routes, utils]);
  
  return null; // Componente invisível
}

// 🎨 Hook para debounce de busca
export function useDebouncedSearch(
  queryKey: string,
  delay: number = 300
) {
  const [searchTerm, setSearchTerm] = useState('');
  const [debouncedSearchTerm, setDebouncedSearchTerm] = useState('');
  
  // 🔄 Debounce do termo de busca
  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedSearchTerm(searchTerm);
    }, delay);
    
    return () => clearTimeout(handler);
  }, [searchTerm, delay]);
  
  // 📊 Query com busca debounced
  const query = trpc.useQuery(
    [queryKey, { searchTerm: debouncedSearchTerm }],
    {
      enabled: Boolean(debouncedSearchTerm),
      keepPreviousData: true,
      staleTime: 2 * 60 * 1000, // 2 minutos
    }
  );
  
  return {
    searchTerm,
    setSearchTerm,
    debouncedSearchTerm,
    ...query,
  };
}

// 📊 Component para analytics otimizado
export function OptimizedAnalytics({ 
  trackingId,
  events 
}: { 
  trackingId: string;
  events: string[];
}) {
  const mutation = trpc.useMutation('analytics.track', {
    // 🎯 Não mostrar loading para analytics
    onError: (error) => {
      console.warn('Analytics error:', error);
    },
  });
  
  // 📊 Batch tracking
  const trackBatch = useCallback((eventData: any[]) => {
    mutation.mutate({
      trackingId,
      events: eventData,
    });
  }, [trackingId, mutation]);
  
  // 🔄 Queue de eventos
  const [eventQueue, setEventQueue] = useState<any[]>([]);
  
  // 📈 Flush da queue periodicamente
  useEffect(() => {
    const interval = setInterval(() => {
      if (eventQueue.length > 0) {
        trackBatch(eventQueue);
        setEventQueue([]);
      }
    }, 5000); // 5 segundos
    
    return () => clearInterval(interval);
  }, [eventQueue, trackBatch]);
  
  // 🎯 Função para adicionar evento
  const trackEvent = useCallback((event: string, data?: any) => {
    setEventQueue(prev => [...prev, {
      event,
      data,
      timestamp: Date.now(),
    }]);
  }, []);
  
  return { trackEvent };
}

💡 Melhores Práticas de Performance

Cache em Camadas:Use memória + Redis para máxima performance com fallback seguro.

Invalidação Inteligente:Invalide apenas dados relacionados, não todo o cache.

Monitoramento Contínuo:Monitore métricas em tempo real para identificar gargalos.

Virtualização:Use listas virtualizadas para grandes volumes de dados.