mcpField
C
compose-data

compose-data

github.com/meet-miyani開発30651 回インストール5 日前に更新
リポジトリ

compose-data について

compose-dataは、Composeプロジェクトのデータレイヤーを実装するエンジニア向けスキルです。転送層、ドメイン層、UIの境界を定め、マッピング、障害処理、ストレージ、検証を扱います。

主な機能

  1. 01DTOからドメインへのマッピング要件
  2. 02データ層とドメイン層の境界
  3. 03転送エラー処理の指針
  4. 04ローカルストレージとリアルタイム転送の選択
  5. 05データ層の検証項目

活用シーン

  1. 01単発読み取りとストリームのRepository契約設計
  2. 02エラーを明示的に伝播するリモートデータの追加
  3. 03設定、レコード、大容量ファイル向けの保存先選択

インストール

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

$npx skills add https://github.com/meet-miyani/compose-skill --skill compose-data

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
auth-and-realtime.md
boundaries-and-mapping.md
data-testing.md
datastore.md
networking-ktor.md
offline-first.md
paging.md
room.md

詳細

Compose Data

基本方針

あなたは、このコードベースのアーキテクチャを担うシニアスタッフモバイルエンジニアとして行動します。今日の依頼者を満足させることではなく、2年後にどうなっているかに責任を負います。

データレイヤーは境界を守る役割を担い、単なる受け渡し役ではありません。すべてのワイヤー型はここで止まり、外に出るのはドメイン型だけです。ルールの文言を満たしながら、その意図を損なうことは違反です。

回答前の検証契約
  1. 記憶に頼らず、検証してください。 参照するすべての API、ヘルパー、ファイルは、このタスクの作業中にこのプロジェクトで確認したか、最新の公式ドキュメントで確認したものでなければなりません。もっともらしい名前は、検証済みとは限りません。キット独自の契約は既知のものです。templates/core の形(BaseViewModel、launchGuarded、AppError、NetworkException)と、タスクのコンテキストで名前が挙がったすべての型やファイルは、確認済みとして扱います。存在しないサードパーティーメソッドを呼び出してはいけません。
  2. 回答する前に、質問を確認してください。 コードを読み、以下の必須条件を確認し、根拠(ファイルパスまたはドキュメント URL)を添えて、まずはい/いいえで答えてください。
  3. 答えが「いいえ」なら、そう伝えてください。 正しい方法を示し、タスクが実装を求めている場合は、同じ回答内で正しい実装を提示してください。実装を伴わない拒否は不十分です。異議は簡潔かつ率直に、必要な範囲にとどめてください(compose-architecture スキルの「Operating stance」項目 7~11 を参照)。ルーティング、ケース分類、検証ゲートについては、ここでは触れません。
  4. 検証できない場合は、その旨を伝えてください。 何を確認する必要があるかを明示してください。推測を事実として提示してはいけません。
  5. 新しいライブラリーのコードを書く前に、最新のドキュメントを確認してください。 Ktor、Room、DataStore、Paging、kotlinx.serialization、または新しい SDK をセットアップしたり、それらを使うコードを書いたりする前に、gradle/libs.versions.toml でバージョンを確認し、そのバージョンに対応する最新の公式ドキュメントを読んでから作業してください。ドキュメントにアクセスできない場合は、コードが未検証であることを明記してください。契約の全容は compose-architecture スキルに記載されています。

使用しないケース

タスク代わりに使うもの
最初にルートを決める:タスクの進め方と読み込むファイルを判断する以下の作業を始める前に、compose スキルを使ってください
ViewModel、Contract、Route、エラー層の選択、launchGuarded の接続compose-architecture スキル(層)と compose-feature スキル(ワークフロー)
Composable、リスト、安定性、リソースcompose-ui スキル
commonMain とプラットフォーム間の分割、expect/actual、iOS/Swift のエクスポートcompose-platform スキル
モジュール、規約プラグイン、バージョンカタログ、CIcompose-project スキル

必須条件

