android-clean-architecture
适用于Android和Kotlin多平台项目的Clean Architecture模式——模块结构、依赖规则、用例、仓库以及数据层模式。
About this skill
This skill provides a comprehensive guide for AI agents to implement robust Clean Architecture patterns in Android and Kotlin Multiplatform (KMP) development. It details best practices for modular structure (e.g., `app`, `core`, `domain`, `data`, `presentation`, `design-system`, `feature`), dependency inversion principles, and the effective use of UseCase and Repository patterns. The skill further elaborates on data layer design, including integration with technologies like Room, SQLDelight, and Ktor, and offers guidance on setting up Dependency Injection with Koin or Hilt. It's designed to enhance an AI agent's ability to generate, analyze, and refactor code according to well-established software development engineering patterns for production-ready systems.
Best use case
Architecting new Android/KMP applications with a clean, scalable structure; Refactoring existing projects to adhere to Clean Architecture; Generating code for specific layers like UseCases, Repositories, or DataSources; Designing data flow between domain, data, and presentation layers; Implementing dependency injection with frameworks like Koin or Hilt.
适用于Android和Kotlin多平台项目的Clean Architecture模式——模块结构、依赖规则、用例、仓库以及数据层模式。
Well-structured Android/KMP code adhering to Clean Architecture principles; Clear separation of concerns across modules (domain, data, presentation); Correct implementation of dependency inversion and data flow; Boilerplate code generation for UseCases, Repositories, and data sources; Recommendations for scalable and maintainable project structures.
Practical example
Example input
Design a new Android application module structure following Clean Architecture. The app needs a user authentication feature and a product listing feature. Suggest how to structure the `domain`, `data`, and `presentation` layers for these features, including a placeholder UseCase for 'LoginUser' and a Repository interface for 'UserRepository'. Also, provide an example of setting up dependency injection for these with Koin.
Example output
```text
Okay, let's outline a Clean Architecture module structure for an Android application with user authentication and product listing features, including Koin DI setup.
Project Structure:
project/
├── app/ # Android entry point, DI assembly, Application class
├── core/ # Shared utilities, base classes, error types
├── domain/ # Pure Kotlin, defines business logic contracts
│ ├── auth/
│ │ ├── model/
│ │ │ └── User.kt
│ │ ├── repository/
│ │ │ └── UserRepository.kt # Interface for auth data operations
│ │ └── usecase/
│ │ └── LoginUserUseCase.kt
│ ├── product/
│ │ ├── model/
│ │ │ └── Product.kt
│ │ ├── repository/
│ │ │ └── ProductRepository.kt # Interface for product data operations
│ │ └── usecase/
│ │ └── GetProductsUseCase.kt
├── data/ # Data layer implementations
│ ├── auth/
│ │ └── repository/
│ │ └── UserRepositoryImpl.kt # Implements domain.auth.repository.UserRepository
│ │ └── source/
│ │ ├── AuthRemoteDataSource.kt # Ktor, API calls
│ │ └── AuthLocalDataSource.kt # Room, local storage
│ ├── product/
│ │ └── repository/
│ │ └── ProductRepositoryImpl.kt # Implements domain.product.repository.ProductRepository
│ │ └── source/
│ │ ├── ProductRemoteDataSource.kt
│ │ └── ProductLocalDataSource.kt
├── presentation/ # UI layer, Android specific
│ ├── auth/
│ │ ├── AuthViewModel.kt
│ │ └── AuthScreen.kt # Compose UI
│ ├── product/
│ │ ├── ProductListViewModel.kt
│ │ └── ProductListScreen.kt # Compose UI
├── design-system/ # Reusable Compose components, themes, typography
└── feature/ # Optional, for larger projects to group related modules
Example UseCase (`domain/auth/usecase/LoginUserUseCase.kt`):
```kotlin
package com.example.domain.auth.usecase
import com.example.domain.auth.model.User
import com.example.domain.auth.repository.UserRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
class LoginUserUseCase(private val userRepository: UserRepository) {
suspend operator fun invoke(username: String, password: String): Flow<Result<User>> = flow {
emit(Result.Loading)
try {
val user = userRepository.login(username, password)
emit(Result.Success(user))
} catch (e: Exception) {
emit(Result.Error(e))
}
}
}
sealed class Result<out T> {
object Loading : Result<Nothing>()
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Throwable) : Result<Nothing>()
}
```
Example Repository Interface (`domain/auth/repository/UserRepository.kt`):
```kotlin
package com.example.domain.auth.repository
import com.example.domain.auth.model.User
interface UserRepository {
suspend fun login(username: String, password: String): User
suspend fun register(user: User): User
// ... other user-related operations
}
```
Example Koin Modules (`app/src/main/java/com/example/app/di/AppModule.kt`):
```kotlin
package com.example.app.di
import com.example.data.auth.repository.UserRepositoryImpl
import com.example.data.auth.source.AuthLocalDataSource
import com.example.data.auth.source.AuthRemoteDataSource
import com.example.data.product.repository.ProductRepositoryImpl
import com.example.data.product.source.ProductLocalDataSource
import com.example.data.product.source.ProductRemoteDataSource
import com.example.domain.auth.repository.UserRepository
import com.example.domain.auth.usecase.LoginUserUseCase
import com.example.domain.product.repository.ProductRepository
import com.example.domain.product.usecase.GetProductsUseCase
import com.example.presentation.auth.AuthViewModel
import com.example.presentation.product.ProductListViewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
val appModule = module {
// Data Sources
single { AuthRemoteDataSource() }
single { AuthLocalDataSource() } // Assuming Room/SQLDelight setup elsewhere
single { ProductRemoteDataSource() }
single { ProductLocalDataSource() }
// Repositories
single<UserRepository> { UserRepositoryImpl(get(), get()) }
single<ProductRepository> { ProductRepositoryImpl(get(), get()) }
// Use Cases
factory { LoginUserUseCase(get()) }
factory { GetProductsUseCase(get()) }
// ViewModels
viewModel { AuthViewModel(get()) }
viewModel { ProductListViewModel(get()) }
}
```
This structure ensures a clear separation of concerns, allowing for independent development and testing of each layer and feature, with Koin handling the dependencies.
```When to use this skill
- When building the module structure for a new Android or Kotlin Multiplatform (KMP) project.
- When implementing UseCases, Repositories, or DataSources within an application.
- When designing the data flow between different architectural layers (Domain, Data, Presentation).
- When setting up Dependency Injection using frameworks like Koin or Hilt.
When not to use this skill
- For simple, single-module projects where the overhead of Clean Architecture might be excessive.
- When working on projects that explicitly follow a different architectural pattern (e.g., MVVM without a strict domain layer, MVI).
- For tasks unrelated to Android/KMP development or architectural guidance, such as general problem-solving or natural language processing.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/android-clean-architecture/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How android-clean-architecture Compares
| Feature / Agent | android-clean-architecture | Standard Approach |
|---|---|---|
| Platform Support | Claude | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | easy | N/A |
Frequently Asked Questions
What does this skill do?
适用于Android和Kotlin多平台项目的Clean Architecture模式——模块结构、依赖规则、用例、仓库以及数据层模式。
Which AI agents support this skill?
This skill is designed for Claude.
How difficult is it to install?
The installation complexity is rated as easy. You can find the installation instructions above.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
Related Guides
AI Agents for Coding
Browse AI agent skills for coding, debugging, testing, refactoring, code review, and developer workflows across Claude, Cursor, and Codex.
Best AI Skills for Claude
Explore the best AI skills for Claude and Claude Code across coding, research, workflow automation, documentation, and agent operations.
ChatGPT vs Claude for Agent Skills
Compare ChatGPT and Claude for AI agent skills across coding, writing, research, and reusable workflow execution.
SKILL.md Source
# 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` 以获得简洁的调用点:
```kotlin
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 数据类——没有框架注解:
```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 }
```
### 仓库接口
在领域层定义,在数据层实现:
```kotlin
interface ItemRepository {
suspend fun getItemsByCategory(category: String): Result<List<Item>>
suspend fun saveItem(item: Item): Result<Unit>
fun observeItems(): Flow<List<Item>>
}
```
## 数据层
### 仓库实现
协调本地和远程数据源:
```kotlin
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() }
}
}
}
```
### 映射器模式
将映射器作为扩展函数放在数据模型附近:
```kotlin
// 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)
```kotlin
@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)
```sql
-- 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)
```kotlin
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)
```kotlin
// 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)
```kotlin
@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>` 或自定义密封类型进行错误传播:
```kotlin
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 项目,使用约定插件以减少构建文件重复:
```kotlin
// 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")) }
}
}
```
在模块中应用:
```kotlin
// 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` 了解异步模式。Related Skills
architecture-decision-records
在Claude Code会话期间,将做出的架构决策捕获为结构化的架构决策记录(ADR)。自动检测决策时刻,记录上下文、考虑的替代方案和理由。维护一个ADR日志,以便未来的开发人员理解代码库为何以当前方式构建。
workspace-surface-audit
Audit the active repo, MCP servers, plugins, connectors, env surfaces, and harness setup, then recommend the highest-value ECC-native skills, hooks, agents, and operator workflows. Use when the user wants help setting up Claude Code or understanding what capabilities are actually available in their environment.
safety-guard
Use this skill to prevent destructive operations when working on production systems or running agents autonomously.
repo-scan
Cross-stack source code asset audit — classifies every file, detects embedded third-party libraries, and delivers actionable four-level verdicts per module with interactive HTML reports.
project-flow-ops
Operate execution flow across GitHub and Linear by triaging issues and pull requests, linking active work, and keeping GitHub public-facing while Linear remains the internal execution layer. Use when the user wants backlog control, PR triage, or GitHub-to-Linear coordination.
manim-video
Build reusable Manim explainers for technical concepts, graphs, system diagrams, and product walkthroughs, then hand off to the wider ECC video stack if needed. Use when the user wants a clean animated explainer rather than a generic talking-head script.
laravel-plugin-discovery
Discover and evaluate Laravel packages via LaraPlugins.io MCP. Use when the user wants to find plugins, check package health, or assess Laravel/PHP compatibility.
design-system
Use this skill to generate or audit design systems, check visual consistency, and review PRs that touch styling.
click-path-audit
Trace every user-facing button/touchpoint through its full state change sequence to find bugs where functions individually work but cancel each other out, produce wrong final state, or leave the UI in an inconsistent state. Use when: systematic debugging found no bugs but users report broken buttons, or after any major refactor touching shared state stores.
ck
Persistent per-project memory for Claude Code. Auto-loads project context on session start, tracks sessions with git activity, and writes to native memory. Commands run deterministic Node.js scripts — behavior is consistent across model versions.
canary-watch
Use this skill to monitor a deployed URL for regressions after deploys, merges, or dependency upgrades.
benchmark
Use this skill to measure performance baselines, detect regressions before/after PRs, and compare stack alternatives.