Slide 1

Slide 1 text

 たすけて! ViewModel mhidaka / Masahiro Hidaka Help me!

Slide 2

Slide 2 text

たすけて!ViewModelの概要  Composeを使いたい、最新のアプリ設計を学びたい、チームで開発に 集中したい、そんな人のためのアーキテクチャ解説セッションです。モダ ンなUIフレームワークComposeとViewModelを組み合わせた Compose時代に最適な設計ノウハウをお伝えします。 2

Slide 3

Slide 3 text

ViewModelの責務と アプリ設計の基本 ViewModel responsibilities and app design basics ● アプリ設計のエッセンス 3

Slide 4

Slide 4 text

4 ソフトウェア設計の役割

Slide 5

Slide 5 text

5 ソフトウェア設計の役割

Slide 6

Slide 6 text

アプリ設計の基本 ● 今ならCompose-awareなアプリ設計がおすすめ ○ 宣言的UI、リアクティブなデータフロー ● ViewModelの責務とは ○ 広義にはモデルをUIの形に変換すること ○ 狭義にはAndroidのViewModelクラスの役割 ○ ただし、これ自体が重要ではなく設計を表現する容れ物 使いこなすためにAndroidの専門知識が必要という現状がある ● ViewModelだけでなく全体の設計方針が必要

Slide 7

Slide 7 text

ViewModelと UI Viewの責務を 分割する基準 Criteria for dividing ViewModel and UI View responsibilities ● 単方向データフローの原則 ● 制御の反転 ● UIとの境界を規定する 7

Slide 8

Slide 8 text

単方向データフローの原則 ● 下方に状態が流れ、上方にイベントが流れる設計 パターン ● 状態やイベントの流れる方向は変わらず双方向と しないことが特徴 ● Compose UIとViewModelを繋ぐルール 8 State UI State Event

Slide 9

Slide 9 text

単方向データフローの原則 9 ViewModel Compose UI UI State UI Event ● 下方に状態が流れ、上方にイベントが流れる設計 パターン ● 状態やイベントの流れる方向は変わらず双方向と しないことが特徴 ● Compose UIとViewModelを繋ぐルール

Slide 10

Slide 10 text

ViewModelとUIにコンテキストの境界を見つけて適切な責務の分割を行うこと。 よくある誤り:ViewModel のバケツリレー 10

Slide 11

Slide 11 text

ViewModelとUIにコンテキストの境界を見つけて適切な責務の分割を行うこと。 よくある誤り:ViewModel のバケツリレー 11

Slide 12

Slide 12 text

Composableでは制御を反転する 子ノードはシンプルかつ最小限の責務で抑えたい。このようなときは制御を反転し子ノー ドのイベントを(渡す価値があるものは)親ノードへ渡してください。 12

Slide 13

Slide 13 text

Compose UIとViewModelを上手に分離する ViewModelへのアクセスは必要最小限にし たいが、どこでViewModelを保持すべきか。 compose-samplesよりJetNewsアプリの 実装をみてみましょう 13 https://github.com/android/compose-samples より引用

Slide 14

Slide 14 text

Compose UIとViewModelを上手に分離する HomeRouteのコンストラクタでは ViewModelからUI状態を取り出して子ノード に伝えています。あわせてUI イベントと ViewModelのメソッドをマッピングして単方向 データフローの原則を守っています。 14 https://github.com/android/compose-samples より引用

Slide 15

Slide 15 text

Compose UIとViewModelを上手に分離する(2) ナビゲーションがない場合でもViewModelと UIの境界は規定できます。右のリストは Compose-samplesよりCraneアプリの実 装です。 なお、このような引数ではHilt等を使ってDIす るケースもあります。 15 https://github.com/android/compose-samples より引用

Slide 16

Slide 16 text

ViewModelのバケツリレーを辞めるタイミング ● Screenレベルのコンポーザブルで保存 ● ナビゲーションコンポーザブルでの保存 (Screenレベルよりも親ノード側) ● Activity やFragment といったデータホルダーでの保存 (Compose UI の導入が本格的でないときは)

Slide 17

