0Pricing
Android Academy · レッスン

FeatureモジュールとCoreモジュール

モジュール境界を設計します。

「FeatureモジュールとCoreモジュール」はCoddyKit上の無料Android Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAndroid Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Android Academyコースには全4レッスンが含まれています。

2種類のモジュール

モジュール化されたAndroidアプリの多くは、コードを主にfeatureモジュールとcoreモジュールの2種類に整理し、その上に薄い:appモジュールを置きます。

  • Featureモジュールには、アプリのユーザー向けの一部分(画面やフロー)を配置します。
  • Coreモジュールには、多くのfeatureで使う共有インフラを配置します。

このレッスンでは、これらの境界を適切に定義する方法を学びます。

Featureモジュールの構成

:feature:profileのようなfeatureモジュールには、1つのfeatureに必要なすべてのもの、つまりComposeの画面、ViewModel、UI状態が含まれます。UIからview-modelまでの垂直方向の一連の機能を所有するため、verticalな構成です。

共有部品のためにcoreモジュールへ依存しますが、他のfeatureモジュールには依存しないようにします。

// feature/profile/ProfileScreen.kt
@Composable
fun ProfileScreen(viewModel: ProfileViewModel = hiltViewModel()) {
    val state by viewModel.uiState.collectAsStateWithLifecycle()
    when (state) {
        is ProfileUiState.Loading -> CircularProgressIndicator()
        is ProfileUiState.Success -> ProfileContent((state as ProfileUiState.Success).user)
        is ProfileUiState.Error -> ErrorMessage()
    }
}

FeatureのViewModel

各featureは独自のViewModelを所有します。coreモジュールのリポジトリ経由でデータを取得し、UI状態を公開します。featureモジュールが知るのはデータの取得方法ではなく、リポジトリの契約だけです。

// feature/profile/ProfileViewModel.kt
@HiltViewModel
class ProfileViewModel @Inject constructor(
    private val userRepository: UserRepository // from :core:data
) : ViewModel() {
    val uiState: StateFlow<ProfileUiState> =
        userRepository.observeUser()
            .map { ProfileUiState.Success(it) }
            .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), ProfileUiState.Loading)
}

Coreモジュールの構成

coreモジュールはhorizontalな構成で、複数のfeatureで使われる1つの機能を提供します。一般的なcoreモジュールには次のようなものがあります。

  • :core:model — どこからでも共有する単純なデータクラス
  • :core:network — Retrofit/Ktorクライアント
  • :core:database — Roomの設定
  • :core:data — ネットワークとデータベースを組み合わせるリポジトリ
  • :core:designsystem — テーマと再利用可能なcomposable
// core/model/User.kt
data class User(
    val id: String,
    val name: String,
    val avatarUrl: String
)

デザインシステムモジュール

:core:designsystemは、最も再利用されるモジュールの1つです。MaterialTheme、カラースキーム、タイポグラフィ、ボタンやカードなどの再利用可能なcomposableを保持します。すべてのfeatureでコードをコピーすることなく、同じ見た目を適用できます。

// core/designsystem/AppTheme.kt
@Composable
fun AppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colors = if (darkTheme) DarkColors else LightColors
    MaterialTheme(
        colorScheme = colors,
        typography = AppTypography,
        content = content
    )
}

データモジュールがリポジトリを所有する

:core:dataモジュールは、featureが依存するリポジトリインターフェースを公開し、実装は隠蔽します。通常は:core:networkと:core:databaseに依存し、それらを組み合わせて単一の信頼できる情報源を提供します。

// core/data/UserRepository.kt
interface UserRepository {
    fun observeUser(): Flow<User>
    suspend fun refresh()
}

// core/data/OfflineFirstUserRepository.kt
internal class OfflineFirstUserRepository @Inject constructor(
    private val api: UserApi,        // :core:network
    private val dao: UserDao         // :core:database
) : UserRepository {
    override fun observeUser(): Flow<User> = dao.observe().map { it.toUser() }
    override suspend fun refresh() { dao.upsert(api.fetch().toEntity()) }
}

model と designsystem を軽量に保つ