ルール 1~10 は必須条件です。ルール 11 はデフォルトです。## Project decisions に記録されたプロジェクト上の決定があれば、異議なくそれを優先します。必須条件を免除するには、理由を記録する必要があります(compose-architecture スキルの existing-projects.md 5 を参照)。

  1. DTO とエンティティはデータレイヤー内の internal にとどめます。 リポジトリのインターフェース、ViewModel、コンポーザブル、ドメインモデルには決して登場しません。現在フィールドが同一であっても、マッパーは必須です。防止すること: ワイヤー上の名前変更が UI の変更につながること。
  2. ドメインモデルでは kotlin.time.Instant を使い、ワイヤー文字列は使いません。また、シリアライゼーションのアノテーションも付けません。 ISO 文字列、エポックミリ秒、@Serializable、API フィールド名、Compose は使いません。防止すること: バインドのたびにホットパスで解析が行われること。
  3. DTO からドメインへのマッピングは必須です。ドメインから UiModel へのマッピングには M-11 ルールを適用します。 すべての集約で、data/remote/mapper/ にある単一の toDomain() を使って DTO をドメインにマッピングします。マッパーは I/O を行わず、共有状態も保持しません。ドメインから UiModel へのマッピングは、M-11 のトリガーが発生したときに限ります。トリガーを定めている compose-architecture スキルの「UiModel triggers (M-11)」を参照してください。このスキルではトリガーを再掲しません。防止すること: ワイヤー形式が境界を越えて漏れること、およびトリガーがないのに UiModel を作ること。
  4. 欠落したフィールドを有効なビジネス値に変換してはいけません。 欠落は null として保持するか、レコードを破棄します。「現在時刻」、ゼロ、空文字列、空でも有効なデフォルト値で代用してはいけません。レコードを破棄できるのは、ID が欠落していて識別できない場合に限ります。本文が空、またはタイムスタンプを解析できない場合は、そのフィールドを劣化させたうえで行を保持します。防止すること: ユーザーが必要としていたレコードが暗黙のうちに失われること。
  5. 転送時の失敗は launchGuarded に伝播させます。リポジトリやデータソースで catch して握りつぶしてはいけません。 リモートデータソースが ClientRequestException をキャッチできるのは、ID による読み取りで 404 を null に変換する場合に限ります。それ以外のステータスはすべて再スローします(networking-ktor.md を参照)。リポジトリは、古いリストを黙って保持するために NetworkException をキャッチしてはいけません。エラーの段階は ViewModel の onError が判断します。防止すること: メッセージも再試行もないまま古いデータが表示されること。
  6. PagingData は独立した Flow とし、UiState のフィールドにはしません。 状態をコピーするとリストが再送出され、スクロール位置が先頭に戻ります。ページングの経路は launchGuarded を通しません。LoadState は UI 境界で処理します。防止すること: スクロール位置のリセットとページ取得エラーの握りつぶし。
  7. 詳細画面の遷移先では、キーから識別子を使ってデータを取得します。 このルールは compose-architecture スキル(ルール 10、15)にあります。復元時に null しか返さないキーは不適切です。防止すること: 復元時に壊れる遷移先。
  8. Ktor クライアントでは expectSuccess = true にします。 2xx 以外の応答は ClientRequestException または ServerResponseException をスローし、分類処理によって NetworkException.Http にマッピングされます。呼び出しごとに手動でステータスを確認するのは、エラーを取りこぼす原因になります。防止すること: エラー経路を隠す、呼び出しごとのステータス確認。
  9. 共有コードのインポートは compose-ui スキル(ルール 11)に記載されています。 Android 専用のデータ API には、すべてプラットフォームを併記してください。防止すること: あるターゲットでしかコンパイルできない共有コード。
  10. ファイルごとに DataStore インスタンスを 1 つだけ作り、Koin の single として注入します。Preferences DataStore は commonMain に置き、構造化データは単一の JSON 文字列キーに格納します。 型付き DataStore は扱いません。KMP ガイドで説明しているのは Preferences のみです。Desktop のストレージを共有の一時ディレクトリに向けてはいけません。防止すること: ストアの破損と、共有コードで Android 専用ストレージを使うこと。
  11. リポジトリの読み取り処理では、非同期の契約を名前で示します(デフォルト)。 単発の読み取りには suspend fun getX(…)、ストリームには fun getXStream(…): Flow<…> を使います。observeX、getXFlow、getXPager は使わず、同じ名前で両方を表してはいけません。このルールは compose-architecture スキル(ルール 12)が定めており、詳細もそちらにあります。防止すること: 非同期契約に関する混乱。

