ABEMAモバイルアプリのアーキテクチャと格闘する
CyberAgent のインターン生が、ABEMA アプリの既存アーキテクチャをクリーンアーキテクチャと Kotlin Multiplatform (KMP) へ移行する実践プロセスと、責務分離の重要性について報告している。
キーポイント
KMP とクリーンアーキテクチャへの移行戦略
ABEMA は既存の Flux アーキテクチャから、ビジネスロジックを共通モジュールに集約し、ViewModel をプラットフォーム側に配置する KMP 設計へ移行している。
OS 間での仕様統一と実装差の解消
iOS と Android で実装に違いがあり挙動が異なる問題を、KMP による共通化で解決し、OS 間で仕様を確実に揃えることを目指している。
責務分離の実践的課題と教訓
インターン生は、形式だけのアーキテクチャ適用が失敗しやすく、抽象化されたインターフェースの記述やテスト設計において深い理解が必要であることを学んだ。
UseCase と Presenter の役割分担
純粋なビジネスロジックを UseCase に集約し、ViewModel はそれを呼び出す層として機能することで、プラットフォーム間の依存関係を減らす設計を採用している。
責務分離の思考レベルでの理解深化
テストコードをファイル単位だけでなく、API 呼び出しとデータマッピングなど「責務ごとに分ける」思考プロセスが重要であると学びました。
設計書先行によるリスク管理と合意形成
大規模改修においてクラス図やシーケンス図を先に作成し、チームで方針を固めることで手戻りを防ぎ、認識合わせの基盤となりました。
設計書を起点とした AI 開発の効率化
充実した設計書が AI のコンテキストとして機能し、実装を半自動化することで人間の工数を方針判断とレビューに集中させることができました。
重要な引用
「KMPは、AndroidとiOSの間でロジックを共通化できる仕組みであり、どこまでを共通モジュールに置き、どこからを各Platform側に置くかを柔軟に決められるという特徴があります。」
「インターフェースの KDoc が、実装クラスの中身を前提とした書き方になっている」
「抽象が具体に縛られている」
形だけでなく思考のレベルでも責務を分ける感覚を、少しずつ掴むことができたと思います
設計書が AI のコンテキスト把握におけるドキュメントとして機能したことで、後述する Claude Code のスキル群が、そのまま設計書を起点として動作することができました
大まかに実装が合っているものは作れても、細かな実装の質を担保するためには、技術的知見が人間側に備わっていることが重要であると痛感しました。
影響分析・編集コメントを表示
影響分析
この記事は、大規模モバイルアプリにおける KMP とクリーンアーキテクチャの実践的導入プロセスを具体的に示しており、特に「形式だけの適用」のリスクと「責務分離の本質的理解」の重要性を強調している点で現場の開発者にとって有益である。ABEMA のような大規模サービスが直面する OS 間仕様の不一致問題を解決するための具体的な設計パターンを提供している。
編集コメント
大規模アプリのアーキテクチャ移行における、理論と実践のギャップを埋める貴重な実例報告です。特に「抽象化」の重要性について具体例を交えて解説している点が評価できます。
はじめに
はじめまして!埼玉大学大学院 修士2年の 半澤 恭介 です。
2026年4月の1ヶ月間、CyberAgent 主催のインターンシップ「CA Tech JOB」に参加し、ABEMAのAndroidエンジニアとして勤務させていただきました。
この記事では、インターンシップ期間中に取り組んだことと学んだことについてご紹介します。
課題: Flux アーキテクチャからクリーンアーキテクチャベースの Kotlin Multiplatform 設計への移行
このインターンでは、既存のFluxアーキテクチャで実装されていた画面を、クリーンアーキテクチャベースのKotlin Multiplatform (KMP) 設計へ移行するというタスクに取り組みました。
本記事では、今回の移行で実際に考えた設計と、実践した開発プロセスについて紹介します。
学んだこと
アーキテクチャ全体像
ABEMAのモバイルアプリでは、画面ごとにクリーンアーキテクチャベースのKotlin Multiplatform (KMP) 設計への移行を進めており、今回の改修もこの方針に沿って進めました。KMPは、AndroidとiOSの間でロジックを共通化できる仕組みであり、どこまでを共通モジュールに置き、どこからを各Platform側に置くかを柔軟に決められるという特徴があります。

