弊社エンジニアがDroidKaigiで登壇してきました! 〜登壇内容とAndroid版paymoのふか〜いカンケイ

kirimin
kirimin
Feb 15, 2018 · 9 min read

こんにちは。
Androidエンジニアの@kiriminこと渡辺です。

私事になりますが、先日DroidKaigi2018で登壇しました。
「How to improve your MVP architecture and tests」というタイトルで、設計とテストについてお話させて頂きました。

スライドはこちらになります。

さて、この発表内容にはAndroid版paymoの設計の歴史と深い関係がありまして、本日はそのあたりの話を書いてみようかな、と思います。

なお、iOS版の設計については別のエントリーで紹介していますので、ぜひ合わせてご覧ください。

Android版paymoの歴史

Image for post
Image for post

これはスライド内で紹介したAndroid版paymoの現在のクラス設計を表した図です。しかし、最初からこのような設計だったわけではなく、様々な議論やリファクタリングの歴史を経て今の設計に落ち着きました。セッションを聴いた方やスライドを見た方の中には、実際の開発現場でどうやってコンセンサスを取り設計のリファクタリングを進めていったのか、という点に興味がある方もいると思うので、そのあたりの経緯を紹介します。

ペイモのAndroidアプリは様々な事情により、実装開始からリリースまではかなりハードなスケジュールで開発されたと聞いています。
ぼくがペイモに携わり始めたのは2017年の2月、ペイモアプリがリリースされて1ヶ月も経っていない頃で、当時フルタイム勤務のAndroidエンジニアはぼくが1人目で、まだまだリリース直後のカオスな実装や不具合が多く残っている状態でした。
ジョイン直後は機能追加の実装を行いながらDialogFragmentやActivityのリストア起因のクラッシュなどを修正し、クラッシュ率をどんどん下げていきました。

そしてプロジェクトのコードを読み解いていくと、クラス設計は基本的にCleanArchitectureをベースにしていてデータレイヤーなどはなかなかきれいに実装されている一方で、Viewに近いレイヤーに関してはかなりカオスで、まさに発表で紹介したMVPのアンチパターンのド王道を行くような実装だったのです。
誤解のないように注訳しておくと、これは別にpaymoのプロジェクトに限った話ではなく、過去に色々なプロジェクトでこのような上手くいっていないMVPというのは経験していました。

設計リファクタリングの提案

そこで、「今こそ長年温めてきた秘伝のMVPプラクティスを実務に投入する絶好の機会だ!」と考え、チーム内で設計のリファクタリングをしようという提案を行いました。
当時のチームはリモートの協力会社様やフルタイムではない業務委託の方など非同期なチームだったので、提案はSlackで少し会話した後はGitHubのIssueをベースに行いました。

Image for post
Image for post

Issueでは現状の課題感に対する問題提起を行い、まずはそれぞれのメンバーで認識合わせを文章ベースで行いました。

Image for post
Image for post
Image for post
Image for post

その後、ぼくがシンプルな画面を一つ自分の考える正しいMVPベースの設計に書き換え、PRを出しチームのAndroid/iOSエンジニアから広くコメント(ツッコミ)を募りました。

Image for post
Image for post
Image for post
Image for post

※上記画像内の会話はあくまで議論の過程であり結論ではありません。

やはりコードベースで会話すると話が早いので、このやり方は良かったなと思っています。この過程によって無事チーム内で新設計の認識合わせを行い、その設計をスプリントの合間などに画面単位で少しずつ適用させていきました。

また、この後すぐにRxJava2移行とKotlin化という2大リファクタ案件があったのですが、こちらも同じようにIssueでの議論→実装例のPRを出して更に議論、というフローでスムーズに行うことができました。

その後のプロジェクトの設計改善

その後、業務委託として@sho5氏など優秀なエンジニアがチームに参加してくれて、少しずつ設計を改善していきました。

たとえばパッケージ構成は何度も改善を行っており、現在はpresentationレイヤーではactivityやpresenterというパッケージは作らず、機能ごとにパッケージを切り、その中に関係するView、Presenter、ListAdapter、DaggerModuleなどを一箇所に置くようにしました。更にかなり迷ったのですが、UseCaseもAndroid版paymoでは基本的に画面と1対1になるため、同じパッケージに置くようにしています。

Image for post
Image for post

これは邪道だと考える方もいるかもしれませんが、思想的に正しい事よりも実際の開発時の見通しの良さや生産性を重視しました。(Kotlinではpackage privateが存在しないという理由もあります)

また、@sho5氏の提案でドメインモデルにはDDDの考え方に乗っ取りEntityとValueObjectという定義付けを行いました。

PresenterとUseCaseの責務分けについて

最後に、発表では盛り込めずセッション後に質問を多く頂いたPresenterとUseCaseの責務分けについてpaymoプロジェクトでの運用と自分の考え方を少し共有しようと思います。

Android版paymoでは基本的にドメインモデルはPresenterから操作され、UseCaseでは行いません。なので、UseCaseの責務は基本的にRepositoryとPresenterの間を仲介し、データアクセスを隠蔽する中間レイヤーとなっています。

これには理由があり、Android版paymoプロジェクトでは原則としてPresenterに対してテストを書くようにしており、ロジックをその中に集約させたいという想いがあります。以前別のプロジェクトでPresenterにはUIロジック、UseCaseにはビジネスロジックを書こうとした時には、処理をどちらに書くべきなのか迷いがちだったり、PresenterとUseCaseで同じようなテストを重複して書くことになってしまいました。これは思想的には正しそうに思えますが、実際に運用する上ではなかなか扱いにくいなと感じ、paymoではPresenterに寄せるようにしています。

もう一つの理由として、ドメインロジックは基本的にドメインモデルとしてクラス化していくので、あえてUseCaseというドメインを担うレイヤーを用意しなくても適切にドメインモデルを定義することでUIロジックとドメインロジックを分離出来るはずであると考えています。

もしかしたら「じゃあUseCaseは定義せずPresenterがRepositoryを直接呼べばいいじゃん」と思う方もいるかもしれません。そうしない理由としては、UseCaseがRepositoryへのアクセスを画面単位で集約する中間レイヤーとして存在することで、Mock化がしやすいという事があります。RepositoryはUIには依存せずデータのコンテキスト単位で存在するため、UseCaseがない場合には一つのPresenterが大量のRepositoryをpropertyとしてInjectすることになり、テスト時にはその全てを個別にMock化する必要が出てきます。

そういう意味ではAndroid版paymoプロジェクトでのUseCaseはPresentationレイヤーに近い存在なのかもしれません。

まとめ

AnyPayには優秀なエンジニアが多数在籍しており、設計や使用する技術やライブラリなどについても積極的に最善のものを検討し導入していくという体制があります。

まだまだ改善したい部分や実装したい機能がたくさんたくさんありますので、AnyPayで一緒に働くことに興味がある方はぜひご応募ください!

そして、このエントリをご覧になってpaymoというアプリが気になった方は、ぜひインストールしてみてください!paymoは飲み会などのわりかんの精算をスマホアプリ上でキャッシュレスに行える便利で楽しいアプリです!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store