Slide 17 text

ViewModelとUIの 初期化を理解する Understanding ViewModel and UI initialization ● 初期化ブロック ● 明示的な初期化 ● リアクティブな初期化 17

Slide 18

Slide 18 text

ViewModel でUI 変数やその他のインスタンスを初期化する にあたって、どのような手法が一般的でしょうか。 ViewModel でプロパティや内部シーケンスを初期化したいとき、 UI に必要なデータを取得したいとき、どのように記述すると適切で しょうか。もっとも手っ取り早く思いつくのは init ブロックの利用で す。Kotlin の言語仕様にならうとinit という初期化ブロックは適切 に思えます。それでは何を持って適切だと保証すればいいでしょう か。 18

Slide 19

Slide 19 text

オーソドックスなinitブロックをつかった初期化 19 https://github.com/android/compose-samples より

Slide 20

Slide 20 text

明示的な初期化 20 https://github.com/android/compose-samples より

Slide 21

Slide 21 text

明示的な初期化 21 https://github.com/android/compose-samples より

Slide 22

Slide 22 text

初期化をリアクティブに行う 22 https://github.com/android/compose-samples より

Slide 23

Slide 23 text

初期化をリアクティブに行う 23

Slide 24

Slide 24 text

初期化の戦略 UI表示に必要なデータを作りたい Flowを使ったデータストリームで実行 UsecaseやRepository、DataStore、 Usecase等の外部コンポーネントの生成 ViewModel内部で生成せず、DIするか引数として処理 ViewModelで使うだけのテスト不要な外部コ ンポーネントの生成 init初期化ブロックで実行。ただし、真にテスト不要かは、独 立性が高いかは要確認 ViewModelクラス内部で必要な初期化 init初期化ブロックが適しているのは ViewModel に与えられ る引数に応じて状態が変わる、クラスの複数プロパティに影 響があるなど 初期化で非同期処理(コルーチン)が存在し ている・非同期処理内でオブジェクトを生成し たい 非同期処理を使うとViewModelが完全に初期化される前に アクセスされる可能性も。初期化途中のエラーはタイミング に依存し発見しにくく判断も困難

Slide 25

Slide 25 text

初期化戦略を評価する 初期化戦略を評価するためには前述の方針に加えて「ライフサイクル(生存期間) 管理」「テスタビリティ」「処理の結合度」などの判断軸を活用 既存ViewModelがテストできていない等の課題に優先対応したいなら、 ViewModelの構成技術に注目してください。 DIを使って外部との結合度を下げ、 Flowでイベントデータの取得タイミングを細かく 制御できるようになれば、単体テストの実施見通しをある程度立てられます。 ViewModel が数多くの処理を抱え、巨大になっている場合には「処理の結合度」 を下げるため、複雑性を移転しましょう。 25

Slide 26

Slide 26 text

Compose時代の UI Stateとロジック UI State and logic in the Compose era ● イケてるUI State ● Composeフレンドリーな利用 26

Slide 27

Slide 27 text

イケてるUI Stateの作り方 27

Slide 28

Slide 28 text

イケてるUI Stateの作り方 28

Slide 29

Slide 29 text

Composeに適したUI State 29 https://github.com/android/compose-samples より

Slide 30

Slide 30 text

Composeに適したUI State 30 https://github.com/android/compose-samples より

Slide 31

Slide 31 text

Composeに適したUI State 31 https://github.com/android/compose-samples より

Slide 32

Slide 32 text

UI Stateをうまく使うコツ 32 ● ViewModelはビジネスロジックを隠蔽 し、UI表示と変更を制御する ● UIを表示するための情報をUIState にカ プセル化し、UIStateをCompose UIに とっての信頼できる唯一の情報源 として 扱う

Slide 33

Slide 33 text

ViewModelにおける エラーハンドリングとビ ジネスロジック Error handling and business logic in ViewModel ● 多様なエラー ● 複数のエラーを扱う ● エラーをUI Stateとして扱う ● UIロジックとエラー 33

Slide 34

Slide 34 text

