Upgrade to Pro — share decks privately, control downloads, hide ads and more …

分析用コードをアプリから 切り離す設計の実現

makun
November 22, 2019

分析用コードをアプリから 切り離す設計の実現

#pixiv_app_night

makun

November 22, 2019
Tweet

More Decks by makun

Other Decks in Programming

Transcript

  1. 2 自己紹介 • 18年 新卒入社 • Androidアプリ開発を担当 • エンジニア採用を担当 •

    好きなのは設計やアイデアだし • 苦手なのは収束させること makun アプリエンジニア
  2. 5 Dispatcher Store (ViewModel) ActionCreator (ViewModel) View (Activity, Fragment) Server

    Repository Database Entity Action Action Item Entity Remote Model Local Model
  3. 6 Database Entity Core Feature Production App Repository Development App

    Feature Feature Repository Repository Database Database Remote Model Local Model Presentation Domain Data Resources
  4. 12

  5. class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)

    { FirebaseAnalytics .getInstance(context.applicationContext) .logEvent(“follow_comic”, bundleOf( “comicId” to comic.id, “comicTitle” to comic.title )) launch { … } } } 14
  6. class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)

    { FirebaseAnalytics… Repro.track(“【フォロ】コミック”, mapOf( “comicId” to comic.id, “comicTitle” to comic.title )) launch { … } } } 15
  7. class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)

    { FirebaseAnalytics… Repro.track(…) AppsFlyerLib.getInstance().trackEvent( context.applicationContext, “key_follow_comic”, mapOf( “comicId” to comic.id, “comicTitle” to comic.title )) launch { … } } } 16
  8. 23

  9. class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)

    { FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 32
  10. class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)

    { FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 33 ユーザーがボタンをタップし たときの処理をここにつくる
  11. class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)

    { FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 34 要求だとFirebase、Repro、 AppsFlyerにイベントを送信す る必要があるぞ
  12. class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)

    { FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 35 分析コードの実装のために Contextを受け取らないと いけないぞ
  13. class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)

    { FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 36 今回の要求だとComicの Entityだけでデータはおくれ そうだぞ
  14. class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)

    { FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 37 Context追加しちゃったから メソッドを実行してる部分に もContextわたさないといけ ないぞ
  15. class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)

    { FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 38 あとテストも書き直さないと いけないなぁ
  16. class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)

    { FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 41 まずはこのコードまでたどりつく必要がある
  17. class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)

    { FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 42 フォローボタンをタップした ときで、すでにフォローして いたかどうかが判定されて いない
  18. class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)

    { if (comic.isFollowed.not()) { FirebaseAnalytics… Repro… AppsFlyerLib… } launch { … } } } 43
  19. 48 Dispatcher Store (ViewModel) ActionCreator (ViewModel) View (Activity, Fragment) Server

    Repository Database Entity Action Action Item Entity Remote Model Local Model
  20. 49 Dispatcher Store (ViewModel) ActionCreator (ViewModel) View (Activity, Fragment) Server

    Repository Database Tracker Entity Action Action Item Action Entity Remote Model Local Model
  21. // Dispatcherを継承する本番用Dispatcher class MainDispatcher( // ActionReceiverを実装したクラスを受け取る private vararg val receivers:

    ActionReceiver ) : Dispatcher() { override fun dispatch(action: Action) { // 全てのActionReceiverにActionを送信する receivers.forEach { it.receive(action) } super.dispatch(action) } } 51
  22. // Dispatcherを継承する本番用Dispatcher class MainDispatcher( // ActionReceiverを実装したクラスを受け取る private vararg val receivers:

    ActionReceiver ) : Dispatcher() { override fun dispatch(action: Action) { // 全てのActionReceiverにActionを送信する receivers.forEach { it.receive(action) } super.dispatch(action) } } 52
  23. class ComicActionCreator : ViewModel() { fun tapFollowComic(comic: Comic) { launch

    { dispatch(TapFollowComicAction(comic)) // 以下に実際のフォロー処理をかく … } } } 54
  24. class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)

    { FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 58
  25. class ComicActionCreator : ViewModel() { fun tapFollowComic(comic: Comic) { launch

    { dispatch(TapFollowComicAction(comic)) // 以下に実際のフォロー処理をかく … } } } 59 ・分析コードをかく必要がない ・機能開発だけに集中できる ・レビューコストが減る
  26. 62 Entity Core Feature Production App Feature Feature Tracker 機能開発がモジュール内だけで完結

    分析コードのロジックが Trackerモジュールにまとまる