クックパッド Android アプリのマルチモジュール化とデモアプリの活用
by
こやまカニ大好き
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
Android アプリの マルチモジュール化と デモアプリの活用 こやまカニ大好き
Slide 2
Slide 2 text
自己紹介 こやまカニ大好き クックパッド モバイル基盤部 所属 モバイルアプリ開発の生産性を向上させ るタスクに従事 最近は WebView 大好き
Slide 3
Slide 3 text
今日のトピック ● マルチモジュール化の取り組み ● マルチモジュールプロジェクトの課題 ● デモアプリの紹介
Slide 4
Slide 4 text
クックパッドの マルチモジュール化の取り組み
Slide 5
Slide 5 text
クックパッドアプリの規模感 ● アプリ全体で 200 画面くらい(ダイアログも含む) ● Kotlin 2700 ファイル、 Java 600 ファイルくらい
Slide 6
Slide 6 text
マルチモジュール化の歴史 クックパッドアプリでは 2018 年からマルチモジュール化に取り組んでいる 2018年のイベントで初期のマルチモジュール化について発表 【開催レポ】Cookpad.apk #1 〜筋肉はすべてを解決する〜 https://techlife.cookpad.com/entry/2018/11/01/090059
Slide 7
Slide 7 text
2018 年時点での構想 左側が 2018 年時点での現状 :app と :legacy が分離しただけの状態 そこから :legacy をどんどん分解して右 側のようにモジュールを増やしていく想定 :legacy :app :legacy :app :featureA :featureB :ui :lib
Slide 8
Slide 8 text
マルチモジュール化の現状
Slide 9
Slide 9 text
マルチモジュール化の現状 ● 共通ビジネスロジックはモジュール化完了 ● 画面実装モジュールも16個存在している ● legacy モジュールは引き続き存在している ○ 1,2 年で全部無くせると思っていたが、作業が大変なものが残ってしまっている ● legacy モジュールの解体作業を進めるよりも 新しい機能を別モジュールで作りやすくするという部分に注力
Slide 10
Slide 10 text
● multi_module.md というファイルにモジュール構成や思想がまとめている ○ モジュールを増やしたり分割する作業で迷う事が減る ○ 「未定」とか「特殊」とか書かれていると課題感がわかるので良い ● モジュールの分割ポリシーはプロジェクト毎に異なるので、各プロジェクトの方針 をドキュメントに残すと良い マルチモジュールドキュメントのすすめ
Slide 11
Slide 11 text
マルチモジュール化の方針 以下の3種類に大まかに分類する ● アプリケーションモジュール ● 画面実装(VIPERシーン) モジュール ● ビジネスロジックモジュール
Slide 12
Slide 12 text
アプリケーションモジュール ビルド設定ごとに :app プレフィックスをつけたモジュールを定義する ● リリース用ビルド、開発用ビルドなどそれぞれモジュールで区別する ● APIサーバーの接続先設定などは各 :app モジュールが参照するモジュールを 切り替えることによって実現する(後述)
Slide 13
Slide 13 text
画面実装(VIPERシーン) モジュール 画面実装(VIPERシーン)は :feature プレフィックスをつけたモジュールで定義 ● :feature:tsukurepo, :feature:bookmark のような命名になる ● 他の :feature 系モジュールや :legacy への依存は許容しない ● 複数の画面(VIPERシーン)がモジュール内に含まれても良い ● Dynamic Feature モジュール ではない
Slide 14
Slide 14 text
ビジネスロジックモジュール ビジネスロジックは :library プレフィックスをつけたモジュールで定義 ● :library:infra, :library:ui のような命名になる ● 他のモジュールとやりとりする部分は interface / data class で定義 ● 実装クラスはできるだけ internal class で実装する
Slide 15
Slide 15 text
特殊なモジュール ● :setting:base ○ サーバの接続先設定などの interface だけが入ったモジュール ● :setting:internal ● :setting:external ○ :setting:base の開発版・本番実装 ○ :app モジュールでどちらかを選んで依存を追加する ● :feature:debug_menu ○ デバッグツール等の実装を押し込めたモジュール ○ 開発版アプリの :app モジュールだけが依存している
Slide 16
Slide 16 text
モジュール依存ツリー リリース版 開発版 :app:release :app:internal :feature: debug_menu :settings:external :settings:internal :settings:base :library modules :feature modules Hyperion, Flipper などの デバッグツールはここ 開発用サーバの設定 本番サーバの設定
Slide 17
Slide 17 text
マルチモジュール プロジェクトの課題
Slide 18
Slide 18 text
マルチモジュールプロジェクトの課題 ● :feature モジュールの差分を確認するのが面倒 ● 画面遷移の解決が難しい ● リソース名の重複で上書きされるのが辛い ● AndroidManifest の記述が分散する ● gradle の共通設定をどこに書けば良いのか問題 ● などなど
Slide 19
Slide 19 text
マルチモジュールプロジェクトの課題 ● :feature モジュールの差分を確認するのが面倒 ● 画面遷移の解決が難しい ● リソース名の重複で上書きされるのが辛い ● AndroidManifest の記述が分散する ● gradle の共通設定をどこに書けば良いのか問題 ● などなど
Slide 20
Slide 20 text
:feature モジュールの差分を 確認するのが面倒 ● 動作確認のためにアプリ全体をビルドする必要がある ○ 巨大な legacy モジュールが存在するクックパッドアプリでは特に辛い ● 画面確認のための確認手順が複雑な場合がある ○ ユーザーのログイン状態 ○ アプリの利用状態 ○ APIのレスポンス ○ などなど
Slide 21
Slide 21 text
デモアプリの活用
Slide 22
Slide 22 text
● 特定の :feature モジュールだけに依存する最小規模のアプリ ● :demo プレフィックスを持つアプリモジュールとして実装 デモアプリの概要
Slide 23
Slide 23 text
デモアプリの概要
Slide 24
Slide 24 text
デモアプリの様子 本来は「ログイン」 「レシピ選択」してからでな いと遷移できない
Slide 25
Slide 25 text
:feature:tsukurepo に同一の変更を加えて差分ビルド時間を計測した 開発版クックパッドアプリのビルド : 40秒 つくれぽ機能デモアプリのビルド : 12秒 デモアプリの効果
Slide 26
Slide 26 text
● :feature モジュールの各画面に遷移するためのUI ● APIレスポンスなどの Stub 実装 ● Stub 実装を Inject するための仕組み デモアプリの構造
Slide 27
Slide 27 text
RecyclerView にタイトルを列挙しているだけ 必要なパラメータを渡して画面遷移させる デモアプリのUI
Slide 28
Slide 28 text
APIレスポンスやユーザー状態を表現する Stub 実装 JSONファイルを読み取ってパースしたり、固定の object を返却したり デモアプリの Stub 実装
Slide 29
Slide 29 text
Stub 実装のクラスが本番クラスの代わりに Inject されるような仕組みが必要 (Dagger Hilt を使っている前提) デモアプリの Stub Injection
Slide 30
Slide 30 text
● ライブラリ側の Dagger Module では @InstallIn しない ● アプリ側で Dagger Module を include し、まとめて @InstallIn する ● デモアプリでは必要なコンポーネントを全て Stub する デモアプリの Stub Injection(旧)
Slide 31
Slide 31 text
デモアプリの Stub Injection(旧) :library :library :library :app 後から依存を差し替えるため、ここでは @InstallIn しない app モジュールで各ライブラリの Module を include
Slide 32
Slide 32 text
デモアプリの Stub Injection(旧) ライブラリ側の Dagger Module を include すると必要なコンポーネントだ け差し替えるということができないので、 feature モジュールに必要なコンポーネントの Stub を全て bind する :demo
Slide 33
Slide 33 text
デモアプリの Stub Injection(旧) の課題 ● デモアプリの Dagger Module 定義が肥大化しすぎる ● ライブラリモジュールを作る際に普通に @InstallIn してしまいがち ● 本番実装が internal class なのでデモアプリから参照できない ● 一部だけ本番実装を使うといったことができない ○ SharedPreferences に読み書きするだけのクラスはそのまま使いたい
Slide 34
Slide 34 text
デモアプリの Stub Injection(新) クックパッドアプリでは Fake Inject Layer と呼んでいる 以下のような機能を満たす新しい Stub Injection の仕組み ● Dagger Hilt の機能で実現する ● Stub が必要なコンポーネントだけを Stub と差し替えられる ● Stub と差し替えなかったコンポーネントは本番実装を利用する
Slide 35
Slide 35 text
デモアプリの Fake Inject Layer ● @DebugOverride という Qualifier を用意する ● Stub したいインターフェイスについて、 @DebugOverride @BindsOptionalOf の定義を追加する ○ これにより、@DebugOverride Optional の依存を Inject してくれるようになる
Slide 36
Slide 36 text
デモアプリの Fake Inject Layer ● 元の実装を bind していた箇所を以下のように変更する ○ @DebugOverride でインスタンスが提供されている場合はそれを使い、 提供されていない場合は本物の実装を利用する
Slide 37
Slide 37 text
デモアプリの Fake Inject Layer ● :demo アプリで差し替えたいインスタンスを @DebugOverride で提供する ○ ここでは Optional として定義する必要は無い
Slide 38
Slide 38 text
デモアプリの Fake Inject Layer :demo :feature :app :library Stub(@DebugOverride) があれば Stub を使う Stub がなければ本番実装を使う Fake Inject Layer を提供 :app では何もしない Stub が必要なコンポーネントだけ @DebugOverride で上書きできる 記述量がかなり少なくて済む
Slide 39
Slide 39 text
● @BindsOptionalOf の記述が必要になるため、ライブラリモジュールの Dagger 実装が増えた ○ 仕組みを理解していないと謎のおまじないにしか見えない ○ デモモジュールの実装量はかなり減った ● Fake Inject layer の処理が本番コードに入ってしまっている ○ これはかなり良くないので将来的には別の方法で解決したい ○ デモアプリの利便性を考えて現在のクックパッドアプリではこのようにしている Fake Inject layer の難点
Slide 40
Slide 40 text
まとめ ● マルチモジュールプロジェクトの動作確認にはデモアプリが便利 ○ 見た目の調整だけならかなりの部分はデモアプリで代用可能 ● デモアプリの依存解決は難しい ● デモアプリの依存解決は難しい…
Slide 41
Slide 41 text
モバイルエンジニア募集中 https://cookpad.com/ct/205307 未承諾広告※
Slide 42
Slide 42 text
おわり