Explicá el patrón Repository y la "única fuente de verdad" en Android
Un Repository esconde de dónde viene el dato; la única fuente de verdad decide en qué copia de ese dato puede confiar realmente la UI — juntas, son la razón por la que las apps offline-first no se caen a pedazos.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
Explicá el patrón Repository y la "única fuente de verdad" en Android
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.#Qué hace realmente un Repository
Un Repository es una clase que se ubica entre "dónde vive físicamente el dato" (una API REST, una base de datos Room, DataStore, una caché en memoria) y "quién lo consume" (un ViewModel, un caso de uso). Expone una API chica y que revela su intención — observeArticles(), refresh(), getArticle(id) — y esconde detrás de ella todo lo relacionado con cómo se obtiene, se cachea o se combina ese dato. El ViewModel que llama a repository.observeArticles() no tiene idea, ni debería necesitarla, de si ese dato vino de una llamada de red hace cinco segundos o de Room.
Esto te da dos cosas que importan en una respuesta de entrevista, no solo en la teoría: testabilidad — un test de ViewModel simula (fake) la interfaz Repository en vez de levantar Retrofit y Room — y una costura para inversión de dependencias — el ViewModel depende de ArticleRepository (una interfaz), no de ArticleRepositoryImpl, así que el cableado concreto (qué API, qué base de datos) vive en el límite del módulo de Hilt/Dagger, no disperso por toda la capa de UI.
interface ArticleRepository {fun observeArticles(): Flow<List<Article>>suspend fun refresh()}
#Qué agrega encima la "única fuente de verdad"
El patrón Repository por sí solo no dice cuál fuente subyacente debería confiar la UI cuando la red y la caché local no coinciden — eso es lo que responde la única fuente de verdad (single source of truth, SSOT). La regla: elegí exactamente un lugar donde el dato vive de forma canónica — casi siempre la base de datos local — y todo lo demás (respuestas de red, cachés en memoria) existe solo para actualizar esa copia canónica. La UI nunca observa la red directamente; observa la base de datos, y la base de datos es donde escribe la red.
class ArticleRepositoryImpl @Inject constructor(private val dao: ArticleDao,private val api: ArticleApi,) : ArticleRepository {override fun observeArticles(): Flow<List<Article>> =dao.observeAll().map { entities -> entities.map { it.toDomain() } }override suspend fun refresh() {val remote = api.fetchArticles()dao.upsertAll(remote.map { it.toEntity() })}}@Daointerface ArticleDao {@Query("SELECT * FROM articles ORDER BY publishedAt DESC")fun observeAll(): Flow<List<ArticleEntity>>@Upsertsuspend fun upsertAll(articles: List<ArticleEntity>)}
Fijate que observeArticles() nunca toca api en absoluto — es un Flow que sale directamente de Room. refresh() es la única función que habla con la red, y su único trabajo es escribir el resultado en Room. El Flow de Room emite automáticamente después, porque Room invalida y vuelve a consultar cada vez que cambia la tabla subyacente — la UI recibe la actualización "gratis" sin que el Repository tenga que empujarle nada manualmente.
#Conectándolo a un ViewModel
class ArticleListViewModel @Inject constructor(private val repository: ArticleRepository,) : ViewModel() {val articles: StateFlow<List<Article>> =repository.observeArticles().stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList())init {viewModelScope.launch { repository.refresh() }}}
articles siempre está respaldado por Room, así que si refresh() falla (sin red), la UI igual muestra la última lista cacheada en vez de una pantalla vacía — ese es el beneficio offline-first de la SSOT, y surge naturalmente del diseño en vez de necesitar código especial para el caso de error.