2026年のAndroidモジュール化:マルチモジュールアーキテクチャと面接対策
Androidアプリのマルチモジュール設計について詳しく解説。Gradleバージョンカタログ、コンベンションプラグイン、モジュール間通信パターン、そして技術面接でよく出題される質問と回答を紹介します。

Androidモジュール化は、モノリシックなコードベースを保守性と拡張性に優れたシステムへと変革する手法です。適切にモジュール化されたAndroidアプリは、ビルド時間の短縮、独立したテスト実行、そして新規開発者のオンボーディング期間の大幅な短縮を実現します。
モジュール化とは、Androidアプリを明確な境界を持つ独立したGradleモジュールに分割することです。モジュール間の低結合性と各モジュール内の高凝集性が基本原則となります。Googleの「Now in Android」リファレンスアプリは、40以上のモジュールでこのパターンを実証しています。
マルチモジュールアーキテクチャがAndroidアプリに重要な理由
単一モジュールのAndroidアプリでは、1行の変更でもすべてが再コンパイルされます。ネットワーク層の修正がUI層の再コンパイルを引き起こし、異なる機能を編集する2人の開発者が共有ファイルでマージコンフリクトを起こすことになります。インクリメンタルビルドでも4分以上かかることは珍しくありません。
マルチモジュールアーキテクチャは、これらの課題を直接解決します:
- 高速なビルド: Gradleは影響を受けたモジュールのみを再コンパイルします。
:feature:checkoutの変更は:feature:profileに影響しません。 - 並行開発: チームは定義されたAPIを持つ個別のモジュールを所有します。マージコンフリクトが大幅に減少します。
- 独立したテスト: 単体テストはアプリ全体をロードせずに単一モジュールに対して実行できます。
- コードの再利用:
:core:uiデザインシステムモジュールは、複数のアプリで使用可能なライブラリになります。
公式Androidモジュール化ガイドでは、単一責任の原則に従い、一緒に変更される機能やレイヤーを中心にモジュールを構成することを推奨しています。
モジュールの種類と責務
Now in Androidリポジトリは、エンタープライズアプリにもスケールする実績のあるモジュール分類を確立しています。
include(":app")
// Coreモジュール - 共有ユーティリティ
include(":core:common")
include(":core:data")
include(":core:database")
include(":core:datastore")
include(":core:designsystem")
include(":core:domain")
include(":core:model")
include(":core:network")
include(":core:ui")
// Featureモジュール - 画面固有のロジック
include(":feature:home")
include(":feature:search")
include(":feature:bookmarks")
include(":feature:settings")Appモジュール: MainActivity、ナビゲーション設定、アプリレベルのスキャフォールディングを含みます。すべてのFeatureモジュールと必要なCoreモジュールに依存します。
Coreモジュール: 機能間で共有される機能を提供します。:core:networkはAPI呼び出しを処理し、:core:databaseはRoomエンティティを管理し、:core:designsystemはアプリの視覚的コンポーネントを定義します。
Featureモジュール: 単一の画面またはユーザーフローをカプセル化します。各Featureモジュールは必要なCoreモジュールにのみ依存し、他のFeatureモジュールには決して依存しません。
この構造により、一方向の依存関係が強制されます:FeatureはCoreに依存し、AppはFeatureに依存します。
依存関係管理のためのGradleバージョンカタログ
25以上のモジュール間でバージョンの競合なく依存関係を管理するには、集中化された設定が必要です。Gradleバージョンカタログは、単一のlibs.versions.tomlファイルでこれを解決します。
# gradle/libs.versions.toml
[versions]
agp = "8.7.0"
kotlin = "2.1.0"
compose-bom = "2026.09.00"
ksp = "2.1.0-1.0.29"
hilt = "2.54"
room = "2.7.0"
retrofit = "2.11.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version = "1.15.0" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
[bundles]
compose = ["androidx-compose-ui", "androidx-compose-material3"]
room = ["room-runtime", "room-ktx"]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }各モジュールは型安全なアクセサで依存関係を参照します:
dependencies {
implementation(libs.bundles.compose)
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
}Gradleは同期時にこれらのアクセサを生成し、IDEの自動補完とコンパイル時の検証を可能にします。
Androidの面接対策はできていますか?
インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。
コンベンションプラグイン:DRYなビルド設定
コンベンションプラグインなしの25モジュールプロジェクトでは、各build.gradle.ktsで同じcompileSdk、minSdk、composeOptions、プラグイン適用を繰り返すことになります。異なるモジュールが異なる設定を使用してしまうリスクもあります。
コンベンションプラグインは、このロジックをbuild-logicインクルードビルドに集中化します。
import com.android.build.api.dsl.LibraryExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
class AndroidLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("com.android.library")
pluginManager.apply("org.jetbrains.kotlin.android")
extensions.configure<LibraryExtension> {
compileSdk = 35
defaultConfig {
minSdk = 26
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
}
}
}build-logic/convention/build.gradle.ktsでプラグインを登録します:
plugins {
`kotlin-dsl`
}
dependencies {
compileOnly(libs.android.gradlePlugin)
compileOnly(libs.kotlin.gradlePlugin)
}
gradlePlugin {
plugins {
register("androidLibrary") {
id = "myapp.android.library"
implementationClass = "AndroidLibraryConventionPlugin"
}
register("androidFeature") {
id = "myapp.android.feature"
implementationClass = "AndroidFeatureConventionPlugin"
}
register("androidHilt") {
id = "myapp.android.hilt"
implementationClass = "AndroidHiltConventionPlugin"
}
}
}Featureモジュールは最小限の設定のみで済むようになります:
plugins {
alias(libs.plugins.myapp.android.feature)
alias(libs.plugins.myapp.android.hilt)
}
dependencies {
implementation(projects.core.data)
implementation(projects.core.designsystem)
}GoogleのNow in Android build-logicもまさにこのパターンを採用しています。
モジュール間通信パターン
Featureモジュールは互いに直接依存してはなりません。この制約により、並列コンパイルが維持され、循環依存が防止されます。3つのパターンがクロスフィーチャー通信を処理します。
共有ルートによるナビゲーション
object Routes {
const val HOME = "home"
const val PRODUCT_DETAIL = "product/{productId}"
const val CHECKOUT = "checkout"
fun productDetail(productId: String) = "product/$productId"
}Featureモジュールは、どのモジュールが各デスティネーションを実装しているかを知らずに、:core:navigationからルートをインポートします。
共有ドメインモデル
data class Product(
val id: String,
val name: String,
val price: BigDecimal,
val imageUrl: String
):feature:catalogと:feature:checkoutの両方がProductクラスのために:core:modelに依存します。
イベントバスまたは共有ViewModel
ランタイム通信には、:core:common内の共有イベントバスまたはスコープ付きViewModelがクロスフィーチャーイベントを処理します:
object CartEventBus {
private val _events = MutableSharedFlow<CartEvent>()
val events: SharedFlow<CartEvent> = _events.asSharedFlow()
suspend fun emit(event: CartEvent) {
_events.emit(event)
}
}
sealed interface CartEvent {
data class ItemAdded(val productId: String) : CartEvent
data class ItemRemoved(val productId: String) : CartEvent
}Androidモジュール化に関する面接質問
モジュール化はシニアAndroid面接で頻繁に出題されます。面接官はビルドシステムの理解、アーキテクチャの意思決定、実践的なトレードオフを評価します。
面接でモジュール化について議論する際は、具体的な指標を参照してください:ビルド時間の改善、チームの生産性向上、またはプロジェクトの具体的なモジュール数などです。「関心の分離が良くなる」という抽象的な回答よりも、「モジュール化後、ビルド時間が4分から45秒に短縮された」という回答の方が説得力があります。
Q: Gradleのapi依存関係とimplementation依存関係の違いは何ですか?
implementation依存関係はモジュール内部のものです。モジュールの利用者はアクセスできません。api依存関係は推移的に公開されます:モジュールAがapi(libs.retrofit)を使用すると、Aに依存するモジュールBはRetrofitクラスを直接使用できます。
経験則:デフォルトでimplementationを使用します。依存関係がモジュールのパブリックAPIの一部である場合にのみapiを使用します。apiを多用するとカプセル化が壊れ、ビルドが遅くなります。
Q: 互いに依存していないFeatureモジュール間のナビゲーションをどのように処理しますか?
ルート定義とナビゲーションインターフェースを含む:core:navigationモジュールを作成します。Featureモジュールはこの共有モジュールに依存します。Appモジュールがデスティネーションを実装に紐付けます。これは依存性逆転の原則に従います:フィーチャーは具体的な実装ではなく抽象化に依存します。
Q: モジュールが多すぎることのトレードオフは何ですか?
各モジュールはGradle設定のオーバーヘッドを追加します:ビルドファイル、リソース処理、マニフェストマージ。初期同期時間が増加します。転換点はプロジェクトによって異なりますが、大規模アプリでは50以上のモジュールが一般的です。5人以上の開発者のチームでは、メリット(並列ビルド、独立したテスト、明確なオーナーシップ)が通常オーバーヘッドを上回ります。
Q: コンベンションプラグインはbuildSrcとどう違いますか?
buildSrcの変更はプロジェクト全体の再ビルドをトリガーします。インクルードビルド内のコンベンションプラグインは、利用するモジュールが変更されたときではなく、プラグインコードが変更されたときのみ再コンパイルされます。これにより、コンベンションプラグインは反復開発においてより高速になります。さらに、コンベンションプラグインはリポジトリ間で再利用するための個別のアーティファクトとして公開できます。
Q: Featureモジュールを単独でテストするにはどうしますか?
Featureモジュールはインターフェースを通じてCoreモジュールに依存します。テストフィクスチャでフェイク実装を作成します:
class FakeProductRepository : ProductRepository {
private val products = mutableListOf<Product>()
override suspend fun getProducts(): List<Product> = products
fun addProduct(product: Product) {
products.add(product)
}
}Featureモジュールのテストは、HiltのテストAPIまたは手動のコンストラクタインジェクションを通じてフェイクを注入します。
Androidアーキテクチャに関するより多くの練習問題については、MVVMアーキテクチャ面接問題と依存性注入モジュールをご覧ください。
今すぐ練習を始めましょう!
面接シミュレーターと技術テストで知識をテストしましょう。
マルチモジュールAndroidプロジェクトの重要なポイント
- 機能(
:feature:home、:feature:checkout)とレイヤー(:core:data、:core:ui)でモジュールを構成します。フィーチャーはCoreに依存し、互いには依存しません。 - Gradleバージョンカタログ(
libs.versions.toml)を使用して依存関係バージョンを集中化します。バンドルは関連する依存関係をグループ化し、より整理されたビルドファイルを実現します。 build-logicにコンベンションプラグインを実装して、モジュール間で重複した設定を排除します。1つのプラグイン変更ですべての利用モジュールが更新されます。- 一方向の依存関係を強制します:AppはFeatureに依存し、FeatureはCoreに依存します。Dependency Guardのようなツールで強制を自動化できます。
- モジュール化の前後でビルドパフォーマンスを測定します。クリーンビルドだけでなく、インクリメンタルビルド時間を追跡して改善を検証します。
- モジュール境界を安定させます。頻繁な再構成はモジュール化の利点を無効にします。
Android のバグを見つけられますか
実際のコード、隠れたバグ、1日1回。アカウントなしで試せます。

執筆
Anthony Fillion-MailletSharpSkill 創業者
10 年以上フルスタック開発に携わっています。SharpSkill を運営し、ここで公開される内容に責任を負っています。
2026年9月17日 更新
タグ
共有
関連記事

Jetpack Navigation Compose 2026年版:型安全ナビゲーションと面接対策
Navigation Compose 2.10の型安全APIを詳細解説。Kotlin Serializationによるルート定義、ネストグラフ、ディープリンク、面接頻出質問まで網羅。

Android WorkManager 2026年完全ガイド:バックグラウンドタスク、制約条件、面接対策
Android WorkManagerを使ったバックグラウンド処理の実装方法を詳しく解説。制約条件の設定、定期実行、チェーン処理、2026年の技術面接でよく問われる質問まで網羅的にカバー。

Kotlin Flow vs StateFlow vs SharedFlow:2026年のAndroid面接質問
2026年にAndroidの面接官が問う Kotlin Flow・StateFlow・SharedFlow の質問を、明快な回答・比較表・本番投入できるコードとともに解説します。