さまざまなエラーを扱う ViewModel におけるエラーハンドリングは設計上、迷いやすいポイント Google公式ガイドラインのAndroid Developers でも記述が少ないため聞かれること が多い話題でもある。 34

Slide 35

Slide 35 text

複数のエラーを扱う 35 https://github.com/android/compose-samples より

Slide 36

Slide 36 text

複数のエラーを扱う 36 https://github.com/android/compose-samples より

Slide 37

Slide 37 text

エラーをUI Stateとして扱う 37 https://github.com/android/compose-samples より

Slide 38

Slide 38 text

エラーをUI Stateとして扱う 38 https://github.com/android/compose-samples より

Slide 39

Slide 39 text

エラーをUI Stateとして扱う 39 https://github.com/android/compose-samples より

Slide 40

Slide 40 text

ViewModel以外のエラー(UIロジックの場合) 40 https://github.com/android/compose-samples より

Slide 41

Slide 41 text

ViewModel以外のエラー(UIロジックの場合) 41 https://github.com/android/compose-samples より

Slide 42

Slide 42 text

ViewModel以外のエラー(UIロジックの場合) 42 https://github.com/android/compose-samples より

Slide 43

Slide 43 text

エラーを分類し、仕様にモデリングする エラーの種類 表示仕様 UIに関係ない続行可能なエラー ViewModelで解決しUIに反映しない 再実行が容易なエラー SnackbarやToastで表示し、認知を得る 実行が困難なエラー 画面内コンテンツが部分的に利用不可などの UI 要 素を反映し、利用を抑止する リカバリが困難な重要エラー ダイアログ等でユーザー操作を中断し、アクションを 選択させる

Slide 44

Slide 44 text

ViewModelの テスト戦略 ViewModel testing strategy ● シンプルなテスト ● テストしやすい構造 44

Slide 45

Slide 45 text

シンプルなテストを目指す 45 https://github.com/android/nowinandroid より

Slide 46

Slide 46 text

テストしやすい構造とは 次のルールに従って構築された ViewModelは、そうでないものに比べて格段にテストしやすくなりま す。 • ViewModelはUIStateを通じてUI画面を構築する • UIイベントはViewModelのビジネスロジックを経由して UIStateに結果を反映する                   (リポジトリ等ドメインを更新する場合もある) • ViewModelのライフサイクルとロジックを分離し、ライフサイクルへの依存を低減する (オブジェクトの作成と処理を分離する) • ViewModelで使うリソースはDIで供給する (依存性注入を使って外部との結合度を下げる) 46

Slide 47

Slide 47 text

テスト戦略 ViewModel は、ビジネスロジックをUI 画面に投影する役割のために外部と 密結合しやすい性質をもちます。 単方向データフローの原則や信頼すべき唯一の情報源といった設計思想を ViewModelの入出力インターフェイスに反映することがテスタビリティの向上 に繋がります。 さらにこの考え方は、ViewModelだけを対象とせず、Compose UIにも適 用できます。たとえばDI可能な構造にしておけばFakeとの相性がよくなり、こ のような構造はPreviewやスクリーンショットテスティングにいい影響を与えま す。アプリ全体の効率化に繋がります

Slide 48

Slide 48 text

おわりに ViewModelの責務と アプリ設計の未来 ViewModel以外の解決策 48

Slide 49

Slide 49 text

Android ViewModelは必ずしも必 要ではなく、実は扱いが難しい。 Composableネイティブな設計手法(プレ ゼンター&UDF)が生まれている 49 https://slackhq.github.io/circuit/ https://github.com/DroidKaigi/conference-app-2024/

Slide 50

Slide 50 text

ViewModelを道具として扱おう ● ViewModelはAndroidアプリという特定環境に適合 ● 使い方をよく知り、アプリを構成する技術スタックに合わせて利用 ● 設計の原則は変化しない ○ Composableネイティブな設計でViewModelを使わなくても、考え方は同じ 得た知識や試行錯誤を、アプリの開発体験に活かしてください

Slide 51

Slide 51 text

   Thank you for watching ! mhidaka / Masahiro Hidaka [email protected]  たすけて! ViewModel Help me!