LASSIC Media らしくメディア
アプリのマルチモジュール化でビルド時間を短縮する外注の進め方
LASSIC IT事業部|元請(プライムベンダー)としてシステム保守・運用を受託
この記事のポイント
- アプリのマルチモジュール化は、Gradleの並列ビルド・インクリメンタルビルド・構成キャッシュを活用してビルド時間を短縮できる手法です。
- 依存方向の設計(featureからcoreへの一方向)と循環依存の回避が、マルチモジュール化をトラブルなく運用するうえで欠かせません。
- 外注先に依頼する際は、モジュール境界の設計・公開APIの合意・CIパイプラインの改修を含めたRFPを作成することで、手戻りを防げます。
目次
アプリのマルチモジュール化とは何か
アプリのマルチモジュール化とは、1つの大きなAndroid・iOSプロジェクトを機能単位・レイヤー単位の複数モジュールに分割し、各モジュールを独立してビルド・テスト・保守できる構成にする開発手法です。ビルド構成の改善を主な目的とする点で、実行時のメモリ使用量やFPS改善(パフォーマンス改善)やバイナリサイズの削減とは目的が異なります。
モノリシック構成では、1行のコード変更でもプロジェクト全体を再コンパイルすることになります。アプリが肥大化するほどフルビルドの時間が伸び、CI/CDパイプラインの待機時間も長くなります。マルチモジュール化では変更があったモジュールだけを再ビルドするため、チームの開発サイクル全体を短縮できます。
ビルド遅延の原因とモジュール分割が機能する仕組み
アプリのビルド時間が長くなる根本的な原因は、コードベースが1つのモジュールに集中していることにあります。Gradleは依存関係グラフを解析して変更箇所を特定しますが、モジュールが1つだと変更の有無にかかわらずプロジェクト全体をコンパイル対象として扱いやすくなります。その結果、機能追加のたびにフルビルドが走り、チームが大きくなるほど待機時間が累積します。
マルチモジュール構成にすると、Gradleの3つのビルド最適化機能を組み合わせて活用できます。
- インクリメンタルビルド:前回のビルド以降に変更されたモジュールだけを再コンパイルします。
- ビルドキャッシュ:同じ入力に対するタスク出力をキャッシュし、次回以降のビルドで再利用します。CIサーバーとローカル環境の間でキャッシュを共有することも可能です。
- 並列ビルド:依存関係のない複数モジュールを同時にビルドします。マルチコア環境でのビルド時間短縮に効果的です。
Android公式ドキュメント「Guide to app modularization」(Google、developer.android.com)では、インクリメンタルビルド・ビルドキャッシュ・並列ビルドの各機能がモジュール化によってビルドパフォーマンスを向上させると明記されています*1。
iOSでは、Swift Package Manager(SPM)(Appleが提供する公式パッケージ管理ツール)を使ったローカルパッケージ分割が有効です。Xcodeプロジェクトをローカルパッケージに分割すると、SwiftUIプレビューのビルドがパッケージ単位で完結するため、UIの試行錯誤にかかる待機時間を短縮できます。swift.org公式サイトでも、Swift Package Managerはコードのモジュール化の手段として位置づけられています*2。
AndroidのGradleマルチプロジェクト設定ポイント
Androidアプリをマルチモジュール化する際は、Gradleのマルチプロジェクト構成を使います。settings.gradleで各サブプロジェクト(モジュール)を定義し、それぞれのbuild.gradleで依存関係を宣言します。ビルドパフォーマンスを引き出すには、gradle.propertiesへの設定追記が欠かせません。
Gradle公式ドキュメント「Improving the Performance of Gradle Builds」*3で推奨されている主な設定は以下のとおりです。
| 設定項目(gradle.properties) | 効果 |
|---|---|
org.gradle.parallel=true |
依存関係のないモジュールを並列コンパイルします。 マルチコア環境でビルド時間を短縮できます。 |
org.gradle.caching=true |
同じ入力に対するタスク出力をキャッシュし再利用します。 ブランチ切り替え後のビルドでも効果があります。 |
org.gradle.configuration-cache=true |
ビルドグラフの解析結果をキャッシュします。 2回目以降のビルドで構成フェーズをスキップできます。 |
org.gradle.daemon=true |
GradleデーモンによりJVM起動コストを削減します。 ローカル開発環境でのビルド応答速度が改善します。 |
featureからcoreへの一方向依存設計
モジュール構成を設計する際の核心は、依存方向を一方向に保つことです。Android公式ドキュメント「Common modularization patterns」*4では、フィーチャーモジュールがデータモジュール(コアモジュール)に依存し、その逆方向の依存を持たない設計が推奨されています。
典型的なレイヤー構成は次のようになります。アプリモジュールがフィーチャーモジュールに依存し、フィーチャーモジュールがデータモジュールとコアモジュールに依存します。データモジュールとコアモジュールは他のモジュールに依存しません。この一方向の依存グラフを守ることで、あるモジュールの変更が予期せず別のモジュールを再コンパイルさせるリスクを低減できます。
循環依存の回避とAPIの公開境界
循環依存(A→B→Aのような相互参照)が発生すると、モジュール分割の効果がなくなるだけでなく、Gradleのビルドが失敗することもあります。解消策として、共通の抽象化モジュールを導入し、双方がその抽象に依存する設計に変更します。
各モジュールが外部に公開するAPIの範囲は、Kotlinのinternal修飾子やprivate修飾子で制限します。また、Gradleの依存宣言ではimplementationを使うことで推移的依存を隠蔽できます。api宣言は本当に外部公開が必要な場合に限定することで、モジュール間の結合度を低く保てます*4。
iOSのSwift Package Managerによるローカルパッケージ分割
iOSでは、Swift Package Manager(SPM)(Appleが提供するSwift公式のパッケージ管理ツール)を使ったローカルパッケージ分割が主流の手法です。Xcodeプロジェクト内にローカルパッケージを作成すると、機能単位のコードをパッケージとして分離でき、Xcodeのビルドシステムがパッケージ単位でインクリメンタルビルドを行います。
巨大なXcodeプロジェクトをローカルパッケージに分割する利点は、ビルド時間の短縮だけにとどまりません。SwiftUIのプレビュー機能はパッケージ境界内でビルドが完結するため、UIコンポーネントの確認サイクルが短くなります。また、同一コードを参照するエンジニアが減るため、.xcodeprojファイルのコンフリクト発生頻度も低下します。
従来のフレームワーク(.framework)やダイナミックライブラリによる分割と比べると、SPMのローカルパッケージはXcodeのプロジェクト設定に組み込みやすく、外部リポジトリへの公開なしに利用できます。バイナリフレームワーク(.xcframework)形式で配布することで、さらにビルド時間の短縮が期待できる場合もありますが、デバッグ容易性とのトレードオフがあるため、外注先との要件定義段階で方針を合意しておくことが大切です。
マルチモジュール化で解消されるチーム開発の課題
ビルド時間の短縮は、チームの開発効率に直接影響します。フルビルドに長時間かかる環境では、エンジニアがコードレビューの確認や動作確認を後回しにしがちです。マルチモジュール化でビルド待機時間が短縮されると、フィードバックループが速くなり、バグの早期発見につながります。
コンフリクトの削減も大きな効果です。複数のエンジニアが同じファイルを同時に編集するとコンフリクトが発生しますが、モジュールの担当範囲が明確になることで、変更が別々のファイルに分散します。特に大人数での並行開発では、コンフリクト対応の工数削減がそのまま開発速度の向上につながります。
テスト容易性の向上も見逃せないポイントです。Android公式ドキュメント*1でも「テスタビリティ」がモジュール化の利点として挙げられています。モジュール単位でユニットテストを実行できるため、テストスイート全体を毎回実行する必要がなく、特定モジュールのテストだけを素早く走らせられます。CI環境での差分テスト実行(変更があったモジュールのみテストする)も実現しやすくなります。
外注先への依頼手順と確認すべき技術要件
マルチモジュール化の外注を進める際、最初に行うべきは現状のモジュール構成と依存グラフの整理です。既存コードの全体像が曖昧なまま外注先に依頼すると、モジュール境界の設計が場当たり的になり、後から循環依存や過剰な公開APIが生まれやすくなります。
外注化すべき工程と内製で判断が必要な工程
モジュール境界の方針(どのドメインをどのモジュールに配置するか)は、事業ロジックに精通している自社担当者が主導して決める必要があります。技術的な実装(Gradleマルチプロジェクト設定、SPMローカルパッケージ作成、既存コードの移行作業)は外注先に委託できます。CIパイプラインの改修(差分ビルド・キャッシュ設定)も合わせて依頼範囲に含めることを検討してください。
RFPに含めるべき技術要件
外注先を選定するためのRFP(提案依頼書)には、以下の技術要件を明記することで、提案の比較がしやすくなります。
- 対象プラットフォームと現状の構成:Android(Gradle AGP(Android Gradle Plugin)バージョン)またはiOS(Xcodeバージョン・SPM利用有無)
- モジュール分割の目標状態:想定モジュール数・レイヤー構成の方針(feature / data / coreの三層か否か)
- 既存テストの有無と品質要件:移行前後でテストカバレッジを維持する要件があるか
- CIパイプラインの現状:GitHub Actions / Bitrise / CircleCI など利用中のCI環境と、差分ビルドキャッシュの設定を依頼範囲に含めるか
- 段階的移行か一括移行か:既存機能を止めずに段階的にモジュール化する場合の方針
必要なスキルセットと工数の目安
マルチモジュール化を内製で進めるには、Gradleビルドスクリプトの知識・Kotlin/Swiftのアクセス修飾子の理解・CI設定の経験が揃ったエンジニアが必要です。さらに依存関係グラフの設計・リファクタリングの進め方・既存テストの維持という複数の専門領域が重なるため、習熟度の低いチームが初めて取り組むと調査・設計フェーズだけで相応の時間を要します。
外注先に依頼する場合、移行規模によって工数は大きく変わります。モジュール数が少ない初期分割(3〜5モジュール程度)でも、現状分析・設計・実装・CI改修・テスト確認を含めると、複数名のエンジニアが複数週にわたって関与するケースが一般的です。費用感は事業規模・既存コードの複雑さ・テスト要件によって幅が生まれるため、複数の外注先から見積もりを取得して比較することが大切です。
まとめ:外注前に握っておくべき3つの判断軸
本稿では、モバイルアプリのマルチモジュール化によるビルド時間短縮の仕組みと、外注を進める際の要点を整理しました。要点を3つに集約すると次のとおりです。
第一に、マルチモジュール化の主目的はビルド構成の改善であり、実行時パフォーマンスやアプリサイズ削減とは異なる施策です。解決したい課題(ビルドが遅い、コンフリクトが多い、CIが重い)と手法の対応関係を社内で合意したうえで外注先と話を進めましょう。
第二に、依存方向の設計(featureからcoreへの一方向)と循環依存の回避は、設計フェーズで決めるべき事項です。外注先任せにすると後から修正コストが膨らむため、モジュール境界の方針は自社主導で決定してからRFPを作成します。
第三に、CIパイプラインの改修(差分ビルド・キャッシュ設定)を依頼範囲に含めることで、移行後のビルド短縮効果を継続して享受できます。gradle.propertiesの設定だけでなく、CI環境でのキャッシュ共有設定まで含めた依頼内容にすることが大切です。
よくある質問
マルチモジュール化とアプリサイズ削減は同じですか?
異なる施策です。マルチモジュール化はビルド時間の短縮・コンフリクト削減・テスト容易性の向上を主な目的とするビルド構成の改善です。アプリサイズ削減はバイナリファイルの容量を減らすための施策(未使用リソースの除去・ProGuard/R8の適用など)であり、目標とする指標が違います。両方を並行して進めることは可能ですが、混同すると依頼内容が曖昧になるため、外注先への要件定義では分けて記載することをお勧めします。
GradleマルチプロジェクトとGradleマルチモジュールAPKは違いますか?
異なる概念です。Gradleマルチプロジェクト(マルチモジュール構成)は、1つのアプリを複数のサブプロジェクト(モジュール)に分割してビルド時間を短縮する手法です。マルチモジュールAPKはAndroid App Bundle(AAB)を使って機能ごとにモジュールを動的に配信するDynamic Delivery(動的機能配信)の仕組みです。前者はビルド構成の改善、後者はアプリ配信サイズの最適化が目的であり、それぞれ独立して採用できます。
iOSでSPMとフレームワーク化(.xcframework)はどちらが向いていますか?
社内利用のローカル分割にはSPMが向いています。外部リポジトリへの公開不要で設定が簡潔なため、既存プロジェクトへの段階的な適用がしやすいです。バイナリ配布(.xcframework)は、ビルド済みバイナリを共有することでビルド時間をさらに短縮できますが、デバッグ時のシンボル参照やXcodeバージョン対応の管理コストが増えます。外注先との設計合意の段階で、チームのスキルセットとデバッグ要件を踏まえてどちらを選ぶか決めることが大切です。
マルチモジュール化の外注費用はどのくらいですか?
費用はアプリの規模・既存コードの複雑さ・移行対象モジュール数・CI/CDパイプライン改修の有無によって大きく異なります。数モジュールの初期分割から始める小規模な移行と、数十モジュールを設計し直す大規模リファクタリングでは工数が大きく変わります。費用を比較するためには複数の外注先に同一要件でRFPを提示して見積もりを取得することが大切です。なお、本記事の費用情報は市場参考値であり一次資料にもとづくものではありません。正確な費用は個別のお見積もりでご確認ください。
既存アプリへの段階的なマルチモジュール化は可能ですか?
可能です。既存のモノリシックアプリを一度にモジュール化するよりも、新規追加機能からモジュール化し、既存コードを徐々に移行する段階的アプローチが実務上よく採られます。Android公式のガイドでも、段階的な分割を前提とした設計パターンが示されています。外注先に依頼する際は「段階的移行」か「一括移行」かを明示し、フェーズ分けの基準(機能単位か、レイヤー単位かなど)を事前に合意しておくと手戻りを防げます。
著者:テレリモ総研編集部 鈴木 亮佑
ご不明な点はお問い合わせフォームからもご連絡いただけます。
- *1 出典:Google「Guide to app modularization」(Android Developers、2024年)
- *2 出典:Swift.org「Swift Packages」(2024年)
- *3 出典:Gradle「Improving the Performance of Gradle Builds」(Gradle User Manual)
- *4 出典:Google「Common modularization patterns」(Android Developers、2024年)