ABEMAでは、上図のように、純粋なビジネスロジックを共通モジュールに配置し、ViewModelはPlatform側(iOS/Android)に配置するという設計をとっています。UseCaseに純粋なビジネスロジックを集約させ、ViewModelはそれを呼び出す層として実装することで、Platform間でのViewModelの実装差を発生しにくくするという設計をとっています。
今回担当した画面でも、実際にiOSとAndroid間で実装に違いがあり、アプリの挙動が異なる問題が存在していました。KMPによって共通化を行うことで、OS間で仕様を確実に揃えることも可能になります。
また、UseCaseは両OSから参照される共通処理として純粋なロジックを担う層です。Platform側のViewModelを直接参照できないため、UseCaseは結果をPresenterに渡し、ViewModelがそれを受けて画面の状態を更新する形をとっています。
知っているだけだった責務の分離
インターンに参加する前は、アーキテクチャを形式的に捉えていました。「責務を分離するためにファイルを切り出す」「テストで差し替えるためにDIを用いる」といった個別のプラクティスを機械的に適用する認識に留まっており、各レイヤーが存在する理由までは十分に理解できていませんでした。
そのため、実装を進める中で、形式だけ整えても責務が混ざってしまう場面に何度か遭遇しました。
– インターフェースのKDocが、実装クラスの中身を前提とした書き方になっている
– APIリクエストのユニットテストが、ドメインオブジェクトへのマッピングまで検証してしまっていた
具体的には、それぞれ次のような形でした。
KDoc の例
kotlin
// image 抽象が具体に縛られている
interface UserRepository {
/** Room データベースからユーザーを取得する */
fun getUser(id: String): User
}
// image 抽象として閉じている
interface UserRepository {
/** 指定された ID のユーザーを取得する */
fun getUser(id: String): User
}
テストの例
kotlin
// image 責務が混ざったテスト
@Test fun getUser returns Alice() {
val response = mockApi.getUser("123")
val user = mapper.toUser(response) // ← マッピングのテストになっている
assertEquals("Alice", user.name)
}
// image 責務ごとに分けたテスト
@Test fun api returns 200() { /* ... */ }
@Test fun mapper converts response to User() { /* ... */ }
ファイルとしては分かれていても、思考としては責務が混ざっているということです。インターンを通して、形だけでなく思考のレベルでも責務を分ける感覚を、少しずつ掴むことができたと思います。
実践した開発スタイル
設計書を先に書く
開発に取り組む際、いきなり実装に入るのではなく、最初にクラス図とシーケンス図を含む設計書を作成しました。今回のような既存の Flux アーキテクチャ(注:Flux)から、クリーンアーキテクチャベースの KMP(注:Kotlin Multiplatform)設計へ移行する大規模な改修では、「どこまで責務を分解するか」「プラットフォーム境界をどこに引くか」といった方針を最初に固めておかなければ、実装中に方向性が揺らぎ、手戻りが発生しやすくなります。
実装の方針について、チームメンバーと認識を合わせて、合意をとって進めていくことで実装のリスク管理も行えた点も実務の開発プロセスとして、価値があったと思っています。
設計書先行で実際に効いたこと
- トレーナーとの実装方針の認識合わせに寄与した
実装に着手する前にクラス図・シーケンス図を用いて合意形成ができることは、大きな利点でした。実装を進めてレビューを受ける中で、考慮漏れが発生する場面はあります。しかし、共通の認識があることで相談がしやすく、大きな手戻りを発生させずに進めることができました。
- AI による半自動開発の基盤として機能した
設計書が AI のコンテキスト把握におけるドキュメントとして機能したことで、後述する Claude Code のスキル群が、そのまま設計書を起点として動作することができました。設計書がなければ、AI に渡す文脈を毎回作り直す必要が生じ、効率が大幅に低下していたと考えられます。
Claude Code を使って開発を半自動化する
インターン中は Claude Code を使用して開発を行いました。個人では使える規模に限りがあるため、この機会に AI を用いた開発プロセスを検証しました。
結果論ではありますが、設計書が十分に充実しているため、実装自体は AI を用いて機械的に進めることができました。これによって、人間の作業を「方針の判断」と「コードレビュー」に集中することができました。この開発プロセスのために、Claude Code のスキルを自作し利用しました。
具体的には、「作成した設計書を読み込み実装する」「実装したコードをレビューする」「得られた知見や進捗を設計書に更新する」という 3 つのスキルです。人間は出力されたコードを読み議論を重ねることで、ナレッジが蓄積されて AI の精度が向上していくシステムになっています。

