android-sdd 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/dist/index.js +143 -0
  2. package/package.json +27 -0
  3. package/skills/Android Ecosystem/Baseline Profile Generator/SKILL.md +277 -0
  4. package/skills/Android Ecosystem/Glance/SKILL.md +315 -0
  5. package/skills/Android Platform/Configuration/SKILL.md +201 -0
  6. package/skills/Android Platform/Filesystem/SKILL.md +216 -0
  7. package/skills/Android Platform/Lifecycle/SKILL.md +233 -0
  8. package/skills/Android Platform/Manifest/SKILL.md +226 -0
  9. package/skills/Android Platform/Process Death Recovery/SKILL.md +214 -0
  10. package/skills/Android Platform/Resources/SKILL.md +234 -0
  11. package/skills/Android Platform/SavedStateHandle/SKILL.md +217 -0
  12. package/skills/Android Platform/State Restoration/SKILL.md +210 -0
  13. package/skills/Architecture/Bounded Context/SKILL.md +207 -0
  14. package/skills/Architecture/Clean Architecture/SKILL.md +229 -0
  15. package/skills/Architecture/Domain Modeling/SKILL.md +236 -0
  16. package/skills/Architecture/Entity Design/SKILL.md +243 -0
  17. package/skills/Architecture/Feature Isolation/SKILL.md +216 -0
  18. package/skills/Architecture/MVI/SKILL.md +224 -0
  19. package/skills/Architecture/MVVM/SKILL.md +198 -0
  20. package/skills/Architecture/Modularization/SKILL.md +194 -0
  21. package/skills/Architecture/Offline First/SKILL.md +249 -0
  22. package/skills/Architecture/Repository Pattern/SKILL.md +216 -0
  23. package/skills/Architecture/Side Effect Management/SKILL.md +278 -0
  24. package/skills/Architecture/State Management/SKILL.md +229 -0
  25. package/skills/Architecture/Unidirectional Data Flow/SKILL.md +196 -0
  26. package/skills/Architecture/Use Case Design/SKILL.md +244 -0
  27. package/skills/Architecture/Value Object/SKILL.md +226 -0
  28. package/skills/Build Infrastructure/Build Orchestration/SKILL.md +257 -0
  29. package/skills/Build Infrastructure/Dependency Compatibility Resolver/SKILL.md +259 -0
  30. package/skills/Build Infrastructure/Environment Validator/SKILL.md +311 -0
  31. package/skills/Build System/Build Cache/SKILL.md +233 -0
  32. package/skills/Build System/Build Flavor Strategy/SKILL.md +171 -0
  33. package/skills/Build System/Build Variant/SKILL.md +215 -0
  34. package/skills/Build System/Convention Plugin/SKILL.md +288 -0
  35. package/skills/Build System/Dependency Management/SKILL.md +261 -0
  36. package/skills/Build System/Gradle/SKILL.md +284 -0
  37. package/skills/Build System/Incremental Build/SKILL.md +199 -0
  38. package/skills/Build System/KAPT/SKILL.md +198 -0
  39. package/skills/Build System/KSP/SKILL.md +263 -0
  40. package/skills/Build System/Module Dependency Graph Validation/SKILL.md +223 -0
  41. package/skills/Build System/Specialized/C++/SKILL.md +308 -0
  42. package/skills/Build System/Specialized/JNI/SKILL.md +306 -0
  43. package/skills/Build System/Specialized/NDK/SKILL.md +264 -0
  44. package/skills/Build System/Version Catalog/SKILL.md +304 -0
  45. package/skills/Concurrency/Background Processing/SKILL.md +185 -0
  46. package/skills/Concurrency/Channel/SKILL.md +207 -0
  47. package/skills/Concurrency/Coroutine/SKILL.md +200 -0
  48. package/skills/Concurrency/Flow/SKILL.md +179 -0
  49. package/skills/Concurrency/Mutex Strategy/SKILL.md +185 -0
  50. package/skills/Concurrency/SharedFlow/SKILL.md +171 -0
  51. package/skills/Concurrency/StateFlow/SKILL.md +175 -0
  52. package/skills/Concurrency/Structured Concurrency/SKILL.md +197 -0
  53. package/skills/Concurrency/Synchronization Policy/SKILL.md +192 -0
  54. package/skills/Core Language/Annotation Processing/SKILL.md +224 -0
  55. package/skills/Core Language/DSL/SKILL.md +186 -0
  56. package/skills/Core Language/Extension Functions Design/SKILL.md +191 -0
  57. package/skills/Core Language/Immutability/SKILL.md +156 -0
  58. package/skills/Core Language/KMP/SKILL.md +182 -0
  59. package/skills/Core Language/Kotlin/SKILL.md +187 -0
  60. package/skills/Core Language/Reactive State Management/SKILL.md +228 -0
  61. package/skills/Core Language/Reactive Streams/SKILL.md +235 -0
  62. package/skills/Core Language/Serialization/SKILL.md +191 -0
  63. package/skills/Data Layer/Cache Strategy/SKILL.md +261 -0
  64. package/skills/Data Layer/Conflict Resolution/SKILL.md +248 -0
  65. package/skills/Data Layer/DAO/SKILL.md +225 -0
  66. package/skills/Data Layer/DTO Mapping/SKILL.md +269 -0
  67. package/skills/Data Layer/DataStore/SKILL.md +264 -0
  68. package/skills/Data Layer/Database Versioning Strategy/SKILL.md +215 -0
  69. package/skills/Data Layer/Encrypted Database/SKILL.md +212 -0
  70. package/skills/Data Layer/File Storage/SKILL.md +247 -0
  71. package/skills/Data Layer/Indexing/SKILL.md +184 -0
  72. package/skills/Data Layer/Key-Value Store Strategy/SKILL.md +185 -0
  73. package/skills/Data Layer/Merge Strategy/SKILL.md +240 -0
  74. package/skills/Data Layer/Migration/SKILL.md +243 -0
  75. package/skills/Data Layer/Paging/SKILL.md +264 -0
  76. package/skills/Data Layer/Proto DataStore/SKILL.md +250 -0
  77. package/skills/Data Layer/Room/SKILL.md +244 -0
  78. package/skills/Data Layer/SQLite/SKILL.md +255 -0
  79. package/skills/Data Layer/Sync Engine/SKILL.md +268 -0
  80. package/skills/Dependency Injection/Dagger/SKILL.md +283 -0
  81. package/skills/Dependency Injection/Hilt/SKILL.md +345 -0
  82. package/skills/Dependency Injection/Koin/SKILL.md +282 -0
  83. package/skills/Developer Experience/Detekt/SKILL.md +272 -0
  84. package/skills/Developer Experience/Lint Rule/SKILL.md +281 -0
  85. package/skills/Google Ecosystem/Analytics/SKILL.md +281 -0
  86. package/skills/Google Ecosystem/Crashlytics/SKILL.md +234 -0
  87. package/skills/Google Ecosystem/Firebase/SKILL.md +200 -0
  88. package/skills/Google Ecosystem/Firebase Messaging/SKILL.md +266 -0
  89. package/skills/Media/Audio/SKILL.md +257 -0
  90. package/skills/Media/Camera/SKILL.md +229 -0
  91. package/skills/Media/CameraX/SKILL.md +295 -0
  92. package/skills/Media/ExoPlayer/SKILL.md +258 -0
  93. package/skills/Media/Video/SKILL.md +228 -0
  94. package/skills/Meta Skills/Domain Error Model/SKILL.md +238 -0
  95. package/skills/Meta Skills/Error Handling/SKILL.md +255 -0
  96. package/skills/Meta Skills/Error Mapping/SKILL.md +232 -0
  97. package/skills/Meta Skills/Failure Strategy/SKILL.md +294 -0
  98. package/skills/Meta Skills/Migration Strategy/SKILL.md +305 -0
  99. package/skills/Meta Skills/User Friendly Errors/SKILL.md +334 -0
  100. package/skills/Navigation/Deep Navigation/SKILL.md +209 -0
  101. package/skills/Navigation/Navigation/SKILL.md +215 -0
  102. package/skills/Navigation/Nested Navigation/SKILL.md +214 -0
  103. package/skills/Networking/API Contract/SKILL.md +220 -0
  104. package/skills/Networking/Authentication/SKILL.md +210 -0
  105. package/skills/Networking/Certificate Pinning/SKILL.md +167 -0
  106. package/skills/Networking/Fallback Strategy/SKILL.md +182 -0
  107. package/skills/Networking/Ktor/SKILL.md +219 -0
  108. package/skills/Networking/Multipart Upload/SKILL.md +213 -0
  109. package/skills/Networking/OkHttp/SKILL.md +193 -0
  110. package/skills/Networking/REST/SKILL.md +178 -0
  111. package/skills/Networking/Rate Limiting/SKILL.md +170 -0
  112. package/skills/Networking/Retrofit/SKILL.md +241 -0
  113. package/skills/Networking/Retry-Backoff/SKILL.md +181 -0
  114. package/skills/Networking/Server-Sent Events (SSE)/SKILL.md +196 -0
  115. package/skills/Networking/WebSocket/SKILL.md +224 -0
  116. package/skills/Observability/Crash Reporting/SKILL.md +219 -0
  117. package/skills/Observability/Logging/SKILL.md +168 -0
  118. package/skills/Observability/Metrics/SKILL.md +227 -0
  119. package/skills/Observability/Structured Logging/SKILL.md +234 -0
  120. package/skills/Performance/ANR Prevention/SKILL.md +192 -0
  121. package/skills/Performance/Allocation Optimization/SKILL.md +179 -0
  122. package/skills/Performance/App Startup/SKILL.md +183 -0
  123. package/skills/Performance/Baseline Profile/SKILL.md +205 -0
  124. package/skills/Performance/Battery Optimization/SKILL.md +192 -0
  125. package/skills/Performance/Benchmark/SKILL.md +182 -0
  126. package/skills/Performance/Bitmap Optimization/SKILL.md +178 -0
  127. package/skills/Performance/Compose Optimization/SKILL.md +187 -0
  128. package/skills/Performance/Heap Management/SKILL.md +184 -0
  129. package/skills/Performance/Macrobenchmark/SKILL.md +214 -0
  130. package/skills/Performance/Memory Leak Prevention/SKILL.md +218 -0
  131. package/skills/Performance/Rendering Performance/SKILL.md +205 -0
  132. package/skills/Performance/Startup Optimization/SKILL.md +219 -0
  133. package/skills/Security/Biometric/SKILL.md +224 -0
  134. package/skills/Security/Certificate Transparency/SKILL.md +158 -0
  135. package/skills/Security/Cryptography/SKILL.md +244 -0
  136. package/skills/Security/Encrypted Storage/SKILL.md +273 -0
  137. package/skills/Security/Frida Detection/SKILL.md +230 -0
  138. package/skills/Security/Hook Detection/SKILL.md +197 -0
  139. package/skills/Security/Keystore/SKILL.md +272 -0
  140. package/skills/Security/Network Security Config/SKILL.md +186 -0
  141. package/skills/Security/Obfuscation/SKILL.md +226 -0
  142. package/skills/Security/Proguard/SKILL.md +202 -0
  143. package/skills/Security/R8/SKILL.md +234 -0
  144. package/skills/Security/Reverse Engineering Resistance/SKILL.md +267 -0
  145. package/skills/Security/Root Detection/SKILL.md +220 -0
  146. package/skills/Security/Secure Networking/SKILL.md +220 -0
  147. package/skills/System Integration/AlarmManager/SKILL.md +182 -0
  148. package/skills/System Integration/App Widget/SKILL.md +182 -0
  149. package/skills/System Integration/Deep Link/SKILL.md +187 -0
  150. package/skills/System Integration/Foreground Service/SKILL.md +212 -0
  151. package/skills/System Integration/Notification/SKILL.md +237 -0
  152. package/skills/System Integration/WorkManager/SKILL.md +256 -0
  153. package/skills/System Integration/clipboard/SKILL.md +155 -0
  154. package/skills/System Integration/share-intent/SKILL.md +182 -0
  155. package/skills/Testing/Compose Testing/SKILL.md +296 -0
  156. package/skills/Testing/Espresso/SKILL.md +292 -0
  157. package/skills/Testing/Fake Data/SKILL.md +245 -0
  158. package/skills/Testing/Integration Testing/SKILL.md +288 -0
  159. package/skills/Testing/Mocking/SKILL.md +229 -0
  160. package/skills/Testing/Snapshot Testing/SKILL.md +259 -0
  161. package/skills/Testing/UI Testing/SKILL.md +293 -0
  162. package/skills/Testing/Unit Testing/SKILL.md +309 -0
  163. package/skills/UI System/Bottom Sheet Patterns/SKILL.md +279 -0
  164. package/skills/UI System/Compose/SKILL.md +296 -0
  165. package/skills/UI System/Compose Animation/SKILL.md +281 -0
  166. package/skills/UI System/Compose Multiplatform/SKILL.md +261 -0
  167. package/skills/UI System/Compose Navigation/SKILL.md +255 -0
  168. package/skills/UI System/Compose Performance/SKILL.md +274 -0
  169. package/skills/UI System/Design System/SKILL.md +217 -0
  170. package/skills/UI System/Empty State Strategy/SKILL.md +208 -0
  171. package/skills/UI System/Keyboard Navigation/SKILL.md +214 -0
  172. package/skills/UI System/Loading Strategy/SKILL.md +254 -0
  173. package/skills/UI System/Material 3/SKILL.md +279 -0
  174. package/skills/UI System/RTL/SKILL.md +179 -0
  175. package/src/index.ts +182 -0
  176. package/tsconfig.json +19 -0
