2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Now in Androidの設計で押さえておきたい3点まとめ

2
Posted at

はじめに

Googleが公式に公開しているサンプルアプリNow in Androidは、Jetpack Composeを前提としたモダンAndroidアプリの設計・実装例として知られています。

一方で、コード量やモジュール数も多く、「どこから読めばいいのか分からない」と感じる人も多いのではないでしょうか。

そこでこの記事では、投稿日時点のNow in Androidの実装をベースにAndroidエンジニアが設計観点で一度は目を通しておくとよいポイントを3つに絞ってまとめてみたいと思います。

① UI Stateを型で表現し、単方向データフローを徹底している点

Now in Androidでは、Composeを前提としたUI State設計が非常に丁寧に行われています。
代表的な例が、Topic画面のViewModelです。

実装例(抜粋)

feature/topic/impl/TopicViewModel.kt
sealed interface TopicUiState {
    data object Loading : TopicUiState
    data class Success(
        val followableTopic: FollowableTopic,
        val news: List<UserNewsResource>
    ) : TopicUiState
}

UI が取り得る状態をsealed interfaceで明示し、

  • ローディング中
  • データ取得成功時

といった状態を型で制限しています。
ViewModel側では、Repositoryから取得したFlowをUI Stateに変換しています。

val topicUiState: StateFlow<TopicUiState> =
    combine(
        getFollowableTopic(topicId),
        getTopicNewsResources(topicId)
    ) { followableTopic, news ->
        TopicUiState.Success(
            followableTopic = followableTopic,
            news = news
        )
    }.stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5_000),
        initialValue = TopicUiState.Loading
    )

この構成により、

  • UIはStateを受け取って描画するだけ
  • ロジックはViewModelに閉じる
  • 状態遷移がコード上で追いやすい

といったメリットが得られます。
Composeを使ったプロジェクトでは、「まず UI State をどう定義するか」を考える重要性を再確認できる実装です。

② Repositoryが「データの組み立て役」に徹している点

Now in AndroidのRepository設計は、抽象化をやりすぎず、現実的な責務分離がされているのが特徴です。

実装例(抜粋)

core/data/.../NewsRepository.kt
interface NewsRepository {
    fun getNewsResources(
        filterTopicIds: Set<String> = emptySet()
    ): Flow<List<UserNewsResource>>
}

RepositoryはFlowを返すだけのシンプルなAPIを公開しています。
実装側ではLocal DBやUserDataを組み合わせて、UIが使いやすい形にデータを整形しています。

override fun getNewsResources(
    filterTopicIds: Set<String>
): Flow<List<UserNewsResource>> =
    combine(
        newsResourceDao.getNewsResources(),
        userDataRepository.userData
    ) { newsResources, userData ->
        newsResources.map { news ->
            UserNewsResource(
                newsResource = news,
                isSaved = news.id in userData.bookmarkedNewsIds
            )
        }
    }

ここで注目したいのは、

  • ViewModel側でデータ加工をさせない
  • Repositoryが「取得+組み立て」を担う
  • UseCase層を無理に挟まない

という設計判断です。
理論的なClean Architectureというより、壊れにくさと読みやすさを優先した構成になっており、実務プロジェクトにそのまま応用しやすい形だと感じます。

③ モジュール分割と依存方向が非常に明確な点

Now in Androidは、モジュール分割が進んでいるサンプルとしても有名です。
ただし、目的なく細分化されているわけではありません。

構成を見ると、

  • feature/*:画面・機能単位
  • core/*:データ・共通ロジック
  • app:アプリ全体のエントリーポイント

という、責務ベースの分割になっています。

featureモジュールはRepositoryインターフェースに依存し、NetworkやDBの実装詳細には触れません。

この構造により、

  • feature単位でのテストがしやすい
  • 変更の影響範囲が限定される
  • 将来的な差し替えや拡張がしやすい

といったメリットが得られます。
一方で、「将来のために分けすぎる」設計にはなっておらず、現時点で必要な粒度に留めている点も実践的です。

まとめ

Now in Androidは最新技術を詰め込んだサンプルというよりも、

  • UI Stateを中心とした単方向データフロー
  • 現実的なRepository設計
  • 責務が明確なモジュール分割

といった、長期運用を意識した設計判断が随所に見られるプロジェクトです。

すべてを読み込む必要はありませんが、今回挙げたポイントに絞ってコードを追うだけでも日々の設計を見直すヒントになると思います。

さいごに

これだけの規模と内容のサンプルが無料で公開されているのは、かなり恵まれているなと感じます。
コードを追っていると意外と時間が溶けるので、読書代わりに眺めるのも良いですね。

設計に迷ったタイミングで、一度コードを眺めてみるとヒントが見つかるかもしれません。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?