実際にこの開発プロセスを試して感じたことは、人間のレビュー精度の影響力が非常に大きいということです。大まかに実装が合っているものは作れても、細かな実装の質を担保するためには、技術的知見が人間側に備わっていることが重要であると痛感しました。
まとめ
このインターンを通して、アーキテクチャの知見と AI 開発の検証を行うことができました。その他、社員の方との 1on1 やトレーナーとお話しする機会も非常に多く設けていただいたため、エンジニアとして仕事への向き合い方やどう学んでいくか、はたまた何気ない技術雑談まで、視野の広がる、とても充実した 1 ヶ月間でした!
最後に
1 ヶ月という短い期間ではありましたが、お世話になったトレーナーの方、人事の方、ABEMA チームの方々、ランチでお話いただいた方々、本当にありがとうございました!
原文を表示
はじめに
はじめまして!埼玉大学大学院 修士2年の 半澤 恭介 です。
2026年4月の1ヶ月間、CyberAgent 主催のインターンシップ「CA Tech JOB」に参加し、ABEMAのAndroidエンジニアとして勤務させていただきました。
この記事では、インターンシップ期間中に取り組んだことと学んだことについてご紹介します。
課題: Flux アーキテクチャからクリーンアーキテクチャベースの Kotlin Multiplatform 設計への移行
このインターンでは、既存のFluxアーキテクチャで実装されていた画面を、クリーンアーキテクチャベースのKotlin Multiplatform (KMP) 設計へ移行するというタスクに取り組みました。
本記事では、今回の移行で実際に考えた設計と、実践した開発プロセスについて紹介します。
学んだこと
アーキテクチャ全体像
ABEMAのモバイルアプリでは、画面ごとにクリーンアーキテクチャベースのKMP設計への移行を進めており、今回の改修もこの方針に沿って進めました。KMPは、AndroidとiOSの間でロジックを共通化できる仕組みであり、どこまでを共通モジュールに置き、どこからを各Platform側に置くかを柔軟に決められるという特徴があります。

