Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
分析用コードをアプリから 切り離す設計の実現
Search
makun
November 22, 2019
Programming
0
120
分析用コードをアプリから 切り離す設計の実現
#pixiv_app_night
makun
November 22, 2019
Tweet
Share
More Decks by makun
See All by makun
既存コードへのテスト追加とリファクタリングの実践
makun
0
100
Jetpack Composeを本番導入してみた結果と課題
makun
1
230
Compose Compiler Metrics 詳細と活用方法
makun
1
840
Other Decks in Programming
See All in Programming
Reactの歴史を振り返る
tutinoko
1
180
実践 Dev Containers × Claude Code
touyu
1
170
Vibe coding コードレビュー
kinopeee
0
430
あのころの iPod を どうにか再生させたい
orumin
2
2.4k
WebAssemblyインタプリタを書く ~Component Modelを添えて~
ruccho
1
740
kiroでゲームを作ってみた
iriikeita
0
150
抽象化という思考のツール - 理解と活用 - / Abstraction-as-a-Tool-for-Thinking
shin1x1
1
950
Jakarta EE Meets AI
ivargrimstad
0
660
ゲームの物理
fadis
3
950
それ CLI フレームワークがなくてもできるよ / Building CLI Tools Without Frameworks
orgachem
PRO
17
3.8k
バイブスあるコーディングで ~PHP~ 便利ツールをつくるプラクティス
uzulla
1
330
構文解析器入門
ydah
7
2.1k
Featured
See All Featured
How to Think Like a Performance Engineer
csswizardry
25
1.8k
Intergalactic Javascript Robots from Outer Space
tanoku
272
27k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
161
15k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
26k
YesSQL, Process and Tooling at Scale
rocio
173
14k
RailsConf 2023
tenderlove
30
1.2k
Producing Creativity
orderedlist
PRO
347
40k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
110
19k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
18
1.1k
Code Reviewing Like a Champion
maltzj
524
40k
The Straight Up "How To Draw Better" Workshop
denniskardys
235
140k
GitHub's CSS Performance
jonrohan
1031
460k
Transcript
分析用コードをアプリから 切り離す設計の実現 pixiv Inc. makun 2019.11.22
2 自己紹介 • 18年 新卒入社 • Androidアプリ開発を担当 • エンジニア採用を担当 •
好きなのは設計やアイデアだし • 苦手なのは収束させること makun アプリエンジニア
3 • マンガサービス • 講談社と協業 • 女性がターゲット • 女性誌特集なども •
Android、iOS
アーキテクチャ 4
5 Dispatcher Store (ViewModel) ActionCreator (ViewModel) View (Activity, Fragment) Server
Repository Database Entity Action Action Item Entity Remote Model Local Model
6 Database Entity Core Feature Production App Repository Development App
Feature Feature Repository Repository Database Database Remote Model Local Model Presentation Domain Data Resources
分析コードとは 7
ユーザーの行動を把握しアプリのマーケティン グやパフォーマンス改善に関する 意思決定を行うためのデータを得ることのでき るコード、もしくは得るためのコード 8
分析コードを 実装するとは 9
意思決定を行うためのデータを得ることのでき るコード、もしくは得るためのコードが動作する よう記述する 10
パルシィのコードに 分析コードを実装してみる 11
12
class ComicActionCreator : ViewModel() { fun tapFollowComic(comic: Comic) { launch
{ … } } } 13
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
class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)
{ FirebaseAnalytics… Repro.track(“【フォロ】コミック”, mapOf( “comicId” to comic.id, “comicTitle” to comic.title )) launch { … } } } 15
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
本当にこの実装でいいんだっけ? 17
Context受け取るようになったけど テストのことちゃんと考慮してる? 18
同じ分析コードを埋め込む箇所が 増えたときはどうする? 19
機能モジュールの依存増えてない? ビルド時間とかも大丈夫? 20
分析ツールが 増えたり減ったりしたときは どうする? 21
そもそもなんでこんなに 分析ツールあんの? 22
23
24 ツール 目的 Firebase ピクシブ全体で積極的に利用している BigQueryにデータをあげて全体的な分析に利用 Repro 協業先に利用実績があり 特定のアクションに対してアプリ内メッセージをだしたりに利用 AppsFlyer
協業先に利用実績があり アプリの流入や経路別のコンバージョンをみるために利用 分析用ツールを複数使う理由
分析コードを実装する場 合の設計を考える 25
26 それぞれの視点で考える 設計時にやること
27 機能を開発する人の視点
28 分析コードを追加する人の視点
29 今回は触れない視点 • アプリを利用するユーザーの視点 • テストの視点 • リファクタリングの視点
30 設計次以外のメリット • コードを書くときにも有効 • 視点ごとにプルリクをわけられる • 実装時の思考コストが減る • レビュー時の思考コストが減る
• 意味のあるまとまりで開発をすすめられる
機能を開発する人の視点でみてみる 31
class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)
{ FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 32
class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)
{ FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 33 ユーザーがボタンをタップし たときの処理をここにつくる
class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)
{ FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 34 要求だとFirebase、Repro、 AppsFlyerにイベントを送信す る必要があるぞ
class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)
{ FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 35 分析コードの実装のために Contextを受け取らないと いけないぞ
class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)
{ FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 36 今回の要求だとComicの Entityだけでデータはおくれ そうだぞ
class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)
{ FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 37 Context追加しちゃったから メソッドを実行してる部分に もContextわたさないといけ ないぞ
class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)
{ FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 38 あとテストも書き直さないと いけないなぁ
39 Entity Core Feature Production App Feature Feature Firebase Repro
AppsFlyer
分析コードを実装する人の視点でみてみる 40
class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)
{ FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 41 まずはこのコードまでたどりつく必要がある
class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)
{ FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 42 フォローボタンをタップした ときで、すでにフォローして いたかどうかが判定されて いない
class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)
{ if (comic.isFollowed.not()) { FirebaseAnalytics… Repro… AppsFlyerLib… } launch { … } } } 43
44 分析コードはロジックをもつ
45 ユーザー体験とは関係ないはずなのに・・ • ユーザー体験にかかわるところ (Featureモジュール) にコードをかく • 既存の処理を変更したりしないといけないことがある • 変更をくわえちゃうから実機のテストもちゃんとしておきたくなる
• テストもちゃんとかいておきたくなる • 考えないといけないところが増えた気がする
パルシィでの設計 46
Flux + Tracker 47
48 Dispatcher Store (ViewModel) ActionCreator (ViewModel) View (Activity, Fragment) Server
Repository Database Entity Action Action Item Entity Remote Model Local Model
49 Dispatcher Store (ViewModel) ActionCreator (ViewModel) View (Activity, Fragment) Server
Repository Database Tracker Entity Action Action Item Action Entity Remote Model Local Model
// Actionを受け取れる概念をinterfaceにする interface ActionReceiver { fun receive(action: Action) } 50
// 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
// 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
// Firebase用にActionReceiverを実装したクラス // このクラスのインスタンスをMainDispatcherにわたす // パルシィではKoinでBeanをつくる class FirebaseTracker( private val
app: Application ) : ActionReceiver { override fun receive(action: Action) { FirebaseAnalytics… } } 53
class ComicActionCreator : ViewModel() { fun tapFollowComic(comic: Comic) { launch
{ dispatch(TapFollowComicAction(comic)) // 以下に実際のフォロー処理をかく … } } } 54
55 Entity Core Feature Production App Feature Feature Firebase Repro
AppsFlyer
56 Entity Core Feature Production App Feature Feature Tracker
結果と考察 57
class ComicActionCreator : ViewModel() { fun tapFollowComic(context: Context, comic: Comic)
{ FirebaseAnalytics… Repro… AppsFlyerLib… launch { … } } } 58
class ComicActionCreator : ViewModel() { fun tapFollowComic(comic: Comic) { launch
{ dispatch(TapFollowComicAction(comic)) // 以下に実際のフォロー処理をかく … } } } 59 ・分析コードをかく必要がない ・機能開発だけに集中できる ・レビューコストが減る
60 Entity Core Feature Production App Feature Feature Firebase Repro
AppsFlyer
61 Entity Core Feature Production App Feature Feature Tracker 機能開発がモジュール内だけで完結
62 Entity Core Feature Production App Feature Feature Tracker 機能開発がモジュール内だけで完結
分析コードのロジックが Trackerモジュールにまとまる
63 Firebase Tracker Repro Tracker AppsFlyer Tracker Tracker ツールそれぞれのロジックが 各モジュールにまとまる
Production App
64 Feature Tracker Production App Firebase Tracker 分析コード実装時の考える依存が少ない
65 Production App Firebase Tracker 分析コード実装時の手を加えたり、考えるス クープはさらに小さい
快適な実装 66
ミスの少ない実装 67
最高 68