$npx -y skills add hanamizuki/solopreneur --skill data-layerGuidance on implementing the Data Layer using Repository pattern, Room (Local), and Retrofit (Remote) with offline-first synchronization.
| 1 | # Android Data Layer & Offline-First |
| 2 | |
| 3 | ## Instructions |
| 4 | |
| 5 | The Data Layer coordinates data from multiple sources. |
| 6 | |
| 7 | ### 1. Repository Pattern |
| 8 | * **Role**: Single Source of Truth (SSOT). |
| 9 | * **Logic**: The repository decides whether to return cached data or fetch fresh data. |
| 10 | * **Implementation**: |
| 11 | ```kotlin |
| 12 | class NewsRepository @Inject constructor( |
| 13 | private val newsDao: NewsDao, |
| 14 | private val newsApi: NewsApi |
| 15 | ) { |
| 16 | // Expose data from Local DB as the source of truth |
| 17 | val newsStream: Flow<List<News>> = newsDao.getAllNews() |
| 18 | |
| 19 | // Sync operation |
| 20 | suspend fun refreshNews() { |
| 21 | val remoteNews = newsApi.fetchLatest() |
| 22 | newsDao.insertAll(remoteNews) |
| 23 | } |
| 24 | } |
| 25 | ``` |
| 26 | |
| 27 | ### 2. Local Persistence (Room) |
| 28 | * **Usage**: Primary cache and offline storage. |
| 29 | * **Entities**: Define `@Entity` data classes. |
| 30 | * **DAOs**: Return `Flow<T>` for observable data. |
| 31 | |
| 32 | ### 3. Remote Data (Retrofit) |
| 33 | * **Usage**: Fetching data from backend. |
| 34 | * **Response**: Use `suspend` functions in interfaces. |
| 35 | * **Error Handling**: Wrap network calls in `try-catch` blocks or a `Result` wrapper to handle exceptions (NoInternet, 404, etc.) gracefully. |
| 36 | |
| 37 | ### 4. Synchronization |
| 38 | * **Read**: "Stale-While-Revalidate". Show local data immediately, trigger a background refresh. |
| 39 | * **Write**: "Outbox Pattern" (Advanced). Save local change immediately, mark as "unsynced", use `WorkManager` to push changes to server. |
| 40 | |
| 41 | ### 5. Dependency Injection |
| 42 | * Bind Repository interfaces to implementations in a Hilt Module. |
| 43 | ```kotlin |
| 44 | @Binds |
| 45 | abstract fun bindNewsRepository(impl: OfflineFirstNewsRepository): NewsRepository |
| 46 | ``` |