mcpField
A
android-data-layer

android-data-layer

github.com/rcosteira79開発169152 回インストール6 週間前に更新
リポジトリ

android-data-layer について

Android 開発者向けのデータレイヤースキルで、リポジトリ、エラー伝播、Room 永続化の構成を扱います。プラットフォーム例外を変換する層と、Kotlin Multiplatform で Room を設定する方法を説明します。

主な機能

  1. 01リポジトリ境界の設計
  2. 02層ごとのエラーマッピング
  3. 03アーキテクチャ層別の Result 処理
  4. 04KMP Room の設定指針
  5. 05ターゲット別 KSP 設定

活用シーン

  1. 01プラットフォーム例外の ViewModel への漏出防止
  2. 02リポジトリとユースケースのエラー境界決定
  3. 03KMP での共有 Room データベース設定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/rcosteira79/android-skills --skill android-data-layer

インストール後、Claude Code で/android-data-layerを入力して読み込まれたか確認します。

詳細

Android データレイヤー

データレイヤーは複数のソースからのデータを調整します。公開 API はリポジトリインターフェースであり、内部の要素(DAO、API サービス、DTO)が上位レイヤーに漏れることはありません。このスキルは、エラー伝播モデルと KMP-Room のセットアップに関する標準の参照先です。関連: android-skills:android-retrofit(Retrofit サービス/OkHttp/Hilt の配線)、android-skills:android-dev(アプリ全体のアーキテクチャ)。

エラー伝播 — レイヤー化モデル

目標は、データレイヤーのエラー(IOException/HttpException/SQLiteException)を ViewModel に決して到達させないことです。リポジトリの境界で、これらをドメインエラー型にマッピングします。プロジェクトに既存のエラー規約がある場合はそれに合わせ、ない場合は sealed な DataError 階層が妥当なデフォルトです。

sealed class DataError(message: String, cause: Throwable? = null) : Exception(message, cause) {
    class Network(cause: Throwable) : DataError("Network error", cause)
    class Server(val code: Int, message: String?) : DataError("Server error $code: $message")
    class Local(cause: Throwable) : DataError("Local storage error", cause)
}

// repository = error boundary: catch platform exceptions, return Result<T> (no domain layer) or throw DataError (with one)
suspend fun refreshNews(): Result<Unit> = withContext(io) {
    try { newsDao.insertAll(newsApi.fetchLatest().map(NewsDto::toEntity)); Result.success(Unit) }
    catch (e: IOException) { Result.failure(DataError.Network(e)) }
    catch (e: HttpException) { Result.failure(DataError.Server(e.code(), e.message())) }
}

ドメインレイヤーがある場合、境界は1つ外側のレイヤーに移ります。

  1. データソースは、プラットフォーム/ライブラリの例外(IOException、HttpException、SQLiteException)をスローします。
  2. リポジトリはエラー境界です。これらをキャッチして DataError にマッピングし、生のプラットフォーム型を決して漏らしません。ドメインレイヤーがない場合(シンプルな MVVM): リポジトリは Result<T> を直接返し、ViewModel はプラットフォーム例外を知らずに処理します。
  3. ユースケース(存在する場合)は Result の境界です。リポジトリは代わりに DataError を スロー し、ユースケースがそれをキャッチして、ドメイン固有のエラーモデルを含む Result<T> を返します。ユースケースはプラットフォーム例外型を決してキャッチしません。
  4. ViewModelは Result<T> を処理し、UI 状態(明示的な読み込み/成功/エラー)にマッピングします。

(Room の型には Entity 接尾辞(ArticleEntity)をデフォルトとして使用します。プロジェクトですでに Cached 接頭辞などの規約が使われている場合は、それに合わせます。)

KMP の Room(commonMain)

Room は 2.7.0 以降、KMP で安定しています。共有セットアップは、Android 専用のセットアップと次の3点で異なります。

  1. @Database に付ける @ConstructedBy(...) と、Room がプラットフォームごとの actual を生成する対象となる expect object を組み合わせます。
  2. BundledSQLiteDriver(androidx.sqlite:sqlite-bundled 由来)を使い、Android/iOS/JVM/web で同じ SQLite バージョンに固定します(Android のシステム SQLite のバージョンは API レベルやデバイスによって異なります)。
  3. setQueryCoroutineContext(Dispatchers.IO) を指定します。Android の Room ではこれがデフォルトですが、KMP ではデフォルトではありません。
// commonMain
@Database(entities = [ArticleEntity::class], version = 1, exportSchema = true)
@ConstructedBy(AppDatabaseConstructor::class)
abstract class AppDatabase : RoomDatabase() { abstract fun articleDao(): ArticleDao }

@Suppress("KotlinNoActualForExpect")
expect object AppDatabaseConstructor : RoomDatabaseConstructor<AppDatabase>

fun getRoomDatabase(builder: RoomDatabase.Builder<AppDatabase>): AppDatabase =
    builder.setDriver(BundledSQLiteDriver()).setQueryCoroutineContext(Dispatchers.IO).build()

各プラットフォームで独自のビルダーを用意します。Android では Room.databaseBuilder(context, AppDatabase::class.java, "app.db")、iOS/JVM では Room.databaseBuilder<AppDatabase>(databasePath = …) を使用します。KSP はターゲットごとに設定する必要があります(ksp(libs.androidx.room.compiler) は Android 専用です)。

dependencies {
    add("kspAndroid", libs.androidx.room.compiler)
    add("kspIosArm64", libs.androidx.room.compiler)
    add("kspIosSimulatorArm64", libs.androidx.room.compiler)
    // … one per target
}
room { schemaDirectory("$projectDir/schemas") }   // export the schema for migration-history tracking

Android 専用プロジェクトでは、@ConstructedBy を省略し、Room.databaseBuilder(context, AppDatabase::class.java, "app.db") を直接呼び出します。Room の動作は同じです。データレイヤーを commonMain に配置する必要がある場合に、KMP セットアップを任意で選択します。

コメント