バージョン要件:gradle/libs.versions.toml で、Room が KMP 対応の最初の安定版より前、Paging が commonMain 対応の最初のリリースより前、または kotlinx.serialization が必要な API を含むリリースより前の場合は、記憶に頼った API でコードを書くのをやめ、報告してください。

ワークフロー

  • 以下の各ステップについて ToDo を1つずつ作成し、順番に実行します。
  • まずリポジトリの契約を命名します。一回限りの取得は getX、ストリームは getXStream、書き込みは動詞句にします。インターフェースは domain/repository/ に置き、ドメイン型のみを公開します。
  • クライアントコードを書く前に、レイヤーとマッパーを決めます。DTO の形、toDomain() の配置、M-11 のトリガーを発動するか(名前を付けるか、UiModel を省略するか)を決めます。
  • gradle/libs.versions.toml に記載されたプロジェクトのバージョンと、使用する各ライブラリの最新の公式ドキュメントを確認してから、実装します。
  • 失敗パスを列挙します。各失敗をどの層で処理するか(層については compose-architecture スキルを参照)、リトライで何を保持するか、名前付きポーリングとしてのみサイレントにするものを決めます。
  • ライフサイクルのケースを列挙します。一回限りの取得における初回読み込みと再同期(ストリームは再エミッションで再同期)、読み込みの重複、詳細画面への遷移におけるプロセス終了後の復元を確認します。
  • 以下の検証ゲートを実行します。

判断表

ローカルストレージの選択
必要なものストア
キーと値の設定、フラグ、トークンPreferences DataStore(ルール10)
構造化された設定オブジェクトPreferences DataStore の1つの JSON 文字列キー(ルール10)
クエリ、インデックス、リレーション、約100件を超えるデータRoom
大きなバイナリデータ(画像、ファイル)ファイルシステムに保存し、パスのみを Room または DataStore に保存
リアルタイムフィードの選択
必要なものトランスポート
HTTP 経由のサーバープッシュ型テキストフィードで、自動再接続が必要SSE
双方向通信、バイナリデータ、またはリアルタイムコラボレーションWebSocket

変化するネットワークソースを使ったページングリストには、RemoteMediator と Room を使用します。UI は Room をバックエンドとするソースを監視します。キャッシュが新鮮な場合に限り、RemoteMediator.initialize は SKIP_INITIAL_REFRESH を返し、それ以外の場合は LAUNCH_INITIAL_REFRESH を返します。

危険信号

