最近AndroidDevelopersのAndroidアプリの設計ドキュメントが頻繁に更新されています。新しくオススメの設計方法が[強く推奨]、[推奨]、[大規模なアプリで推奨]に分類されているので、一つずつ確認します。(2022年11月時点のドキュメントであり将来変更があると思います)

https://developer.android.com/topic/architecture/recommendations

階層型アーキテクチャ

  • [強く推奨] データレイヤは、アプリデータをアプリの他の部分に公開し、アプリのビジネス ロジックの大部分を含みます。

データレイヤは、アプリデータをアプリの他の部分に公開し、アプリのビジネス ロジックの大部分を含みます。

  • リポジトリは、データソースが 1 つだけの場合でも作成する必要があります。
  • 小規模なアプリでは、data パッケージやモジュール内にデータレイヤ タイプを配置できます。
  • [強く推奨]明確に定義された UI レイヤを使用します。

UI レイヤは、アプリケーション データを画面に表示するもので、ユーザー インタラクションの主要なポイントとして機能します。

  • 小規模なアプリでは、ui パッケージやモジュール内にデータレイヤ タイプを配置できます。

詳しくは、こちらの UI レイヤに関するベスト プラクティスをご覧ください

  • [強く推奨]データレイヤでは、リポジトリを使用してアプリケーション データを公開する必要があります。

コンポーザブル、アクティビティ、ViewModel などの UI レイヤのコンポーネントを、データソースと直接やり取りさせないようにします。データソースの例:

  • データベース、DataStore、SharedPreferences、Firebase API
  • GPS 位置情報プロバイダ。
  • Bluetooth データ プロバイダ。
  • ネットワーク接続ステータス プロバイダ。

コルーチンとフローを使用してレイヤ間の通信を行います。

その他のコルーチンに関するベスト プラクティスをご覧ください。

複数の ViewModel 間でデータレイヤとやり取りするビジネス ロジックを再利用する必要がある場合、または特定の ViewModel のビジネス ロジックの複雑さを簡素化したい場合に、ドメインレイヤを使用します。

UIレイヤ

単方向データフロー(UDF)の原則に従い、ViewModel はオブザーバー パターンを使用して UI の状態を公開し、メソッド呼び出しを介して UI からアクションを受け取ります。

  • [強く推奨]メリットをアプリに適用できる場合は、AAC ViewModel を使用します。

AAC ViewModel を使用してビジネス ロジックを処理し、アプリデータを取得して UI の状態を UI(Compose または Android ビュー)に公開します。

詳しくは、ViewModel のベスト プラクティスをご覧ください。

ViewModel のメリットについては、こちらをご覧ください。

  • [強く推奨]ライフサイクル対応 UI 状態コレクションを使用します。

適切なライフサイクル対応コルーチン ビルダー(UI では repeatOnLifecycle、Jetpack Compose では collectAsStateWithLifecycle)を使用して、UI から UI の状態を収集します。

詳しくは、repeatOnLifecycle をご覧ください。

詳しくは、collectAsStateWithLifecycle をご覧ください。

  • [強く推奨]ViewModel から UI にイベントを送信しないようにします。

ViewModel でイベントをすぐに処理し、イベント処理の結果で状態を更新します。UI イベントについて詳しくは、こちらをご覧ください。

  • [推奨]単一アクティビティのアプリケーションを使用します

アプリに複数の画面がある場合、Navigation フラグメントまたは Navigation Compose を使用して画面間を移動し、アプリへのディープリンクを設定します。

Jetpack Compose を使用して、スマートフォン、タブレット、折りたたみ式デバイス、Wear OS 向けの新しいアプリを作成します。

ViewModel

  • [強く推奨] ViewModel が、Android のライフサイクルに依存しないようにします。

ViewModel が、ライフサイクルに関連する型への参照を保持しないようにします。Activity, Fragment, Context または Resources を依存関係として渡さないようにします。ViewModel で Context を必要とする場合は、それが適切なレイヤにあるかどうかを強く評価する必要があります。