最下層のコアモジュールは、できるだけ少ない依存関係だけを持つようにします。:core:model は理想的にはAndroidへの依存関係を一切持たず、通常のKotlinデータクラスだけで構成します。これにより、どこでも利用でき、ビルドも高速になります。

もし:core:model が Retrofit や Room に依存し始めると、データクラスを利用するすべてのモジュールに、それらの重いライブラリまで取り込まれてしまいます。

// core/model/build.gradle.kts
plugins {
    id("myapp.jvm.library") // pure Kotlin, no Android
}
// No Retrofit, no Room, no Compose here — just data classes

薄い :app モジュール

:app モジュールは組み立て役です。ここには、Application クラス、単一のMainActivity、トップレベルのNavHost、依存性注入の配線だけを置き、ロジックはほとんど含めないようにします。実際の画面はすべて機能モジュールに配置します。

appモジュールを薄くすると、変更の大半が機能モジュール内で発生するため、appモジュールが再ビルドされることはほとんどありません。

// app/MainActivity.kt
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            AppTheme {            // from :core:designsystem
                AppNavHost()      // routes into :feature:* screens
            }
        }
    }
}

適切な境界を設ける

何をモジュールにするかは、どのように決めればよいのでしょうか。実用的な目安をいくつか紹介します。

  • 機能 = ユーザーが名前を挙げられる画面またはフロー(ホーム、プロフィール、購入手続きなど)です。
  • コアモジュール = 2つ以上の機能で再利用される能力です。
  • 2つの機能が同じコードを必要とする場合は、そのコードをコアモジュールに下ろします。
  • 1つのモジュールが無関係な処理を多く担っている場合は、分割します。

オプション: api と impl の分割

大規模なアプリでは、機能を公開用の:feature:profile:api(インターフェース、ナビゲーションルート)と、非公開の:feature:profile:impl(画面、ViewModel)に分割することがあります。他の機能は小さなapiにのみ依存し、実装には依存しません。

これは高度な設計です。ほとんどのアプリでは、機能ごとに1つのモジュールがあれば十分です。このパターンが非常に大規模なコードベースで使われることだけ知っておいてください。

// Other features see only the contract, not the screens
// feature/home depends on :feature:profile:api
interface ProfileEntry {
    val route: String
    fun NavGraphBuilder.register(navController: NavController)
}

グラフを組み立てる

こちらが、今回の例におけるレイヤーの接続方法です。依存関係が常に下向きにのみ向いていることに注目してください。app -> feature -> data -> network/database -> model という構成です。

// :app           -> :feature:home, :feature:profile
// :feature:home  -> :core:data, :core:designsystem
// :feature:profile -> :core:data, :core:designsystem
// :core:data     -> :core:network, :core:database, :core:model
// :core:network  -> :core:model
// :core:database -> :core:model
// :core:model    -> (nothing)

理解度チェック

:feature:home と :feature:profile の両方で、ユーザーデータの取得とキャッシュが必要です。このリポジトリのコードは、どこに配置すべきでしょうか。

まとめ: 機能モジュールとコアモジュール

コードを2つのレイヤーに分割する方法を学びました。

  • 機能モジュールは、画面、ViewModel、UI状態を含む垂直的なスライスです。
  • コアモジュールは、model、network、database、data、designsystemなどの水平的な機能です。
  • :app モジュールは薄く保ち、機能を組み立てるだけにします。
  • 共有コードはコアへ下ろし、:core:model はAndroidに依存しない軽量な状態を保ちます。

次は、これらのモジュール間の依存関係を管理し、グラフをきれいに保つ方法を学びます。

よくある質問

「FeatureモジュールとCoreモジュール」レッスンは無料ですか?

はい。「FeatureモジュールとCoreモジュール」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Android Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Android Academyコースには全4レッスンが含まれています。

「FeatureモジュールとCoreモジュール」で何を学びますか?

モジュール境界を設計します。 ブラウザで直接実行するハンズオンコードでAndroid Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Android Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAndroid Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「FeatureモジュールとCoreモジュール」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAndroid Academyレッスンでコードを書いて実行できますか?

はい。すべてのAndroid Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. モジュール化する理由
  2. FeatureモジュールとCoreモジュール
  3. モジュール依存関係の管理
  4. モジュール間のナビゲーション
← Android Academyに戻る