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,236 @@
1
+ ---
2
+ name: domain-modeling
3
+ description: >
4
+ Domain modeling for Android Clean Architecture projects.
5
+ Load this skill when designing domain entities, defining business rules
6
+ in the domain layer, modeling relationships between domain objects,
7
+ choosing between Entity vs Value Object, or structuring the domain package.
8
+ ---
9
+
10
+ # Domain Modeling
11
+
12
+ ## Overview
13
+
14
+ Domain modeling defines the core business concepts of the application as pure Kotlin classes — free of Android framework, database annotations, or network serialization. The domain model is the language of the business, shared across all layers via mapping.
15
+
16
+ ---
17
+
18
+ ## Core Principles
19
+
20
+ - Domain models are **pure Kotlin** — no `@Entity`, `@SerialName`, `@Parcelize`
21
+ - Domain models express **business concepts** — not database tables or API contracts
22
+ - **Behavior belongs in domain** — validation, business rules, computations live here
23
+ - **Immutable** — use `val`, use `data class`, use `copy()` for changes
24
+ - Domain models are the **shared language** — same names used in UI, DB, API (via mapping)
25
+
26
+ ---
27
+
28
+ ## Entity vs Value Object
29
+
30
+ | | Entity | Value Object |
31
+ | --------- | ------------------------ | -------------------------- |
32
+ | Identity | Has unique `id` | Defined by its values |
33
+ | Equality | By `id` | By all fields |
34
+ | Lifecycle | Persisted, tracked | Transient, replaceable |
35
+ | Example | `User(id=1, name="Ali")` | `Email("ali@example.com")` |
36
+
37
+ ---
38
+
39
+ ## Domain Entity
40
+
41
+ ```kotlin
42
+ // ✅ Entity — has identity, represents a business object
43
+ data class User(
44
+ val id: String,
45
+ val name: String,
46
+ val email: Email, // ✅ use Value Object for validated fields
47
+ val role: UserRole,
48
+ val status: UserStatus,
49
+ val createdAt: Instant
50
+ ) {
51
+ // ✅ Business rules as functions — not in ViewModel
52
+ fun canEdit(actor: User): Boolean =
53
+ actor.id == id || actor.role == UserRole.ADMIN
54
+
55
+ fun isActive(): Boolean = status == UserStatus.ACTIVE
56
+
57
+ fun withRole(newRole: UserRole): User = copy(role = newRole)
58
+ }
59
+
60
+ data class Order(
61
+ val id: String,
62
+ val customerId: String,
63
+ val items: List<OrderItem>,
64
+ val status: OrderStatus,
65
+ val placedAt: Instant
66
+ ) {
67
+ val totalAmount: Money
68
+ get() = items.fold(Money.ZERO) { acc, item -> acc + item.subtotal }
69
+
70
+ fun canBeCancelled(): Boolean =
71
+ status == OrderStatus.PENDING || status == OrderStatus.PROCESSING
72
+
73
+ fun withStatus(newStatus: OrderStatus): Order = copy(status = newStatus)
74
+ }
75
+ ```
76
+
77
+ ---
78
+
79
+ ## Value Object
80
+
81
+ ```kotlin
82
+ // ✅ Value Object — validated, no identity
83
+ @JvmInline
84
+ value class Email(val value: String) {
85
+ init {
86
+ require(value.contains("@")) { "Invalid email: $value" }
87
+ require(value.length <= 254) { "Email too long" }
88
+ }
89
+ }
90
+
91
+ @JvmInline
92
+ value class UserId(val value: String) {
93
+ init { require(value.isNotBlank()) { "UserId cannot be blank" } }
94
+ }
95
+
96
+ data class Money(
97
+ val amount: Long, // stored in smallest unit (cents/rials)
98
+ val currency: Currency
99
+ ) {
100
+ operator fun plus(other: Money): Money {
101
+ require(currency == other.currency) { "Currency mismatch" }
102
+ return copy(amount = amount + other.amount)
103
+ }
104
+
105
+ fun isPositive(): Boolean = amount > 0
106
+
107
+ companion object {
108
+ val ZERO = Money(0, Currency.IRR)
109
+ }
110
+ }
111
+
112
+ data class DateRange(
113
+ val start: LocalDate,
114
+ val end: LocalDate
115
+ ) {
116
+ init { require(!end.isBefore(start)) { "End must be after start" } }
117
+
118
+ fun contains(date: LocalDate): Boolean = !date.isBefore(start) && !date.isAfter(end)
119
+ fun durationDays(): Long = ChronoUnit.DAYS.between(start, end)
120
+ }
121
+ ```
122
+
123
+ ---
124
+
125
+ ## Enums and Sealed Classes
126
+
127
+ ```kotlin
128
+ // ✅ Simple states — enum
129
+ enum class UserRole { ADMIN, MEMBER, GUEST }
130
+ enum class UserStatus { ACTIVE, SUSPENDED, DELETED }
131
+ enum class OrderStatus { PENDING, PROCESSING, SHIPPED, DELIVERED, CANCELLED }
132
+
133
+ // ✅ States with data — sealed class
134
+ sealed interface PaymentResult {
135
+ data class Success(val transactionId: String, val amount: Money) : PaymentResult
136
+ data class Failed(val reason: String, val code: Int) : PaymentResult
137
+ data object Cancelled : PaymentResult
138
+ }
139
+
140
+ sealed interface ValidationResult {
141
+ data object Valid : ValidationResult
142
+ data class Invalid(val errors: List<String>) : ValidationResult
143
+
144
+ fun isValid(): Boolean = this is Valid
145
+ }
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Aggregate Design
151
+
152
+ ```kotlin
153
+ // ✅ Aggregate root — owns its children, enforces consistency
154
+ data class Cart(
155
+ val id: String,
156
+ val customerId: String,
157
+ val items: List<CartItem> = emptyList()
158
+ ) {
159
+ val totalAmount: Money
160
+ get() = items.fold(Money.ZERO) { acc, item -> acc + item.subtotal }
161
+
162
+ val itemCount: Int get() = items.sumOf { it.quantity }
163
+
164
+ fun addItem(product: Product, quantity: Int): Cart {
165
+ require(quantity > 0) { "Quantity must be positive" }
166
+ val existing = items.find { it.productId == product.id }
167
+ return if (existing != null) {
168
+ copy(items = items.map {
169
+ if (it.productId == product.id)
170
+ it.copy(quantity = it.quantity + quantity)
171
+ else it
172
+ })
173
+ } else {
174
+ copy(items = items + CartItem(product.id, product.name, product.price, quantity))
175
+ }
176
+ }
177
+
178
+ fun removeItem(productId: String): Cart =
179
+ copy(items = items.filter { it.productId != productId })
180
+
181
+ fun clear(): Cart = copy(items = emptyList())
182
+ }
183
+
184
+ data class CartItem(
185
+ val productId: String,
186
+ val productName: String,
187
+ val unitPrice: Money,
188
+ val quantity: Int
189
+ ) {
190
+ val subtotal: Money get() = Money(unitPrice.amount * quantity, unitPrice.currency)
191
+ }
192
+ ```
193
+
194
+ ---
195
+
196
+ ## Domain Package Structure
197
+
198
+ ```
199
+ domain/
200
+ ├── model/
201
+ │ ├── User.kt
202
+ │ ├── Order.kt
203
+ │ ├── Cart.kt
204
+ │ └── Product.kt
205
+ ├── value/
206
+ │ ├── Email.kt
207
+ │ ├── Money.kt
208
+ │ ├── DateRange.kt
209
+ │ └── UserId.kt
210
+ ├── repository/
211
+ │ ├── UserRepository.kt
212
+ │ └── OrderRepository.kt
213
+ └── usecase/
214
+ ├── GetUserUseCase.kt
215
+ └── PlaceOrderUseCase.kt
216
+ ```
217
+
218
+ ---
219
+
220
+ ## Anti-Patterns
221
+
222
+ - Domain models with `@Entity` or `@SerialName` — domain must not know about persistence or network
223
+ - Anemic domain model — models with only `data class` fields and no behavior; business rules scattered in ViewModel
224
+ - Using `String` for everything — wrap validated concepts in Value Objects (`Email`, `UserId`)
225
+ - Mutable domain models (`var` fields) — always `val` + `copy()`
226
+ - Domain model inheriting from framework class — domain is pure Kotlin only
227
+
228
+ ---
229
+
230
+ ## Related Skills
231
+
232
+ - `entity-design` — designing Room entities that map to domain models
233
+ - `value-object` — in-depth Value Object patterns
234
+ - `clean-architecture` — where domain sits in the layer structure
235
+ - `dto-mapping` — mapping between domain and data layer models
236
+ - `use-case-design` — business operations that act on domain models
@@ -0,0 +1,243 @@
1
+ ---
2
+ name: entity-design
3
+ description: >
4
+ Designing Room database entities for Android.
5
+ Load this skill when defining @Entity classes, choosing primary keys,
6
+ modeling relationships (one-to-many, many-to-many), using type converters,
7
+ handling nullable columns, or mapping between domain models and entities.
8
+ ---
9
+
10
+ # Entity Design
11
+
12
+ ## Overview
13
+
14
+ Room entities are data classes annotated with `@Entity` that map directly to SQLite tables. Entities live in the Data layer and must never leak into Domain or Presentation. The mapping between Entity and Domain model is always explicit.
15
+
16
+ ---
17
+
18
+ ## Core Principles
19
+
20
+ - Entities live in **Data layer only** — never imported by Domain or Presentation
21
+ - Entity fields map to **SQLite columns** — use types SQLite supports natively or via TypeConverter
22
+ - Every entity has a **primary key** — prefer `String` UUID over auto-generated `Int` for distributed data
23
+ - **Nullable columns** only when the data is truly optional — not as a lazy default
24
+ - Entity names reflect **storage** — Domain names reflect **business concepts**
25
+
26
+ ---
27
+
28
+ ## Basic Entity
29
+
30
+ ```kotlin
31
+ // ✅ Standard entity
32
+ @Entity(tableName = "users")
33
+ data class UserEntity(
34
+ @PrimaryKey val id: String,
35
+ @ColumnInfo(name = "full_name") val name: String,
36
+ @ColumnInfo(name = "email_address") val email: String,
37
+ @ColumnInfo(name = "role") val role: String, // store enum as String
38
+ @ColumnInfo(name = "is_active") val isActive: Boolean,
39
+ @ColumnInfo(name = "created_at") val createdAt: Long, // store Instant as epoch millis
40
+ @ColumnInfo(name = "updated_at") val updatedAt: Long
41
+ )
42
+
43
+ // ✅ Mapping to/from domain
44
+ fun UserEntity.toDomain(): User = User(
45
+ id = id,
46
+ name = name,
47
+ email = Email(email),
48
+ role = UserRole.valueOf(role),
49
+ status = if (isActive) UserStatus.ACTIVE else UserStatus.SUSPENDED,
50
+ createdAt = Instant.ofEpochMilli(createdAt)
51
+ )
52
+
53
+ fun User.toEntity(): UserEntity = UserEntity(
54
+ id = id,
55
+ name = name,
56
+ email = email.value,
57
+ role = role.name,
58
+ isActive = status == UserStatus.ACTIVE,
59
+ createdAt = createdAt.toEpochMilli(),
60
+ updatedAt = System.currentTimeMillis()
61
+ )
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Primary Keys
67
+
68
+ ```kotlin
69
+ // ✅ String UUID — preferred for distributed/synced data
70
+ @Entity(tableName = "orders")
71
+ data class OrderEntity(
72
+ @PrimaryKey val id: String = UUID.randomUUID().toString(),
73
+ ...
74
+ )
75
+
76
+ // ✅ Auto-increment Int — for local-only data with no sync
77
+ @Entity(tableName = "drafts")
78
+ data class DraftEntity(
79
+ @PrimaryKey(autoGenerate = true) val id: Int = 0,
80
+ val content: String,
81
+ val createdAt: Long
82
+ )
83
+
84
+ // ✅ Composite primary key
85
+ @Entity(
86
+ tableName = "user_roles",
87
+ primaryKeys = ["user_id", "role_id"]
88
+ )
89
+ data class UserRoleEntity(
90
+ @ColumnInfo(name = "user_id") val userId: String,
91
+ @ColumnInfo(name = "role_id") val roleId: String,
92
+ @ColumnInfo(name = "assigned_at") val assignedAt: Long
93
+ )
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Relationships
99
+
100
+ ```kotlin
101
+ // ✅ One-to-many: Order has many OrderItems
102
+ @Entity(tableName = "orders")
103
+ data class OrderEntity(
104
+ @PrimaryKey val id: String,
105
+ @ColumnInfo(name = "customer_id") val customerId: String,
106
+ @ColumnInfo(name = "status") val status: String,
107
+ @ColumnInfo(name = "placed_at") val placedAt: Long
108
+ )
109
+
110
+ @Entity(
111
+ tableName = "order_items",
112
+ foreignKeys = [
113
+ ForeignKey(
114
+ entity = OrderEntity::class,
115
+ parentColumns = ["id"],
116
+ childColumns = ["order_id"],
117
+ onDelete = ForeignKey.CASCADE // delete items when order deleted
118
+ )
119
+ ],
120
+ indices = [Index("order_id")] // ✅ index foreign key columns
121
+ )
122
+ data class OrderItemEntity(
123
+ @PrimaryKey val id: String,
124
+ @ColumnInfo(name = "order_id") val orderId: String,
125
+ @ColumnInfo(name = "product_id") val productId: String,
126
+ @ColumnInfo(name = "quantity") val quantity: Int,
127
+ @ColumnInfo(name = "unit_price") val unitPrice: Long
128
+ )
129
+
130
+ // ✅ Relation data class for queries
131
+ data class OrderWithItems(
132
+ @Embedded val order: OrderEntity,
133
+ @Relation(
134
+ parentColumn = "id",
135
+ entityColumn = "order_id"
136
+ )
137
+ val items: List<OrderItemEntity>
138
+ )
139
+
140
+ // ✅ Many-to-many via junction table
141
+ @Entity(
142
+ tableName = "product_tags",
143
+ primaryKeys = ["product_id", "tag_id"]
144
+ )
145
+ data class ProductTagCrossRef(
146
+ @ColumnInfo(name = "product_id") val productId: String,
147
+ @ColumnInfo(name = "tag_id") val tagId: String
148
+ )
149
+
150
+ data class ProductWithTags(
151
+ @Embedded val product: ProductEntity,
152
+ @Relation(
153
+ parentColumn = "id",
154
+ entityColumn = "id",
155
+ associateBy = Junction(ProductTagCrossRef::class,
156
+ parentColumn = "product_id",
157
+ entityColumn = "tag_id")
158
+ )
159
+ val tags: List<TagEntity>
160
+ )
161
+ ```
162
+
163
+ ---
164
+
165
+ ## Type Converters
166
+
167
+ ```kotlin
168
+ // ✅ TypeConverter for types Room can't store natively
169
+ class Converters {
170
+
171
+ // List<String>
172
+ @TypeConverter
173
+ fun fromStringList(value: List<String>): String = value.joinToString(",")
174
+
175
+ @TypeConverter
176
+ fun toStringList(value: String): List<String> =
177
+ if (value.isBlank()) emptyList() else value.split(",")
178
+
179
+ // Enum (store as String, not ordinal — ordinals break on reorder)
180
+ @TypeConverter
181
+ fun fromUserRole(role: UserRole): String = role.name
182
+
183
+ @TypeConverter
184
+ fun toUserRole(value: String): UserRole = UserRole.valueOf(value)
185
+
186
+ // Instant (store as epoch millis)
187
+ @TypeConverter
188
+ fun fromInstant(instant: Instant?): Long? = instant?.toEpochMilli()
189
+
190
+ @TypeConverter
191
+ fun toInstant(value: Long?): Instant? = value?.let { Instant.ofEpochMilli(it) }
192
+ }
193
+
194
+ // ✅ Register converters on the database
195
+ @Database(entities = [...], version = 1)
196
+ @TypeConverters(Converters::class)
197
+ abstract class AppDatabase : RoomDatabase()
198
+ ```
199
+
200
+ ---
201
+
202
+ ## Indices
203
+
204
+ ```kotlin
205
+ // ✅ Index frequently queried columns
206
+ @Entity(
207
+ tableName = "products",
208
+ indices = [
209
+ Index(value = ["category_id"]), // single column
210
+ Index(value = ["name", "category_id"]), // composite
211
+ Index(value = ["sku"], unique = true) // unique constraint
212
+ ]
213
+ )
214
+ data class ProductEntity(
215
+ @PrimaryKey val id: String,
216
+ @ColumnInfo(name = "name") val name: String,
217
+ @ColumnInfo(name = "sku") val sku: String,
218
+ @ColumnInfo(name = "category_id") val categoryId: String,
219
+ @ColumnInfo(name = "price") val price: Long
220
+ )
221
+ ```
222
+
223
+ ---
224
+
225
+ ## Anti-Patterns
226
+
227
+ - `@Entity` class used in Domain or Presentation layer — entities are Data layer only
228
+ - Storing enum as ordinal (`role.ordinal`) — breaks when enum values are reordered; use `name`
229
+ - Missing `Index` on foreign key columns — causes full table scan on joins
230
+ - `onDelete = ForeignKey.NO_ACTION` on cascading data — leaves orphan rows
231
+ - Using `@Ignore` to skip fields instead of proper mapping — creates confusion about what's stored
232
+ - Storing complex objects as JSON strings without a TypeConverter — makes querying impossible
233
+
234
+ ---
235
+
236
+ ## Related Skills
237
+
238
+ - `dao` — DAO patterns for querying entities
239
+ - `room` — Room database setup and configuration
240
+ - `migration` — handling entity schema changes
241
+ - `domain-modeling` — domain models that entities map to
242
+ - `dto-mapping` — mapping patterns between layers
243
+ - `indexing` — in-depth index strategy for performance
@@ -0,0 +1,216 @@
1
+ ---
2
+ name: feature-isolation
3
+ description: >
4
+ Feature isolation patterns for Android modular projects.
5
+ Load this skill when ensuring features don't leak implementation details,
6
+ designing feature APIs, preventing feature-to-feature coupling,
7
+ or structuring a feature module's internal vs public surface.
8
+ ---
9
+
10
+ # Feature Isolation
11
+
12
+ ## Overview
13
+
14
+ Feature isolation ensures that each feature module exposes only a minimal, stable public API while hiding all implementation details as `internal`. Features never depend on each other directly. Cross-feature communication happens via shared interfaces, callbacks, or a shared core module — never via direct imports.
15
+
16
+ ---
17
+
18
+ ## Core Principles
19
+
20
+ - **`internal` by default** — everything inside a feature is `internal` unless explicitly needed outside
21
+ - **No feature-to-feature imports** — features only import from `:core:*` modules
22
+ - **Public API is minimal** — expose only navigation routes, DI modules, and nav graph extensions
23
+ - **Feature entry points are composable extensions** — `NavGraphBuilder.featureGraph()`
24
+ - **Callbacks over navigation coupling** — features receive lambdas for cross-feature navigation
25
+
26
+ ---
27
+
28
+ ## Feature Public Surface
29
+
30
+ ```kotlin
31
+ // ✅ What a feature exposes (public)
32
+ // feature/orders/src/main/kotlin/.../
33
+
34
+ // 1. Navigation contract
35
+ object OrdersNavigation {
36
+ const val graphRoute = "orders_graph"
37
+ const val listRoute = "orders/list"
38
+ const val detailRoute = "orders/detail/{orderId}"
39
+
40
+ fun detailRoute(orderId: String) = "orders/detail/$orderId"
41
+ }
42
+
43
+ // 2. NavGraph extension — entry point for :app
44
+ fun NavGraphBuilder.ordersGraph(
45
+ navController: NavController,
46
+ onNavigateToProduct: (productId: String) -> Unit, // cross-feature via callback
47
+ onNavigateToProfile: () -> Unit
48
+ ) {
49
+ navigation(
50
+ startDestination = OrdersNavigation.listRoute,
51
+ route = OrdersNavigation.graphRoute
52
+ ) {
53
+ composable(OrdersNavigation.listRoute) {
54
+ OrderListScreen( // internal composable
55
+ onOrderClick = { orderId ->
56
+ navController.navigate(OrdersNavigation.detailRoute(orderId))
57
+ }
58
+ )
59
+ }
60
+ composable(
61
+ route = OrdersNavigation.detailRoute,
62
+ arguments = listOf(navArgument("orderId") { type = NavType.StringType })
63
+ ) { backStackEntry ->
64
+ val orderId = backStackEntry.arguments?.getString("orderId") ?: return@composable
65
+ OrderDetailScreen(
66
+ orderId = orderId,
67
+ onNavigateToProduct = onNavigateToProduct,
68
+ onNavigateToProfile = onNavigateToProfile
69
+ )
70
+ }
71
+ }
72
+ }
73
+
74
+ // 3. Hilt module (internal — bindings are auto-discovered by Hilt)
75
+ @Module
76
+ @InstallIn(SingletonComponent::class)
77
+ internal abstract class OrdersModule {
78
+ @Binds
79
+ abstract fun bindOrderRepository(impl: OrderRepositoryImpl): OrderRepository
80
+ }
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Internal Implementation
86
+
87
+ ```kotlin
88
+ // ✅ Everything implementation-related is internal
89
+ internal class OrderRepositoryImpl @Inject constructor(
90
+ private val dao: OrderDao,
91
+ private val api: OrderApiService
92
+ ) : OrderRepository { ... }
93
+
94
+ internal class GetOrdersUseCase @Inject constructor(
95
+ private val repository: OrderRepository
96
+ ) {
97
+ operator fun invoke(): Flow<List<Order>> = repository.observeOrders()
98
+ }
99
+
100
+ @HiltViewModel
101
+ internal class OrderListViewModel @Inject constructor(
102
+ private val getOrdersUseCase: GetOrdersUseCase
103
+ ) : ViewModel() { ... }
104
+
105
+ @Composable
106
+ internal fun OrderListScreen(onOrderClick: (String) -> Unit) { ... }
107
+
108
+ @Composable
109
+ internal fun OrderDetailScreen(
110
+ orderId: String,
111
+ onNavigateToProduct: (String) -> Unit,
112
+ onNavigateToProfile: () -> Unit
113
+ ) { ... }
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Cross-Feature Communication Patterns
119
+
120
+ ```kotlin
121
+ // ✅ Pattern 1: Callbacks (simplest — for navigation)
122
+ // :app wires the callback
123
+ ordersGraph(
124
+ navController = navController,
125
+ onNavigateToProduct = { productId ->
126
+ navController.navigate(CatalogNavigation.detailRoute(productId))
127
+ },
128
+ onNavigateToProfile = {
129
+ navController.navigate(ProfileNavigation.graphRoute)
130
+ }
131
+ )
132
+
133
+ // ✅ Pattern 2: Shared interface in :core:domain (for data)
134
+ // :core:domain
135
+ interface ProductInfoProvider {
136
+ suspend fun getProductSummary(productId: String): Result<ProductSummary>
137
+ }
138
+
139
+ // :feature:catalog implements it
140
+ internal class ProductInfoProviderImpl @Inject constructor(
141
+ private val repository: ProductRepository
142
+ ) : ProductInfoProvider { ... }
143
+
144
+ // :feature:orders uses it (no knowledge of :feature:catalog)
145
+ internal class OrderDetailViewModel @Inject constructor(
146
+ private val productInfoProvider: ProductInfoProvider // injected, not imported
147
+ ) : ViewModel() { ... }
148
+
149
+ // ✅ Pattern 3: Shared ViewModel in :app (for truly global state)
150
+ // :app — AuthViewModel scoped to root nav graph
151
+ @Composable
152
+ fun AppNavHost(navController: NavHostController) {
153
+ val appViewModel: AppViewModel = hiltViewModel() // scoped to root
154
+ val authState by appViewModel.authState.collectAsStateWithLifecycle()
155
+
156
+ ordersGraph(
157
+ navController = navController,
158
+ currentUserId = authState.userId // passed down as param
159
+ )
160
+ }
161
+ ```
162
+
163
+ ---
164
+
165
+ ## Feature API Contract Checklist
166
+
167
+ ```
168
+ ✅ Public (exposed to :app):
169
+ - Navigation object with route constants
170
+ - NavGraphBuilder extension function
171
+ - Hilt module (auto-discovered, but still internal annotation on bindings)
172
+
173
+ ❌ Must be internal:
174
+ - All ViewModel classes
175
+ - All composable functions
176
+ - All UseCase classes
177
+ - All Repository implementations
178
+ - All DAO and API service classes
179
+ - All domain model classes (unless in :core:domain)
180
+ - All Hilt @Binds/@Provides methods
181
+ ```
182
+
183
+ ---
184
+
185
+ ## Detecting Isolation Violations
186
+
187
+ ```kotlin
188
+ // ❌ Violation: feature:cart importing from feature:catalog
189
+ import com.example.feature.catalog.domain.model.Product // WRONG
190
+
191
+ // ✅ Fix: define what cart needs in :core:domain or via an interface
192
+ // :core:domain
193
+ data class ProductSummary(val id: String, val name: String, val price: Money)
194
+
195
+ // :feature:cart uses ProductSummary — :feature:catalog maps to it
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Anti-Patterns
201
+
202
+ - `public` on internal ViewModels or composables — leaks implementation, creates coupling
203
+ - Feature importing another feature's ViewModel — use shared ViewModel in :app instead
204
+ - Passing navigation controller deep into composables — use callbacks instead
205
+ - Shared mutable state between features via static/companion objects — use proper DI scope
206
+ - One feature calling another feature's UseCase directly — go through shared interface in :core
207
+
208
+ ---
209
+
210
+ ## Related Skills
211
+
212
+ - `modularization` — module structure that enables isolation
213
+ - `multi-module-architecture` — full multi-module setup
214
+ - `bounded-context` — conceptual boundaries between features
215
+ - `hilt` — scoping DI across modules correctly
216
+ - `navigation` — wiring navigation between isolated features