この記事は フラー Advent Calendar 2019 の7日目の記事です。
個人的なことですが、今年初めてお仕事として新規Androidアプリ開発に携わり、今年の5月にリリースしました。 そして現在も、来年にリリース予定の別のプロダクトを開発しています。
新規でアプリを作るときにまず間違いなく通る道は「どのような設計パターンにするか」だと思いますが、 現在、フラーで新規に開発しているAndroidアプリは 「SingleActivity + MVVM + DI(Dagger) + Repository」 という構成を取ることが多いです。
そもそもなぜ設計パターンを導入したのか
これは16日目に登場予定の "Daiki Okumura" が2018年の夏頃に導入したことが発端ですが、それまでのプロダクトにはこれといった設計パターンがありませんでした。
ここからは内情的な話になりますが、2017年から発足した弊共創事業部の創成期は「来年にはもしかしたらプロダクト毎死んでいるかも分からない」 「一人が短期間で作り上げる」「そもそもAndroidエンジニア足りなすぎた、時間が足りなかった」と聞いています。
そもそもアプリ自体もミニマムなものが多く、導入に至らなかったことは容易に想像できます。その状況から一気に割とモダンな構成になった訳ですが、結論から言うと、非常に開発がやり易くなっていると感じます。
それぞれのメリットと課題感
昔話はこの辺にして、現在弊チームが採用している「SingleActivity + MVVM + DI(Dagger2) + Repository」について1年間使ってみて感じた良い点と 現在抱えている課題感を共有したいと思います。
また、構成についての説明はほぼしないのですが、同じ構成のサンプルアプリが、 Googleが紹介しているarchitecture-components-samples`のGithubBrowserSampleとしてGitHubにあがっているのでそちらをご覧ください。 色々なMVVMのサンプルを読みましたが、このサンプルが簡素すぎても複雑すぎてもなく丁度良いサイズ感で、参考にしています。
横道に逸れますが「GitHubのリポジトリ一覧を表示する」アプリって何万回作られているんでしょうね。もしかしたら一番多いアプリかもしれません。
「SingleActivity + MVVM + DI(Dagger) + Repository」の良かった点と現状の課題
Single Activityについて
「Android Single Activity」でGoogle検索してもなぜかあまり日本語の記事が出てきませんが、SingleActivityとはその名の通り、アプリケーションにActivityを 一つしか持たないようにするものです。Fragment間の画面遷移は Android Architecture Component(以下、AAC)のNavigationに頼っています。
良かった点
- Activityのライフサイクル管理を考えず、Fragmentのみに集中できること
- AAC Navigationをフル活用できること
- SafeArgsを使うことで型安全に画面間の値渡しが可能になる
今まで値を渡されるActivityに
createIntent()
のようなメソッドを書いていたのが不要になった
- SafeArgsを使うことで型安全に画面間の値渡しが可能になる
今まで値を渡されるActivityに
fun createIntent( context: Context, param1: Int, ...
- 画面遷移のアニメーション定義もxml管理できるようになる
課題
- 単一のActivityが唯一全てのFragmentの状態を知り得るため、処理が多くなりがちになる
- 処理が多くなるなら、Singleであることにこだわる理由はなさそうなので、分割もあり?
- Fragmentのバックスタックの理解など、Fragmentへの理解がだいぶ必要になる。というか一度はハマる
- みんな本当にGUIのNavigationEditorで管理してるのだろうか、、、ぐちゃぐちゃになっているので見れたものじゃないが
MVVMについて
MVVMパターンはGoogleも推奨しているパターンの一つですね。もちろん以下のような注釈付きですが。
1 つの方法であらゆるシナリオにそれぞれ応じるアプリを作成することは不可能です。 しかし、ここで紹介する推奨アーキテクチャは、多くの状況やワークフローで利用できる優れた出発点になります。
https://developer.android.com/jetpack/docs/guide?hl=ja
良かった点
- それなりに古くからある考えなので、情報量が多いことがありがたい。参考になるコードの実例も、解説記事も
- Jetpack(AAC ViewModel、LifeCycle、LiveData)をフル活用できる
- 適切な分離が出来れば、Viewをスリムに保つ事が出来て、かつViewModelのUnitTestを書くことができる
課題
- 例えば自分は、MVPパターンの開発経験しかなかったのでMVVM自体の習得と共に、DataBindingやJetpackも同時に把握しなければならず、慣れるまで時間がかかった。 Android設計パターン入門でもMVVMは習得にコストがかかる、と書かれているが、本当にそうだと感じる
DI(Dagger2)について
DIパターンはUnitTestを書く時に便利なので、採用しています。Dagger2を使用しています。
良かった点
- 複雑そうだし、実際最初に書く時は辛いが一度書いてしまえば、後は便利
- UnitTestが容易に書けるようになる。寧ろ無いと本当に辛い
課題
- やはり一番の課題はここも学習コストだと感じる
- MVVMとDaggerのダブルパンチだと相当辛い。(正直、納期のために雰囲気コピペで乗り切ることもあった。今は無い。)
- Daggerを捨てて、Koinに移行?という手もあるが、以下のようなデータを見るとまだまだDaggerで良いのかなという感じ https://github.com/Sloy/android-dependency-injection-performance
Repositoryパターンについて
MVVMパターンでは特に Model
をどのように組むかについて指示は無いので、
サンプルや記事を眺めてみてもプロダクトによってUseCaseを挟んでいたりInterfaceを挟んでいたりいなかったりとバラバラですが、
弊チームでは、APIやローカルDBまでの仲介役としてRepositoryを利用していて、UseCaseは使っていません。
良かった点
- 具体的なデータ取得の方法に依存しないように出来るため、APIが直前まで出来ない問題に対応出来ること
- モックデータを用意しておいて、直前に差し替える、ということを行える
- DIパターンの恩恵に預かって、だがビジネスロジックのUnitTestを書く事ができる
課題
- Repositoryに処理が寄りがちになる
- まだそこまで大きくなっていないが、もっと大きくなったらUseCaseを導入した方が良いなと感じている
全体:今後やりたいこと
- Multi Moduleの導入
- やっと最近上記の構成に慣れ始め、また人も増え始めたところなので、また新しいことを導入するには早い、と思い導入に至っていないが、 次に新規で作る際は入れない理由が無さそう。
後半駆け足になりましたが、今日はこの辺で。
明日はまた私で、「エンジニアリング全般で何か」書きます。