affaan-m/ECC/docs/zh-CN/skills/android-clean-architecture/SKILL.md
android-clean-architecture
Review android-clean-architecture's use cases, installation, workflow, and original source instructions.
- Source repository stars
- 234,327
- Declared platforms
- 0
- Static risk flags
- 1
- Last source update
- 2026-07-27
- Source checked
- 2026-07-28
Decision brief
What it does—and where it fits
适用于 Android 和 KMP 项目的整洁架构模式。涵盖模块边界、依赖反转、UseCase/Repository 模式,以及使用 Room、SQLDelight 和 Ktor 的数据层设计。
Not for
- Tasks that require unconfirmed production actions or broad system permissions.
- Environments where the pinned source and install steps cannot be inspected.
Compatibility matrix
Platform support, with evidence labels
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
Inspect first. Install second.
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/affaan-m/ECC --skill "docs/zh-CN/skills/android-clean-architecture"Inspect the Agent Skill "android-clean-architecture" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/docs/zh-CN/skills/android-clean-architecture/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
What the source asks the agent to do
- 01
何时启用
构建 Android 或 KMP 项目模块结构 实现 UseCases、Repositories 或 DataSources 设计各层(领域层、数据层、表示层)之间的数据流 使用 Koin 或 Hilt 设置依赖注入 在分层架构中使用 Room、SQLDelight 或 Ktor
构建 Android 或 KMP 项目模块结构实现 UseCases、Repositories 或 DataSources设计各层(领域层、数据层、表示层)之间的数据流 - 02
模块结构
关键:domain 绝不能依赖 data、presentation 或任何框架。它仅包含纯 Kotlin 代码。
关键:domain 绝不能依赖 data、presentation 或任何框架。它仅包含纯 Kotlin 代码。 - 03
推荐布局
Review the “推荐布局” section in the pinned source before continuing.
Review and apply the “推荐布局” source section. - 04
依赖规则
关键:domain 绝不能依赖 data、presentation 或任何框架。它仅包含纯 Kotlin 代码。
关键:domain 绝不能依赖 data、presentation 或任何框架。它仅包含纯 Kotlin 代码。 - 05
领域层
每个 UseCase 代表一个业务操作。使用 operator fun invoke 以获得简洁的调用点:
每个 UseCase 代表一个业务操作。使用 operator fun invoke 以获得简洁的调用点:领域模型是普通的 Kotlin 数据类——没有框架注解:
Permission review
Static risk signals and limitations
Network access
The documentation includes network, browsing, or remote request actions.
defaultRequest { url("https://api.example.com/") }Evidence record
Why each signal appears
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 73/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 234,327 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Provenance and original SKILL.md
- Repository
- affaan-m/ECC
- Skill path
- docs/zh-CN/skills/android-clean-architecture/SKILL.md
- Commit
- 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
- License
- MIT
- Collected
- 2026-07-28
- Default branch
- main
View the original SKILL.md
Android 整洁架构
适用于 Android 和 KMP 项目的整洁架构模式。涵盖模块边界、依赖反转、UseCase/Repository 模式,以及使用 Room、SQLDelight 和 Ktor 的数据层设计。
何时启用
- 构建 Android 或 KMP 项目模块结构
- 实现 UseCases、Repositories 或 DataSources
- 设计各层(领域层、数据层、表示层)之间的数据流
- 使用 Koin 或 Hilt 设置依赖注入
- 在分层架构中使用 Room、SQLDelight 或 Ktor
模块结构
推荐布局
project/
├── app/ # Android 入口点,DI 装配,Application 类
├── core/ # 共享工具类,基类,错误类型
├── domain/ # 用例,领域模型,仓库接口(纯 Kotlin)
├── data/ # 仓库实现,数据源,数据库,网络
├── presentation/ # 界面,ViewModel,UI 模型,导航
├── design-system/ # 可复用的 Compose 组件,主题,排版
└── feature/ # 功能模块(可选,用于大型项目)
├── auth/
├── settings/
└── profile/
依赖规则
app → presentation, domain, data, core
presentation → domain, design-system, core
data → domain, core
domain → core (或无依赖)
core → (无依赖)
关键:domain 绝不能依赖 data、presentation 或任何框架。它仅包含纯 Kotlin 代码。
领域层
UseCase 模式
每个 UseCase 代表一个业务操作。使用 operator fun invoke 以获得简洁的调用点:
class GetItemsByCategoryUseCase(
private val repository: ItemRepository
) {
suspend operator fun invoke(category: String): Result<List<Item>> {
return repository.getItemsByCategory(category)
}
}
// Flow-based UseCase for reactive streams
class ObserveUserProgressUseCase(
private val repository: UserRepository
) {
operator fun invoke(userId: String): Flow<UserProgress> {
return repository.observeProgress(userId)
}
}
领域模型
领域模型是普通的 Kotlin 数据类——没有框架注解:
data class Item(
val id: String,
val title: String,
val description: String,
val tags: List<String>,
val status: Status,
val category: String
)
enum class Status { DRAFT, ACTIVE, ARCHIVED }
仓库接口
在领域层定义,在数据层实现:
interface ItemRepository {
suspend fun getItemsByCategory(category: String): Result<List<Item>>
suspend fun saveItem(item: Item): Result<Unit>
fun observeItems(): Flow<List<Item>>
}
数据层
仓库实现
协调本地和远程数据源:
class ItemRepositoryImpl(
private val localDataSource: ItemLocalDataSource,
private val remoteDataSource: ItemRemoteDataSource
) : ItemRepository {
override suspend fun getItemsByCategory(category: String): Result<List<Item>> {
return runCatching {
val remote = remoteDataSource.fetchItems(category)
localDataSource.insertItems(remote.map { it.toEntity() })
localDataSource.getItemsByCategory(category).map { it.toDomain() }
}
}
override suspend fun saveItem(item: Item): Result<Unit> {
return runCatching {
localDataSource.insertItems(listOf(item.toEntity()))
}
}
override fun observeItems(): Flow<List<Item>> {
return localDataSource.observeAll().map { entities ->
entities.map { it.toDomain() }
}
}
}
映射器模式
将映射器作为扩展函数放在数据模型附近:
// In data layer
fun ItemEntity.toDomain() = Item(
id = id,
title = title,
description = description,
tags = tags.split("|"),
status = Status.valueOf(status),
category = category
)
fun ItemDto.toEntity() = ItemEntity(
id = id,
title = title,
description = description,
tags = tags.joinToString("|"),
status = status,
category = category
)
Room 数据库 (Android)
@Entity(tableName = "items")
data class ItemEntity(
@PrimaryKey val id: String,
val title: String,
val description: String,
val tags: String,
val status: String,
val category: String
)
@Dao
interface ItemDao {
@Query("SELECT * FROM items WHERE category = :category")
suspend fun getByCategory(category: String): List<ItemEntity>
@Upsert
suspend fun upsert(items: List<ItemEntity>)
@Query("SELECT * FROM items")
fun observeAll(): Flow<List<ItemEntity>>
}
SQLDelight (KMP)
-- Item.sq
CREATE TABLE ItemEntity (
id TEXT NOT NULL PRIMARY KEY,
title TEXT NOT NULL,
description TEXT NOT NULL,
tags TEXT NOT NULL,
status TEXT NOT NULL,
category TEXT NOT NULL
);
getByCategory:
SELECT * FROM ItemEntity WHERE category = ?;
upsert:
INSERT OR REPLACE INTO ItemEntity (id, title, description, tags, status, category)
VALUES (?, ?, ?, ?, ?, ?);
observeAll:
SELECT * FROM ItemEntity;
Ktor 网络客户端 (KMP)
class ItemRemoteDataSource(private val client: HttpClient) {
suspend fun fetchItems(category: String): List<ItemDto> {
return client.get("api/items") {
parameter("category", category)
}.body()
}
}
// HttpClient setup with content negotiation
val httpClient = HttpClient {
install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) }
install(Logging) { level = LogLevel.HEADERS }
defaultRequest { url("https://api.example.com/") }
}
依赖注入
Koin (适用于 KMP)
// Domain module
val domainModule = module {
factory { GetItemsByCategoryUseCase(get()) }
factory { ObserveUserProgressUseCase(get()) }
}
// Data module
val dataModule = module {
single<ItemRepository> { ItemRepositoryImpl(get(), get()) }
single { ItemLocalDataSource(get()) }
single { ItemRemoteDataSource(get()) }
}
// Presentation module
val presentationModule = module {
viewModelOf(::ItemListViewModel)
viewModelOf(::DashboardViewModel)
}
Hilt (仅限 Android)
@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
@Binds
abstract fun bindItemRepository(impl: ItemRepositoryImpl): ItemRepository
}
@HiltViewModel
class ItemListViewModel @Inject constructor(
private val getItems: GetItemsByCategoryUseCase
) : ViewModel()
错误处理
Result/Try 模式
使用 Result<T> 或自定义密封类型进行错误传播:
sealed interface Try<out T> {
data class Success<T>(val value: T) : Try<T>
data class Failure(val error: AppError) : Try<Nothing>
}
sealed interface AppError {
data class Network(val message: String) : AppError
data class Database(val message: String) : AppError
data object Unauthorized : AppError
}
// In ViewModel — map to UI state
viewModelScope.launch {
when (val result = getItems(category)) {
is Try.Success -> _state.update { it.copy(items = result.value, isLoading = false) }
is Try.Failure -> _state.update { it.copy(error = result.error.toMessage(), isLoading = false) }
}
}
约定插件 (Gradle)
对于 KMP 项目,使用约定插件以减少构建文件重复:
// build-logic/src/main/kotlin/kmp-library.gradle.kts
plugins {
id("org.jetbrains.kotlin.multiplatform")
}
kotlin {
androidTarget()
iosX64(); iosArm64(); iosSimulatorArm64()
sourceSets {
commonMain.dependencies { /* shared deps */ }
commonTest.dependencies { implementation(kotlin("test")) }
}
}
在模块中应用:
// domain/build.gradle.kts
plugins { id("kmp-library") }
应避免的反模式
- 在
domain中导入 Android 框架类——保持其为纯 Kotlin - 向 UI 层暴露数据库实体或 DTO——始终映射到领域模型
- 将业务逻辑放在 ViewModels 中——提取到 UseCases
- 使用
GlobalScope或非结构化协程——使用viewModelScope或结构化并发 - 臃肿的仓库实现——拆分为专注的 DataSources
- 循环模块依赖——如果 A 依赖 B,则 B 绝不能依赖 A
参考
查看技能:compose-multiplatform-patterns 了解 UI 模式。
查看技能:kotlin-coroutines-flows 了解异步模式。
Alternatives
Compare before choosing
affaan-m/ECC
android-clean-architecture
Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns.
affaan-m/ECC
android-clean-architecture
Review android-clean-architecture's use cases, installation, workflow, and original source instructions.