Communitygithub.com

Android 整洁架构

为 Android 和 Kotlin 多平台项目提供整洁架构模式——涵盖模块结构、依赖规则、UseCase、Repository 以及数据层模式。在规划模块、层或数据流时使用。

Android 整洁架构 是什么?

该技能为 Android 和 Kotlin 多平台 (KMP) 项目提供了全面的整洁架构指南。内容包括清晰的模块划分、严格的依赖规则(核心是 domain 层不依赖任何框架)、UseCase 与 Repository 的设计模式,以及数据层实现(支持 Room、SQLDelight 和 Ktor)。平台方面,同时覆盖了 Android 和 KMP 环境,并提供了 Koin 和 Hilt 两种依赖注入方案的示例。通过遵循本技能中描述的模块结构、代码示例和反模式列表,开发者能够构建出可维护、可测试且与框架解耦的应用程序。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/android-clean-architecture

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Android 整洁架构 是做什么的?

面向 Android 和 KMP 项目的整洁架构模式。涵盖模块边界、依赖反转、UseCase/Repository 模式,以及使用 Room、SQLDelight 和 Ktor 的数据层设计。

何时激活

  • 构建 Android 或 KMP 项目模块
  • 实现 UseCase、Repository 或 DataSource
  • 在各层(domain、data、presentation)之间设计数据流
  • 使用 Koin 或 Hilt 设置依赖注入
  • 在分层架构中使用 Room、SQLDelight 或 Ktor

模块结构

推荐布局

project/
├── app/                  # Android 入口点,DI 装配,Application 类
├── core/                 # 共享工具类、基类、错误类型
├── domain/               # UseCase、领域模型、仓库接口(纯 Kotlin)
├── data/                 # Repository 实现、DataSource、数据库、网络
├── 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 的 UseCase,用于反应式数据流
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 }

仓库接口

定义在 domain 层,实现在 data 层:

interface ItemRepository {
    suspend fun getItemsByCategory(category: String): Result<List<Item>>
    suspend fun saveItem(item: Item): Result<Unit>
    fun observeItems(): Flow<List<Item>>
}

数据层

Repository 实现

协调本地和远程数据源:

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() }
        }
    }
}

Mapper 模式

将映射器作为扩展函数放置在数据模型附近:

// 在 data 层
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,支持内容协商
val httpClient = HttpClient {
    install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) }
    install(Logging) { level = LogLevel.HEADERS }
    defaultRequest { url("https://api.example.com/") }
}

依赖注入

Koin(兼容 KMP)

// Domain 模块
val domainModule = module {
    factory { GetItemsByCategoryUseCase(get()) }
    factory { ObserveUserProgressUseCase(get()) }
}

// Data 模块
val dataModule = module {
    single<ItemRepository> { ItemRepositoryImpl(get(), get()) }
    single { ItemLocalDataSource(get()) }
    single { ItemRemoteDataSource(get()) }
}

// Presentation 模块
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
}

// 在 ViewModel 中——映射到 UI 状态
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 { /* 共享依赖 */ }
        commonTest.dependencies { implementation(kotlin("test")) }
    }
}

在模块中应用:

// domain/build.gradle.kts
plugins { id("kmp-library") }

应避免的反模式

  • 在 domain 层导入 Android 框架类——保持纯 Kotlin
  • 将数据库实体或 DTO 暴露给 UI 层——始终映射到领域模型
  • 将业务逻辑放在 ViewModel 中——将其提取到 UseCase
  • 使用 GlobalScope 或非结构化协程——使用 viewModelScope 或结构化并发
  • 过于臃肿的 Repository 实现——拆分为聚焦的 DataSource
  • 模块间的循环依赖——如果 A 依赖 B,则 B 不能依赖 A

参考

查看技能:compose-multiplatform-patterns 了解 UI 模式。 查看技能:kotlin-coroutines-flows 了解异步模式。

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

相关技能