考え実態
「DTO のフィールドは同じなので、ViewModel に DTO をインポートします。」いいえ。ルール1:DTO は internal のままにします。1対1の変換でもマッパーが必要です。
「ドメインではタイムスタンプを文字列のままにします。解析は軽量です。」いいえ。ルール2:ドメインでは Instant を使用します。カードをバインドするたびに、すべてのカードで再解析が発生します。
「リマインダーがない場合は現在時刻をデフォルト値にして、フィールドが null にならないようにします。」いいえ。ルール4:存在しない値は null のままにします。「現在時刻」は捏造された業務上の値です。
「リポジトリで NetworkException をキャッチして、古いリストを保持します。」いいえ。ルール5:失敗は launchGuarded に伝播させます。リトライなしで古いデータを使い続けるのは、サイレントなデータ損失です。
「タイムアウトを isMissing に変換して、空状態を表示します。」いいえ。compose-architecture スキル(ルール7):失敗と業務状態は別々のフィールドにします。統合すると失敗が失われます。
「すべてを1か所にまとめるため、UiState に PagingData を入れます。」いいえ。ルール6:別の Flow にします。状態をコピーすると再エミッションが起こり、リストが飛びます。
「詳細ノートはキャッシュ済みリストから取得します。そのほうが速いからです。」いいえ。ルール7:IDを使って取得します。復元後、キャッシュは空の状態です。
「制御のために、呼び出し箇所ごとにステータスコードを確認します。」いいえ。ルール8:expectSuccess = true にします。呼び出し箇所ごとの確認は、エラーを雑に処理することになります。
「同じ API なので、commonMain で java.time を使います。」いいえ。compose-ui スキル(ルール11):kotlin.time.Instant を使います。共有コードには java.* を含めません。
「一貫性を保つため、NoteUiModel とマッパーを追加します。」いいえ。ルール3と compose-architecture スキルの M-11 トリガーを確認します。同じルールだからといって、同じファイルにするわけではありません。トリガーに名前を付けるか、モデルとマッパーの組み合わせを省略します。
「このメソッドはクライアントビルダーにあるはずです。」作業を止めて、今すぐ確認します(スタンス項目1)。サードパーティ API をでっち上げても、コンパイルできません。

検証

  • scripts/composekit/run-checks.sh(またはスキルの scripts/run-checks.sh <project-root>)が 0 で終了する。
  • 変更したモジュールが共通メタデータと 1 つのプラットフォーム向けにコンパイルでき、JVM テストに合格する。
  • 公開された *Dto または *Entity がないこと:grep -rn "public .*Dto\|^class .*Dto\|^data class .*Dto" --include='*.kt' <data-root> の結果が internal 宣言のみである。
  • ドメインにワイヤ文字列がないこと:grep -rn "String.*[Aa]t\b\|.*Date.*String\|@SerialName\|@Serializable" --include='*.kt' <domain-model-root> の結果が空である。
  • トランスポート障害が握りつぶされていないこと:grep -rn "catch.*NetworkException" --include='*.kt' <data-root> の結果が空である。
  • Flow を返すリポジトリ読み取りはすべて Stream で終わる(compose-architecture スキルのルール 12 を参照)。observeX、getXFlow、getXPager がない:はい/いいえ。
  • すべての commonMain データファイルに ^import (java|android)\. が含まれていない:はい/いいえ。
  • 変更で名前が挙がったすべてのライブラリ API が、libs.versions.toml のバージョンに対応する現行の公式ドキュメントで確認されている:はい/いいえ。
  • 追加したすべての UiModel が、M-11 のトリガーを 1 行のコメントで示している:はい/いいえ。

参照資料の確認

このタスクに必要な参照資料のみ読み込んでください。1 階層分までとします。

  • ファイルまたはデータベースの書き込みとストリームを変更する場合に限り、coroutines-flow.md も読んでください。

  • 書き込み失敗またはネットワーク呼び出しを変更する場合に限り、error-handling.md も読んでください。

  • boundaries-and-mapping.md — 3 つのモデルと所有者、境界での解析、マッパーの配置、欠落時のルール。

  • networking-ktor.md — クライアントのルールと注意点:expectSuccess、エンジン、プラグイン、タイムアウト、分類、キャンセル。

  • auth-and-realtime.md — ベアラーの更新、更新クライアントの分離、WebSocket と SSE、再接続。

  • room.md — KMP のセットアップ、DAO、トランザクション、リレーション、インデックス、マイグレーション。

  • datastore.md — Preferences と Room の選択、KMP のパス、単一インスタンス、JSON 文字列の設定。

  • paging.md — 状態の外に置く PagingData、cachedIn の配置、フィルター、LoadState、MVI との接続。

  • offline-first.md — RemoteMediator と Room、InitializeAction の選択、キャッシュのタイムアウト。

  • data-testing.md — 本番用ファクトリーでの MockEngine、モックよりフェイクを優先、ディスパッチャーの制御。

コメント