¿Qué estrategia de cacheo usas para las respuestas de API en móvil: cacheo HTTP, ETags, cache-aside?
La respuesta correcta combina tres cachés distintas — a nivel HTTP, peticiones condicionales y cache-aside a nivel de app — porque cada una resuelve un problema diferente.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
¿Qué estrategia de cacheo usas para las respuestas de API en móvil: cacheo HTTP, ETags, cache-aside?
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.#Tres capas, tres trabajos distintos
'Estrategia de cacheo' suena como una sola decisión, pero en un cliente móvil maduro en realidad son tres capas apiladas una sobre otra, cada una resolviendo un problema que las otras dos no resuelven: el cacheo a nivel HTTP te evita volver a descargar una respuesta por completo, las peticiones condicionales te ahorran ancho de banda cuando tienes que volver a preguntar, y una capa de cache-aside a nivel de app es lo que realmente le da a tu UI acceso offline a los datos como objetos de dominio en lugar de bytes HTTP crudos.
#Capa 1: el cacheo HTTP hace el trabajo más barato, gratis
Si tu backend envía los headers Cache-Control correctos, la caché en disco integrada de OkHttp servirá las peticiones GET directamente desde disco sin tocar la red en absoluto, mientras la respuesta siga fresca. Esto cuesta casi nada de configurar y es lo primero que hay que dejar bien antes de recurrir a algo más sofisticado.
val cacheSize = 20L * 1024 * 1024 // 20 MBval cache = Cache(File(context.cacheDir, "http_cache"), cacheSize)val client = OkHttpClient.Builder().cache(cache).build()
Esto solo ayuda en peticiones GET, y solo si el servidor realmente envía Cache-Control: max-age=.... Un servidor que envía no-store o nada en absoluto no le da a OkHttp nada con qué trabajar, y golpearás la red cada vez sin importar qué tan grande sea tu directorio de caché.
#Capa 2: las peticiones condicionales convierten un cache miss en un 304 barato
Una vez que la respuesta cacheada está vencida (pasó su max-age), lo ingenuo es volver a descargar todo. Algo mejor es una petición condicional: si el servidor envió un ETag (una huella del recurso) o Last-Modified, OkHttp adjunta automáticamente If-None-Match o If-Modified-Since en la petición de revalidación. Si el recurso en realidad no cambió, el servidor responde 304 Not Modified con el cuerpo vacío, y OkHttp sirve la copia cacheada de forma transparente — te ahorras la transferencia completa del payload aunque técnicamente hiciste un round trip de red.
// Rara vez escribes esto a mano — OkHttp lo hace automáticamente cuando// la respuesta cacheada trae un ETag y Cache-Control indica que necesita// revalidación (por ejemplo "max-age=0, must-revalidate"). Así se ve// en el cable://// Petición: GET /articles/42// If-None-Match: "a1b2c3-etag"// Respuesta: 304 Not Modified (sin cuerpo — la copia cacheada sigue siendo válida)
La interacción entre ambos importa para una respuesta de entrevista: max-age controla si preguntas siquiera; ETag/Last-Modified controlan qué tan costoso es preguntar una vez que lo haces.
#Capa 3: cache-aside es donde realmente vive tu modelo de dominio
El cacheo HTTP opera sobre respuestas crudas y es invisible para el código de tu app — excelente para el ancho de banda, inútil en el momento en que necesitas mostrar el 'último dato bueno conocido' como un objeto de dominio tipado, fusionarlo con estado local, o soportar GraphQL (donde, como vimos antes, no existe una URL por recurso contra la cual cachear). Cache-aside soluciona eso: el repositorio revisa primero un almacén local persistido, devuelve lo que tiene de inmediato, y por separado dispara una petición de red que actualiza ese almacén — con la UI observando el almacén de forma reactiva para recoger los datos frescos en el momento en que llegan.
class ArticleRepository(private val dao: ArticleDao,private val api: ArticleApi,) {fun observeArticle(id: String): Flow<Article> = flow {// 1. Emite de inmediato lo que ya tenemos, aunque esté vencido.dao.getArticle(id)?.let { emit(it.toDomain()) }// 2. Refresca desde la red en segundo plano.try {val fresh = api.getArticle(id)dao.upsert(fresh.toEntity())} catch (e: IOException) {// Sin conexión o la petición falló — la emisión cacheada de// arriba ya le dio a la UI algo que renderizar.}}.flatMapLatest {// 3. Vuelve a emitir desde la BD, que ahora refleja el valor actualizado.dao.observeArticle(id).map { it.toDomain() }}}
Esta es la sensación de 'stale-while-revalidate' que la mayoría de las apps busca: la pantalla pinta de inmediato con lo que hay en disco, luego se actualiza en silencio en el mismo lugar cuando la red responde — y sigue funcionando sin ningún cambio de código si el dispositivo está completamente sin conexión, porque el paso 1 ya renderizó algo.
#Eligiendo la combinación
🧠 Check your understanding
0/1 · 0/1 answered1. La respuesta cacheada de una petición GET ya pasó su max-age, pero el servidor todavía tiene un ETag para ese recurso. ¿Qué pasa en la siguiente petición si el recurso en realidad no cambió en el servidor?