ABEMAでは、上図のように、純粋なビジネスロジックを共通モジュールに配置し、ViewModelはPlatform側(iOS/Android)に配置するという設計をとっています。UseCaseに純粋なビジネスロジックを集約させ、ViewModelはそれを呼び出す層として実装することで、Platform間でのViewModelの実装差を発生しにくくするという設計をとっています。
今回担当した画面でも、実際にiOSとAndroid間で実装に違いがあり、アプリの挙動が異なる問題が存在していました。KMPによって共通化を行うことで、OS間で仕様を確実に揃えることも可能になります。
また、UseCaseは両OSから参照される共通処理として純粋なロジックを担う層です。Platform側のViewModelを直接参照できないため、UseCaseは結果をPresenterに渡し、ViewModelがそれを受けて画面の状態を更新する形をとっています。
知っているだけだった責務の分離
インターンに参加する前は、アーキテクチャを形式的に捉えていました。「責務を分離するためにファイルを切り出す」「テストで差し替えるためにDIを用いる」といった個別のプラクティスを機械的に適用する認識に留まっており、各レイヤーが存在する理由までは十分に理解できていませんでした。
そのため、実装を進める中で、形式だけ整えても責務が混ざってしまう場面に何度か遭遇しました。
– インターフェースのKDocが、実装クラスの中身を前提とした書き方になっている
– APIリクエストのユニットテストが、ドメインオブジェクトへのマッピングまで検証してしまっていた
具体的には、それぞれ次のような形でした。
KDoc の例
kotlin
// 抽象が具体に縛られている
interface UserRepository {
/** Room データベースからユーザーを取得する */
fun getUser(id: String): User
}
// 抽象として閉じている
interface UserRepository {
/** 指定された ID のユーザーを取得する */
fun getUser(id: String): User
}テストの例
kotlin
// 責務が混ざったテスト
@Test fun `getUser returns Alice`() {
val response = mockApi.getUser("123")
val user = mapper.toUser(response) // ← マッピングのテストになっている
assertEquals("Alice", user.name)
}
// 責務ごとに分けたテスト
@Test fun `api returns 200`() { /* ... */ }
@Test fun `mapper converts response to User`() { /* ... */ }ファイルとしては分かれていても、思考としては責務が混ざっているということです。インターンを通して、形だけでなく思考のレベルでも責務を分ける感覚を、少しずつ掴むことができたと思います。
実践した開発スタイル
設計書を先に書く
開発に取り組む際、いきなり実装に入るのではなく、最初にクラス図とシーケンス図を含む設計書を作成しました。今回のような既存のFluxアーキテクチャから、クリーンアーキテクチャベースのKMP設計へ移行する大規模な改修では、「どこまで責務を分解するか」「プラットフォーム境界をどこに引くか」といった方針を最初に固めておかなければ、実装中に方向性が揺らぎ、手戻りが発生しやすくなります。
実装の方針について、チームメンバーと認識を合わせて、合意をとって進めていくことで実装のリスク管理も行えた点も実務の開発プロセスとして、価値があったと思っています。
設計書先行で実際に効いたこと
- トレーナーとの実装方針の認識合わせに寄与した
実装に着手する前にクラス図・シーケンス図を用いて合意形成ができることは、大きな利点でした。実装を進めてレビューを受ける中で、考慮漏れが発生する場面はあります。しかし、共通の認識があることで相談がしやすく、大きな手戻りを発生させずに進めることができました。
- AI による半自動開発の基盤として機能した
設計書がAIのコンテキスト把握におけるドキュメントとして機能したことで、後述する Claude Codeのスキル群が、そのまま設計書を起点として動作することができました。設計書がなければ、AIに渡す文脈を毎回作り直す必要が生じ、効率が大幅に低下していたと考えられます。
Claude Codeを使って開発を半自動化する
インターン中はClaude Codeを使用して開発を行いました。個人では使える規模に限りがあるため、この機会にAIを用いた開発プロセスを検証しました。
結果論ではありますが、設計書が十分に充実しているため、実装自体はAIを用いて機械的に進めることができました。これによって、人間の作業を「方針の判断」と「コードレビュー」に集中することができました。この開発プロセスのために、Claude Codeのスキルを自作し利用しました。
具体的には、「作成した設計書を読み込み実装する」「実装したコードをレビューする」「得られた知見や進捗を設計書に更新する」という3つのスキルです。人間は出力されたコードを読み議論を重ねることで、ナレッジが蓄積されてAIの精度が向上していくシステムになっています。

実際にこの開発プロセスを試して感じたことは、人間のレビュー精度の影響力が非常に大きいということです。大まかに実装が合っているものは作れても、細かな実装の質を担保するためには、技術的知見が人間側に備わっていることが重要であると痛感しました。
まとめ
このインターンを通して、アーキテクチャの知見とAI開発の検証を行うことができました。その他、社員の方との1on1やトレーナーとお話しする機会も非常に多く設けていただいたため、エンジニアとして仕事への向き合い方やどう学んでいくか、はたまた何気ない技術雑談まで、視野の広がる、とても充実した1ヶ月間でした!
最後に
1ヶ月という短い期間ではありましたが、お世話になったトレーナーの方、人事の方、ABEMAチームの方々、ランチでお話いただいた方々、本当にありがとうございました!
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み