Source profileQuality 74/100

affaan-m/ECC/docs/ja-JP/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 を使用したデータ層設計をカバーします。

Best for

    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

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    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.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "docs/ja-JP/skills/android-clean-architecture"
    Safe inspection promptEditorial

    Inspect the Agent Skill "android-clean-architecture" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/docs/ja-JP/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

    1. 01

      起動タイミング

      Android または KMP プロジェクトモジュールの構造化

      Android または KMP プロジェクトモジュールの構造化UseCase、Repository、DataSource の実装層間のデータフロー設計(ドメイン、データ、プレゼンテーション)
    2. 02

      モジュール構造

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

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

      推奨レイアウト

      Review the “推奨レイアウト” section in the pinned source before continuing.

      Review and apply the “推奨レイアウト” source section.
    4. 04

      依存関係ルール

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

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

      ドメイン層

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

      各 UseCase は 1 つのビジネス操作を表します。クリーンな呼び出しサイトのために operator fun invoke を使用します:ドメインモデルはプレーンな Kotlin データクラス — フレームワークのアノテーションなし:

    Permission review

    Static risk signals and limitations

    Network access

    medium · line 222

    The documentation includes network, browsing, or remote request actions.

    defaultRequest { url("https://api.example.com/") }

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score74/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    docs/ja-JP/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 プロジェクトモジュールの構造化
    • 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 を参照。

    Alternatives

    Compare before choosing