{
 "subtemas": [],
 "tarjetas": [
  {
   "sub": "AOP",
   "q": "Vocabulario AOP: aspect, join point, pointcut, advice, weaving",
   "a": "Aspect: la clase modular que agrupa funcionalidad transversal (@Aspect + @Component).\nJoin point: punto del programa donde PUEDE intervenir (en Spring AOP: ejecución de métodos).\nPointcut: la EXPRESIÓN que selecciona join points (execution(...)).\nAdvice: la acción (@Before/@After/@Around).\nWeaving: tejer aspects en los beans (Spring: proxies en runtime).",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "AOP",
   "q": "Tipos de advice y cuál usar",
   "a": "@Before: antes (validación, log de entrada).\n@AfterReturning: tras retorno normal.\n@AfterThrowing: tras excepción.\n@After: tras ambos (finally).\n@Around: ENVUELVE todo — puedes decidir si llamar proceed(), medir tiempo, cachear, transformar resultado. Es el más poderoso (timers, rate limiting).",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "AOP",
   "q": "Pointcut expressions: los patrones que debes saber",
   "a": "execution(* com.app.service..*.*(..)) — todo el paquete service y subpaquetes.\nexecution(public * *(..)), execution(* Servicio*.metodo(..)).\n@annotation(com.app.MiAnotacion) — métodos anotados.\nwithin(com.app..*), this/target(bean type), bean(miBean*).\nCombinar con &&, ||, !; reutilizar con @Pointcut nombrado.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "AOP",
   "q": "@Around con timer: el ejemplo que todo examen pide",
   "a": "@Around(\"@annotation(Monitoreado)\")\npublic Object medir(ProceedingJoinPoint pjp) throws Throwable {\n  long t0 = System.nanoTime();\n  try { return pjp.proceed(); } finally {\n    log.info(\"{} tardó {} ms\", pjp.getSignature(), (System.nanoTime()-t0)/1_000_000);\n  }\n}\nproceed(args) permite MODIFICAR argumentos/resultado. Sin proceed, el método original NO se ejecuta.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "AOP",
   "q": "Limitaciones de Spring AOP (proxy-based)",
   "a": "Solo métodos públicos visibles por el proxy: private/final/static NO se interceptan.\nSelf-invocation (this.metodo()) salta el proxy → @Transactional/@Cacheable/@Async 'no funcionan' dentro del mismo bean.\nSolo ejecución de métodos (no campos ni constructores, eso es AspectJ completo).\nObjetos internos creados con new nunca son proxies.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "AOP",
   "q": "Ordenar aspects y handlers con @Order",
   "a": "@Order(n): menor = más EXTERNO (corre antes en entry, después en exit).\nCasos: un aspect de retry fuera del de logging; transacción típicamente muy interna.\nTambién @Order en interceptores, filtros y ExceptionHandlerAdvices.\nSin order = indefinido entre aspects (no entre categorías distintas).",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "AOP",
   "q": "Casos de uso legítimos de AOP en apps de negocio",
   "a": "Logging/auditoría estructurada (@Loggable custom), métricas/timing, manejo de errores transversal, rate limiting, autorización custom (@RequirePermission), retry declarativo, enmascarar datos sensibles en logs, propagar tenant/ctx.\nNO para lógica de negocio (invisible al leer el flujo — 'magia').\nRegla: si lo borras, la app sigue CORRECTA (solo menos observable/protegida).",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "AOP",
   "q": "¿AspectJ vs Spring AOP?",
   "a": "Spring AOP: proxies dinámicos, solo métodos de beans Spring, runtime weaving — cubre 95% de casos (usar con @EnableAspectJAutoProxy).\nAspectJ completo: bytecode weaving en compilación/carga — intercepta constructores, campos, métodos private/static, objetos no-Spring; requiere procesador/loader (ajc, LTW).\nUsar AspectJ solo si el límite del proxy realmente duele.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Async, caché y scheduling",
   "q": "@Async: setup y reglas para que funcione",
   "a": "1) @EnableAsync en una @Configuration.\n2) Configurar executor: AsyncConfigurer o spring.task.execution.* (default SimpleAsyncTaskExecutor crea un hilo por llamada — ¡peligroso en prod!).\n3) Método público llamado DESDE OTRO bean (proxy).\nRetorno: void o Future/CompletableFuture. Excepciones: AsyncUncaughtExceptionHandler si void.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Async, caché y scheduling",
   "q": "@Scheduled: cron, fixedRate vs fixedDelay y zona",
   "a": "@Scheduled(cron = \"0 0 2 * * *\", zone = \"America/Lima\") — 2 a.m. (seg min hora día mes díaSem).\nfixedRate = cada N ms DESDE EL INICIO; fixedDelay = N ms DESDE EL FIN (nunca solapa) — el seguro para tareas lentas.\nRequiere @EnableScheduling. default es single-thread: tareas largas retrasan las demás → configurar TaskScheduler pool.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Async, caché y scheduling",
   "q": "Schedulers en cluster: por qué necesitas un lock (ShedLock)",
   "a": "Con 3 réplicas, @Scheduled corre 3 VECES (una por instancia).\nSoluciones: ShedLock (@SchedulerLock(name, lockAtMostFor) + tabla/Redis: solo una instancia ejecuta), Quartz clusterizado, o mover al scheduler del cloud (Cloud Scheduler → endpoint, Cloud Tasks).\nElegir idempotencia SIEMPRE como red de seguridad.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Async, caché y scheduling",
   "q": "@Cacheable, @CacheEvict, @CachePut: semántica exacta",
   "a": "@Cacheable(\"productos\", key=\"#id\"): si está en caché NO ejecuta el método.\n@CachePut: SIEMPRE ejecuta y actualiza la caché (para writes que deben refrescar).\n@CacheEvict: invalida (allEntries=true borra todo).\nunless=\"#result == null\" evita cachear vacíos; condition filtra.\nRequiere @EnableCaching y un CacheManager (Caffeine local, Redis distribuido).",
   "nivel": "intermedio",
   "clave": true
  },
  {
   "sub": "Async, caché y scheduling",
   "q": "Caffeine: configuración típica de caché local",
   "a": "Caffeine.newBuilder().maximumSize(10_000).expireAfterWrite(Duration.ofMinutes(10)).refreshAfterWrite(5min)\n.maximumWeight + weigher para limitar por memoria.\nrecordStats() para métricas (hit rate).\nEn Spring: SpringApplicationBuilder o CaffeineCacheManager; expiración variable con expireAfter(Expiry custom).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Async, caché y scheduling",
   "q": "Caché con Redis en Spring: consideraciones",
   "a": "spring-boot-starter-data-redis + RedisCacheManager (Jackson2JsonRedisSerializer para guardar DTOs).\nClaves: nombres de caché como prefijo (productos::42).\nTTL obligatorio (todo dato cached expira), invalidación cruzada entre servicios por eventos.\nSerialización estable: sin classnames frágiles, versionar DTOs cached.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Async, caché y scheduling",
   "q": "Cuándo cachear y cuándo NO (decisiones que evaluán en entrevista)",
   "a": "SÍ: lecturas frecuentes de datos poco cambiantes (catálogos, configs, cálculos costosos), alivio a BD bajo picos.\nNO: datos que cambian con cada write, respuestas personalizadas sin clave correcta, datos críticos sin tolerancia a stale, tasa de hit baja (overhead gratis).\nTTL + invalidación + stampede protection (refreshAhead/singleflight) = el kit completo.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Async, caché y scheduling",
   "q": "Cache stampede (thundering herd): qué es y cómo mitigarlo",
   "a": "Clave expira → 1000 requests concurrentes golpean la BD simultáneamente para rehacerla.\nMitigación: lock/singleflight (uno recalcula, el resto espera el nuevo valor), refreshAhead (renovar antes de expirar), TTL con jitter aleatorio, y fallback a valor stale mientras se recalcula (serve-stale).",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Async, caché y scheduling",
   "q": "Eventos de aplicación: @EventListener y publishing",
   "a": "publisher.publishEvent(new PedidoCreadoEvent(id)) → @EventListener corren sincrónicos EN EL MISMO HILO/tx por defecto.\nAsync: @Async sobre el listener.\nOrder con @Order. Eventos desacoplan módulos internos (no son mensajes de microservicios).\nPayload inmutable; listener NO debe fallar el flujo principal (try/catch o async).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Async, caché y scheduling",
   "q": "Cuándo @EventListener vs @TransactionalEventListener",
   "a": "@EventListener: inmediato (misma tx — ve cambios sin confirmar; si el listener escribe, comparte tx).\n@TransactionalEventListener(AFTER_COMMIT): solo si la tx CONFIRMA — el estándar para efectos externos (emails, colas, invalidar caché).\nTrampa AFTER_COMMIT: nueva tx propia para escribir (REQUIRES_NEW o TransactionTemplate).\nfallbackExecution=true si no hay tx.",
   "nivel": "avanzado",
   "clave": false
  }
 ]
}