@@ -0,0 +1,249 @@
1
+ ---
2
+ name: offline-first
3
+ description: >
4
+ Offline-first architecture for Android apps.
5
+ Load this skill when designing apps that must work without network,
6
+ implementing local-first data access, syncing data with a remote server,
7
+ handling network availability changes, or designing cache invalidation strategy.
8
+ ---
9
+
10
+ # Offline First
11
+
12
+ ## Overview
13
+
14
+ Offline-first means the app reads and writes to local storage first. The network is treated as an unreliable sync mechanism — not a requirement. The local database is the single source of truth; the UI observes local data, and a sync engine reconciles local state with the remote server in the background.
15
+
16
+ ---
17
+
18
+ ## Core Principles
19
+
20
+ - **Local DB is the source of truth** — UI never reads directly from network
21
+ - **Write locally first** — operations succeed immediately, sync later
22
+ - **Observe local, sync remotely** — UI observes Room/DataStore, background sync updates local DB
23
+ - **Surface sync status** — UI shows sync state, errors, last-synced time
24
+ - **Idempotent sync** — syncing the same data twice must not corrupt state
25
+
26
+ ---
27
+
28
+ ## Architecture Pattern
29
+
30
+ ```
31
+ UI → ViewModel → UseCase → Repository
32
+
33
+ ┌─────────┴─────────┐
34
+ ▼ ▼
35
+ LocalDataSource RemoteDataSource
36
+ (Room / DataStore) (Retrofit / Ktor)
37
+
38
+
39
+ Single Source of Truth
40
+ (UI observes this)
41
+
42
+ SyncManager (WorkManager) periodically:
43
+ 1. Fetches remote data
44
+ 2. Writes to LocalDataSource
45
+ 3. UI updates automatically via Flow
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Repository Implementation
51
+
52
+ ```kotlin
53
+ // ✅ Repository — reads local, triggers sync, exposes Flow
54
+ class UserRepositoryImpl @Inject constructor(
55
+ private val localDataSource: UserLocalDataSource,
56
+ private val remoteDataSource: UserRemoteDataSource,
57
+ private val syncManager: SyncManager
58
+ ) : UserRepository {
59
+
60
+ // ✅ Always return local Flow — UI updates automatically when sync writes to DB
61
+ override fun observeUsers(): Flow<List<User>> =
62
+ localDataSource.observeAll().map { entities -> entities.map { it.toDomain() } }
63
+
64
+ // ✅ Read-through cache
65
+ override suspend fun getUser(id: String): Result<User> = runCatching {
66
+ localDataSource.getById(id)?.toDomain()
67
+ ?: fetchAndCacheUser(id)
68
+ }
69
+
70
+ // ✅ Write-through: write locally first, enqueue remote sync
71
+ override suspend fun updateUser(user: User): Result<Unit> = runCatching {
72
+ localDataSource.upsert(user.toEntity().copy(syncStatus = SyncStatus.PENDING))
73
+ syncManager.enqueueUserSync()
74
+ }
75
+
76
+ private suspend fun fetchAndCacheUser(id: String): User {
77
+ val dto = remoteDataSource.getUser(id)
78
+ val entity = dto.toDomain().toEntity().copy(syncStatus = SyncStatus.SYNCED)
79
+ localDataSource.upsert(entity)
80
+ return entity.toDomain()
81
+ }
82
+ }
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Sync Status Tracking
88
+
89
+ ```kotlin
90
+ // ✅ Track sync state per entity
91
+ enum class SyncStatus { SYNCED, PENDING, FAILED }
92
+
93
+ @Entity(tableName = "users")
94
+ data class UserEntity(
95
+ @PrimaryKey val id: String,
96
+ val name: String,
97
+ val email: String,
98
+ val syncStatus: SyncStatus = SyncStatus.SYNCED,
99
+ val lastModified: Long = System.currentTimeMillis()
100
+ )
101
+
102
+ // ✅ DAO queries for pending sync
103
+ @Dao
104
+ interface UserDao {
105
+ @Query("SELECT * FROM users WHERE syncStatus = 'PENDING'")
106
+ suspend fun getPendingSync(): List<UserEntity>
107
+
108
+ @Query("UPDATE users SET syncStatus = :status WHERE id = :id")
109
+ suspend fun updateSyncStatus(id: String, status: SyncStatus)
110
+ }
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Sync Engine with WorkManager
116
+
117
+ ```kotlin
118
+ // ✅ Periodic sync via WorkManager
119
+ class UserSyncWorker @AssistedInject constructor(
120
+ @Assisted context: Context,
121
+ @Assisted workerParams: WorkerParameters,
122
+ private val userRepository: UserRepository,
123
+ private val remoteDataSource: UserRemoteDataSource,
124
+ private val localDataSource: UserLocalDataSource
125
+ ) : CoroutineWorker(context, workerParams) {
126
+
127
+ override suspend fun doWork(): Result {
128
+ return try {
129
+ // Push pending changes
130
+ val pending = localDataSource.getPendingSync()
131
+ pending.forEach { entity ->
132
+ remoteDataSource.updateUser(entity.toDto())
133
+ localDataSource.updateSyncStatus(entity.id, SyncStatus.SYNCED)
134
+ }
135
+
136
+ // Pull remote changes
137
+ val remoteUsers = remoteDataSource.getUsers()
138
+ localDataSource.upsertAll(remoteUsers.map { it.toDomain().toEntity() })
139
+
140
+ Result.success()
141
+ } catch (e: Exception) {
142
+ if (runAttemptCount < 3) Result.retry() else Result.failure()
143
+ }
144
+ }
145
+
146
+ companion object {
147
+ fun schedule(workManager: WorkManager) {
148
+ val request = PeriodicWorkRequestBuilder<UserSyncWorker>(15, TimeUnit.MINUTES)
149
+ .setConstraints(
150
+ Constraints.Builder()
151
+ .setRequiredNetworkType(NetworkType.CONNECTED)
152
+ .build()
153
+ )
154
+ .build()
155
+ workManager.enqueueUniquePeriodicWork(
156
+ "user_sync",
157
+ ExistingPeriodicWorkPolicy.KEEP,
158
+ request
159
+ )
160
+ }
161
+ }
162
+ }
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Network Connectivity Observation
168
+
169
+ ```kotlin
170
+ // ✅ Observe network state
171
+ class NetworkMonitor @Inject constructor(
172
+ @ApplicationContext private val context: Context
173
+ ) {
174
+ val isOnline: Flow<Boolean> = callbackFlow {
175
+ val manager = context.getSystemService(ConnectivityManager::class.java)
176
+
177
+ val callback = object : ConnectivityManager.NetworkCallback() {
178
+ override fun onAvailable(network: Network) { trySend(true) }
179
+ override fun onLost(network: Network) { trySend(false) }
180
+ }
181
+
182
+ val request = NetworkRequest.Builder()
183
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
184
+ .build()
185
+
186
+ manager.registerNetworkCallback(request, callback)
187
+ trySend(manager.activeNetwork != null)
188
+
189
+ awaitClose { manager.unregisterNetworkCallback(callback) }
190
+ }.distinctUntilChanged()
191
+ }
192
+
193
+ // ✅ Surface network state in ViewModel
194
+ @HiltViewModel
195
+ class HomeViewModel @Inject constructor(
196
+ private val networkMonitor: NetworkMonitor
197
+ ) : ViewModel() {
198
+
199
+ val isOffline: StateFlow<Boolean> = networkMonitor.isOnline
200
+ .map { !it }
201
+ .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), false)
202
+ }
203
+ ```
204
+
205
+ ---
206
+
207
+ ## UI: Offline Banner
208
+
209
+ ```kotlin
210
+ // ✅ Show offline indicator when not connected
211
+ @Composable
212
+ fun OfflineBanner(isOffline: Boolean) {
213
+ AnimatedVisibility(visible = isOffline) {
214
+ Surface(color = MaterialTheme.colorScheme.errorContainer) {
215
+ Row(
216
+ modifier = Modifier
217
+ .fillMaxWidth()
218
+ .padding(horizontal = 16.dp, vertical = 8.dp),
219
+ horizontalArrangement = Arrangement.Center
220
+ ) {
221
+ Icon(Icons.Default.WifiOff, contentDescription = null)
222
+ Spacer(Modifier.width(8.dp))
223
+ Text("You're offline", style = MaterialTheme.typography.labelMedium)
224
+ }
225
+ }
226
+ }
227
+ }
228
+ ```
229
+
230
+ ---
231
+
232
+ ## Anti-Patterns
233
+
234
+ - Reading directly from network in ViewModel — always read from local source
235
+ - No sync status tracking — can't tell which records need to be pushed
236
+ - Replacing entire local DB on every sync — causes UI flicker; use upsert
237
+ - Syncing on main thread — always use WorkManager or background coroutine
238
+ - No conflict resolution strategy — when local and remote diverge, there must be a rule
239
+
240
+ ---
241
+
242
+ ## Related Skills
243
+
244
+ - `room` — local database for offline storage
245
+ - `sync-engine` — advanced sync patterns and conflict resolution
246
+ - `workmanager` — background sync scheduling
247
+ - `conflict-resolution` — handling remote/local data conflicts
248
+ - `repository-pattern` — repository wiring for offline-first
249
+ - `cache-strategy` — cache invalidation and expiry
@@ -0,0 +1,216 @@
1
+ ---
2
+ name: repository-pattern
3
+ description: >
4
+ Repository pattern for Android data access abstraction.
5
+ Load this skill when implementing a Repository, defining its interface,
6
+ coordinating between local and remote data sources, handling caching logic,
7
+ or structuring the data layer of a feature.
8
+ ---
9
+
10
+ # Repository Pattern
11
+
12
+ ## Overview
13
+
14
+ The Repository pattern abstracts data sources behind a single interface. The ViewModel and UseCases never know whether data comes from Room, Retrofit, DataStore, or memory cache — they only interact with the Repository interface defined in the Domain layer. The implementation in the Data layer coordinates between local and remote sources.
15
+
16
+ ---
17
+
18
+ ## Core Principles
19
+
20
+ - Repository **interface** lives in Domain — pure Kotlin, no Android dependencies
21
+ - Repository **implementation** lives in Data — knows about Room, Retrofit, etc.
22
+ - Repository is the **only entry point** to data — no direct DAO or API calls from UseCases
23
+ - Repositories return **Domain models** — never DTOs or Entities
24
+ - One repository per **aggregate root** — not per screen or per DAO
25
+
26
+ ---
27
+
28
+ ## Interface (Domain Layer)
29
+
30
+ ```kotlin
31
+ // ✅ Interface in domain — clean, no framework dependencies
32
+ interface UserRepository {
33
+ // Reactive — for UI observation
34
+ fun observeUsers(): Flow<List<User>>
35
+ fun observeUser(id: String): Flow<User?>
36
+
37
+ // Suspend — for one-time reads and writes
38
+ suspend fun getUser(id: String): Result<User>
39
+ suspend fun getUsers(): Result<List<User>>
40
+ suspend fun createUser(user: User): Result<User>
41
+ suspend fun updateUser(user: User): Result<Unit>
42
+ suspend fun deleteUser(id: String): Result<Unit>
43
+
44
+ // Domain operations
45
+ suspend fun searchUsers(query: String): Result<List<User>>
46
+ suspend fun syncUsers(): Result<Unit>
47
+ }
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Implementation (Data Layer)
53
+
54
+ ```kotlin
55
+ // ✅ Implementation in data layer — coordinates sources
56
+ class UserRepositoryImpl @Inject constructor(
57
+ private val userDao: UserDao,
58
+ private val userApiService: UserApiService,
59
+ private val dispatcher: CoroutineDispatcher = Dispatchers.IO
60
+ ) : UserRepository {
61
+
62
+ // ✅ Observe local DB — UI updates automatically when DB changes
63
+ override fun observeUsers(): Flow<List<User>> =
64
+ userDao.observeAll()
65
+ .map { entities -> entities.map { it.toDomain() } }
66
+ .flowOn(dispatcher)
67
+
68
+ // ✅ Cache-first read
69
+ override suspend fun getUser(id: String): Result<User> = withContext(dispatcher) {
70
+ runCatching {
71
+ userDao.getById(id)?.toDomain()
72
+ ?: fetchAndCacheUser(id)
73
+ }
74
+ }
75
+
76
+ // ✅ Write to local + remote
77
+ override suspend fun createUser(user: User): Result<User> = withContext(dispatcher) {
78
+ runCatching {
79
+ val dto = userApiService.createUser(user.toCreateRequest())
80
+ val created = dto.toDomain()
81
+ userDao.insert(created.toEntity())
82
+ created
83
+ }
84
+ }
85
+
86
+ override suspend fun updateUser(user: User): Result<Unit> = withContext(dispatcher) {
87
+ runCatching {
88
+ userApiService.updateUser(user.id, user.toUpdateRequest())
89
+ userDao.upsert(user.toEntity())
90
+ }
91
+ }
92
+
93
+ override suspend fun deleteUser(id: String): Result<Unit> = withContext(dispatcher) {
94
+ runCatching {
95
+ userApiService.deleteUser(id)
96
+ userDao.deleteById(id)
97
+ }
98
+ }
99
+
100
+ override suspend fun syncUsers(): Result<Unit> = withContext(dispatcher) {
101
+ runCatching {
102
+ val dtos = userApiService.getUsers()
103
+ val entities = dtos.map { it.toDomain().toEntity() }
104
+ userDao.replaceAll(entities)
105
+ }
106
+ }
107
+
108
+ private suspend fun fetchAndCacheUser(id: String): User {
109
+ val dto = userApiService.getUser(id)
110
+ val domain = dto.toDomain()
111
+ userDao.insert(domain.toEntity())
112
+ return domain
113
+ }
114
+ }
115
+ ```
116
+
117
+ ---
118
+
119
+ ## Hilt Binding
120
+
121
+ ```kotlin
122
+ // ✅ Bind interface to implementation
123
+ @Module
124
+ @InstallIn(SingletonComponent::class)
125
+ abstract class UserRepositoryModule {
126
+
127
+ @Binds
128
+ @Singleton
129
+ abstract fun bindUserRepository(
130
+ impl: UserRepositoryImpl
131
+ ): UserRepository
132
+ }
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Repository Strategies
138
+
139
+ ```kotlin
140
+ // ✅ Strategy 1: Network-first (always fresh data)
141
+ override suspend fun getUser(id: String): Result<User> = runCatching {
142
+ val dto = userApiService.getUser(id)
143
+ val domain = dto.toDomain()
144
+ userDao.upsert(domain.toEntity())
145
+ domain
146
+ }
147
+
148
+ // ✅ Strategy 2: Cache-first (fast, offline-capable)
149
+ override suspend fun getUser(id: String): Result<User> = runCatching {
150
+ userDao.getById(id)?.toDomain() ?: run {
151
+ val dto = userApiService.getUser(id)
152
+ val domain = dto.toDomain()
153
+ userDao.insert(domain.toEntity())
154
+ domain
155
+ }
156
+ }
157
+
158
+ // ✅ Strategy 3: Cache-then-network (show cached, refresh in background)
159
+ override fun observeUserWithRefresh(id: String): Flow<User> = flow {
160
+ userDao.getById(id)?.toDomain()?.let { emit(it) }
161
+ val refreshed = userApiService.getUser(id).toDomain()
162
+ userDao.upsert(refreshed.toEntity())
163
+ emit(refreshed)
164
+ }
165
+ ```
166
+
167
+ ---
168
+
169
+ ## Fake Repository for Testing
170
+
171
+ ```kotlin
172
+ // ✅ Fake implementation for unit tests — no Room, no Retrofit
173
+ class FakeUserRepository : UserRepository {
174
+ private val users = MutableStateFlow<List<User>>(emptyList())
175
+ var shouldThrow = false
176
+
177
+ override fun observeUsers(): Flow<List<User>> = users
178
+
179
+ override suspend fun getUser(id: String): Result<User> {
180
+ if (shouldThrow) return Result.failure(Exception("Network error"))
181
+ return users.value.find { it.id == id }
182
+ ?.let { Result.success(it) }
183
+ ?: Result.failure(Exception("Not found"))
184
+ }
185
+
186
+ override suspend fun createUser(user: User): Result<User> {
187
+ users.update { it + user }
188
+ return Result.success(user)
189
+ }
190
+
191
+ fun emit(userList: List<User>) {
192
+ users.value = userList
193
+ }
194
+ }
195
+ ```
196
+
197
+ ---
198
+
199
+ ## Anti-Patterns
200
+
201
+ - DAO called directly from ViewModel or UseCase — always go through Repository
202
+ - Repository returning Entity or DTO — always map to Domain model before returning
203
+ - One Repository per screen — one Repository per aggregate root (User, Order, Product)
204
+ - Repository containing UI logic — Repository only handles data access and mapping
205
+ - Not using `flowOn(dispatcher)` — DB and network operations must not run on Main thread
206
+
207
+ ---
208
+
209
+ ## Related Skills
210
+
211
+ - `clean-architecture` — where Repository fits in layer structure
212
+ - `dao` — DAO patterns the Repository coordinates
213
+ - `dto-mapping` — mapping between DTO/Entity and Domain model
214
+ - `use-case-design` — UseCases that call Repositories
215
+ - `cache-strategy` — cache invalidation and expiry policies
216
+ - `offline-first` — Repository design for offline-capable apps