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

Android Clean Architecture

  • 1.4k installs
  • 238k repo stars
  • Updated August 5, 2026
  • affaan-m/ecc

This is a copy of android-clean-architecture by affaan-m - installs and ranking accrue to the original listing.

android-clean-architecture is an ECC skill that enforces Clean Architecture module boundaries, UseCase and Repository patterns, and data-layer design for developers structuring Android or Kotlin Multiplatform codebases.

About

android-clean-architecture from affaan-m/ecc covers Clean Architecture patterns for Android and Kotlin Multiplatform projects. It defines recommended module layouts—app entry, core utilities, domain UseCases, data repositories—and dependency inversion rules across presentation, domain, and data layers. The skill applies when structuring modules, implementing UseCase, Repository, and DataSource patterns, designing cross-layer data flow, or setting up Koin or Hilt dependency injection with Room, SQLDelight, or Ktor in layered architectures. Developers reach for it when agents need explicit guidance on Android/KMP boundaries instead of ad-hoc package organization.

  • Recommended 7-module project layout including domain, data, presentation and feature modules
  • Strict dependency inversion rules between layers (app → presentation → domain → data)
  • Implementation patterns for UseCases, Repositories, DataSources and domain models
  • Guidance for Room, SQLDelight, Ktor, Koin and Hilt integration in layered architecture
  • Patterns that work for both Android and Kotlin Multiplatform projects

Android Clean Architecture by the numbers

  • 1,425 all-time installs (skills.sh)
  • +89 installs in the week ending Aug 4, 2026 (Skillselion tracking)
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/affaan-m/ecc --skill android-clean-architecture

Add your badge

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

Listed on Skillselion
Installs1.4k
repo stars238k
Last updatedAugust 5, 2026
Repositoryaffaan-m/ecc

How do you structure Android Clean Architecture modules?

Enforce Clean Architecture boundaries when structuring Android or Kotlin Multiplatform codebases with layered modules and dependency rules.

Who is it for?

Android and Kotlin Multiplatform developers implementing Clean Architecture with Room, SQLDelight, Ktor, Koin, or Hilt.

Skip if: iOS-only Swift projects, flat single-module prototypes, or teams rejecting layered architecture patterns.

When should I use this skill?

The user structures Android or KMP modules, implements UseCases or Repositories, or wires DI with Room, SQLDelight, or Ktor.

What you get

Layered module tree with domain UseCases, Repository implementations, DI wiring, and presentation-to-data flow boundaries.

  • module structure layout
  • UseCase and Repository implementations
  • DI wiring configuration

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

How it compares

Use android-clean-architecture for layered Android/KMP modules; use simpler skills for single-module prototypes.

FAQ

What modules does android-clean-architecture recommend?

android-clean-architecture recommends a layered layout with app (entry and DI wiring), core (shared utilities and error types), domain (UseCases and models), and data (Repository and DataSource implementations) modules for Android and KMP projects.

Which data libraries does android-clean-architecture cover?

android-clean-architecture covers Room, SQLDelight, and Ktor within layered data modules, plus Koin or Hilt for dependency injection across presentation, domain, and data boundaries.

Backend & APIsbackendintegrations

This week in AI coding

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

unsubscribe anytime.