Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
affaan-m avatar

Android Clean Architecture

  • 6.8k installs
  • 238k repo stars
  • Updated August 5, 2026
  • affaan-m/everything-claude-code

A pattern reference for structuring Android and KMP projects into domain, data, presentation, and core modules with clean dependency rules, UseCase/Repository abstractions, and DI wiring.

About

This skill defines clean architecture patterns for Android and Kotlin Multiplatform (KMP) projects, covering module boundaries, dependency inversion, and data flow across domain, data, and presentation layers. Developers use it when scaffolding new Android or KMP projects, implementing UseCase and Repository abstractions, or wiring up Room/SQLDelight databases with Ktor network clients. Key workflows include defining pure-Kotlin domain models with no framework annotations, implementing repository classes that coordinate local and remote data sources via mapper extension functions, and configuring dependency injection with either Koin (KMP-compatible) or Hilt (Android-only). The skill also covers Gradle convention plugins to reduce build file duplication across modules, sealed error types for propagating failures through Result or custom Try types, and explicit anti-patterns such as placing business logic in ViewModels or exposing database entities to the UI layer.

  • Module dependency graph enforces domain layer isolation: domain depends only on core and never on data, presentation, or
  • UseCase classes use operator fun invoke for clean call-site syntax, with Flow-based variants for reactive streams
  • Repository implementations coordinate local Room/SQLDelight and remote Ktor data sources using mapper extension function
  • DI setup supports both Koin (KMP-friendly, factory/single/viewModelOf) and Hilt (Android-only, @Binds/@HiltViewModel) wi
  • Sealed AppError hierarchy (Network, Database, Unauthorized) maps to UI state inside viewModelScope using structured coro

Android Clean Architecture by the numbers

  • 6,849 all-time installs (skills.sh)
  • +288 installs in the week ending Aug 5, 2026 (Skillselion tracking)
  • Ranked #24 of 1,039 Mobile Development skills by installs in the Skillselion catalog
  • Security screen: MEDIUM risk (skills.sh audit)
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
At a glance

android-clean-architecture capabilities & compatibility

Capabilities
module structure generation · usecase scaffolding · repository pattern implementation · di wiring · error handling patterns · database schema generation · mapper function generation
Use cases
api development · database · frontend
Platforms
macOS · Windows · Linux
Runs
Runs locally
Pricing
Free
From the docs

What android-clean-architecture says it does

マッパーはデータモデルの近くに拡張関数として保持する
SKILL.md
KMP プロジェクトでは、ビルドファイルの重複を削減するためにコンベンションプラグインを使用する
SKILL.md
npx skills add https://github.com/affaan-m/everything-claude-code --skill android-clean-architecture

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs6.8k
repo stars238k
Security audit2 / 3 scanners passed
Last updatedAugust 5, 2026
Repositoryaffaan-m/everything-claude-code

What it does

Structure Android and Kotlin Multiplatform projects using clean architecture with UseCase, Repository, and data layer patterns backed by Room, SQLDelight, and Ktor.

Who is it for?

Android or KMP developers structuring multi-module projects with Room or SQLDelight local storage and Ktor remote networking.

Skip if: Simple single-screen Android apps or projects not using Kotlin Multiplatform or coroutines-based async patterns.

When should I use this skill?

Starting a new Android or KMP module structure, implementing UseCase/Repository classes, setting up Koin or Hilt DI, or integrating Room, SQLDelight, or Ktor into a layered architecture.

What you get

Developers get a well-layered project where the domain module is pure Kotlin, data sources are isolated, errors propagate through typed sealed classes, and DI modules map cleanly to architectural layers.

  • Module directory layout with app, core, domain, data, presentation, design-system, and optional feature modules
  • UseCase and Repository interface definitions with Flow and suspend variants
  • Room DAO and entity classes or SQLDelight .sq schema files