ViewModel は、以下を使用してデータレイヤまたはドメインレイヤとやり取りします。

  • アプリケーション データを受信するための Kotlin Flow。
  • viewModelScope を使用してアクションを実行するための suspend 関数。
  • [強く推奨]画面レベルで ViewModel を使用します。

再利用可能な UI で ViewModel を使用しないようにします。ViewModel は、以下で使用します。

  • 画面レベルのコンポーザブル。
  • View のアクティビティ / フラグメント。
  • Jetpack Navigation を使用する場合のデスティネーションまたはグラフ。

再利用可能な UI コンポーネントの複雑さに対処するには、プレーンな状態ホルダークラスを使用します。これにより、状態を外部でホイスティングして制御できるようになります。

AndroidViewModel ではなく ViewModel クラスを使用します。Application クラスは ViewModel で使用しないようにします。代わりに、依存関係を UI またはデータレイヤに移行します。

  • [推奨]UI 状態を公開します。

ViewModel が、uiState という単一のプロパティを介して UI にデータを公開する必要があります。UI に互いに関係のない複数のデータが表示されている場合、VM が複数の UI 状態プロパティを公開する可能性があります。

  • uiState を StateFlow にする必要があります。
  • データが階層の他のレイヤからのデータ ストリームとして来る場合は、stateIn 演算子と WhileSubscribed(5000) ポリシー(例)を使用して uiState を作成する必要があります。
  • データレイヤから来るデータ ストリームがない単純なケースでは、不変の StateFlow として公開される MutableStateFlow を使用できます(例)
  • ${Screen}UiState をデータクラスとして指定できます。データクラスには、データ、エラー、読み込みシグナルを含めることができます。異なる状態が排他的である場合、このクラスがシールクラスになることもあります。

Lifecycle

  • [強く推奨]ライフサイクル メソッドをオーバーライドしないようにします。

アクティビティやフラグメントの onResume などのライフサイクル メソッドをオーバーライドしないようにします。代わりに LifecycleObserver を使用します。ライフサイクルが特定の Lifecycle.State に達したときにアプリが処理を実行する必要がある場合は、repeatOnLifecycle API を使用します。

依存関係を処理する

依存関係挿入のベスト プラクティス、可能であれば特にコンストラクタ挿入のベスト プラクティスを活用してください。

  • [強く推奨]必要に応じてコンポーネントにスコープを設定します。

型が共有する必要のある可変データを含む場合、あるいは、型がアプリ内で広く使用されており、その初期化にコストがかかる場合は、依存関係コンテナにスコープを設定します。

  • [推奨]Hilt を使用します。

単純なアプリでは、Hilt または手動依存関係挿入を使用します。複雑なプロジェクトの場合は Hilt を使用します。たとえば、次の場合:

  • ViewModel を使用する複数の画面 – 統合
  • WorkManager の使用 – 統合
  • ナビゲーション グラフにスコープ設定された ViewModel など、Navigation の高度な使用 – 統合

テスト

プロジェクトが Hello World アプリのように単純なものでない限り、最低でも以下でテストする必要があります。

  • フローを含む ViewModel の単体テスト。
  • データレイヤ エンティティの単体テスト。つまり、リポジトリとデータソースです。
  • CI の回帰テストとして役立つ UI ナビゲーション テスト。
  • [強く推奨]モックよりもフェイクを優先します。

詳しくは、Android ドキュメントでテストダブルを使用するをご覧ください。

  • [強く推奨]StateFlow をテストします。

StateFlow をテストする場合:

モデル

  • [推奨]複雑なアプリではレイヤごとにモデルを作成します。

複雑なアプリでは、必要に応じて、別のレイヤやコンポーネントで新しいモデルを作成します。以下の例を考えてみましょう。

  • リモート データソースは、ネットワーク経由で受け取るモデルを、アプリが必要とするデータのみを含むシンプルなクラスにマッピングできます
  • リポジトリは、UI レイヤが必要とする情報だけで DAO モデルをシンプルなデータクラスにマッピングできます。
  • ViewModel では、UiState クラスにデータレイヤ モデルを含めることができます。

強く推奨はほぼ必須と見て差し支えないと思います。