2026年のAndroidモジュール化:マルチモジュールアーキテクチャと面接対策

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

Android modularization architecture diagram showing multi-module structure

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リポジトリは、エンタープライズアプリにもスケールする実績のあるモジュール分類を確立しています。

settings.gradle.kts - モジュール構成kotlin
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ファイルでこれを解決します。

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" }

各モジュールは型安全なアクセサで依存関係を参照します:

feature/home/build.gradle.ktskotlin
dependencies {
    implementation(libs.bundles.compose)
    implementation(libs.hilt.android)
    ksp(libs.hilt.compiler)
}

Gradleは同期時にこれらのアクセサを生成し、IDEの自動補完とコンパイル時の検証を可能にします。

Androidの面接対策はできていますか?

インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。

コンベンションプラグイン:DRYなビルド設定

コンベンションプラグインなしの25モジュールプロジェクトでは、各build.gradle.ktsで同じcompileSdkminSdkcomposeOptions、プラグイン適用を繰り返すことになります。異なるモジュールが異なる設定を使用してしまうリスクもあります。

コンベンションプラグインは、このロジックをbuild-logicインクルードビルドに集中化します。

build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.ktkotlin
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でプラグインを登録します:

build-logic/convention/build.gradle.ktskotlin
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モジュールは最小限の設定のみで済むようになります:

feature/home/build.gradle.ktskotlin
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つのパターンがクロスフィーチャー通信を処理します。

共有ルートによるナビゲーション

core/navigation/src/main/kotlin/Routes.ktkotlin
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からルートをインポートします。

共有ドメインモデル

core/model/src/main/kotlin/Product.ktkotlin
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がクロスフィーチャーイベントを処理します:

core/common/src/main/kotlin/CartEventBus.ktkotlin
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モジュールに依存します。テストフィクスチャでフェイク実装を作成します:

core/data/src/testFixtures/kotlin/FakeProductRepository.ktkotlin
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-Maillet

執筆

Anthony Fillion-Maillet

SharpSkill 創業者

10 年以上フルスタック開発に携わっています。SharpSkill を運営し、ここで公開される内容に責任を負っています。

2026年9月17日 更新

タグ

#android
#kotlin
#architecture
#gradle
#modularization

共有

関連記事