By the numbers

  • 7 top-level module directories defined in recommended layout (app, core, domain, data, presentation, design-system, feat
  • 6 anti-patterns explicitly documented (framework imports in domain, entity exposure, logic in ViewModel, GlobalScope, bl
  • 2 DI frameworks supported with distinct code examples (Koin for KMP, Hilt for Android-only)

Files

SKILL.mdMarkdownGitHub ↗

Android クリーンアーキテクチャ

Android と KMP プロジェクトのクリーンアーキテクチャパターン。モジュール境界、依存関係の逆転、UseCase/Repository パターン、Room・SQLDelight・Ktor を使用したデータ層設計をカバーします。

起動タイミング

  • Android または KMP プロジェクトモジュールの構造化
  • UseCase、Repository、DataSource の実装
  • 層間のデータフロー設計(ドメイン、データ、プレゼンテーション)
  • Koin または Hilt による依存性注入のセットアップ
  • 層状アーキテクチャでの Room、SQLDelight、Ktor の使用

モジュール構造

推奨レイアウト

project/
├── app/                  # Android エントリポイント、DI ワイヤリング、Application クラス
├── core/                 # 共有ユーティリティ、基底クラス、エラー型
├── domain/               # UseCase、ドメインモデル、リポジトリインターフェース(純粋 Kotlin)
├── data/                 # リポジトリ実装、DataSource、DB、ネットワーク
├── 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 → (なし)

重要: domaindatapresentation、またはどのフレームワークにも依存してはいけません。純粋な Kotlin のみを含みます。

ドメイン層

UseCase パターン

各 UseCase は 1 つのビジネス操作を表します。クリーンな呼び出しサイトのために operator fun invoke を使用します:

class GetItemsByCategoryUseCase(
    private val repository: ItemRepository
) {
    suspend operator fun invoke(category: String): Result<List<Item>> {
        return repository.getItemsByCategory(category)
    }
}

// リアクティブストリーム向けフローベースの 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 }

リポジトリインターフェース

ドメインで定義し、データで実装する:

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

マッパーパターン

マッパーはデータモデルの近くに拡張関数として保持する:

// データ層
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 フレンドリー)

// ドメインモジュール
val domainModule = module {
    factory { GetItemsByCategoryUseCase(get()) }
    factory { ObserveUserProgressUseCase(get()) }
}

// データモジュール
val dataModule = module {
    single<ItemRepository> { ItemRepositoryImpl(get(), get()) }
    single { ItemLocalDataSource(get()) }
    single { ItemRemoteDataSource(get()) }
}

// プレゼンテーションモジュール
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 または構造化された並行処理を使用する
  • 肥大化したリポジトリ実装 — 焦点を絞った DataSource に分割する
  • 循環モジュール依存 — A が B に依存する場合、B は A に依存してはいけない

参考資料

スキル参照: UI パターンは compose-multiplatform-patterns を参照。 非同期パターンは kotlin-coroutines-flows を参照。

Related skills

Forks & variants (1)

Android Clean Architecture has 1 known copy in the catalog totaling 1.4k installs. They canonicalize to this original listing.

How it compares

Pick android-clean-architecture over generic mobile skills when structuring layered Android or KMP projects with explicit Clean Architecture module rules.

FAQ

Can the domain module depend on Android framework classes?

No. The domain module must contain only pure Kotlin - no Android framework imports, no Room or Ktor annotations. Framework code belongs in the data or presentation layers.

When should I use Koin versus Hilt for dependency injection?

Use Koin for KMP projects because it is Kotlin Multiplatform compatible. Use Hilt for Android-only projects where annotation processing is acceptable and Dagger integration is preferred.

Where should business logic live - ViewModel or UseCase?

Business logic belongs in UseCase classes, not ViewModels. ViewModels should call UseCases and map results to UI state. Placing logic in ViewModels is listed as an explicit anti-pattern.

Is Android Clean Architecture safe to install?

skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.

Mobile Developmentbackendintegrationsdocs

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.