Slide 1

Slide 1 text

マルチテナントにおける イベント計測の話 id:r4wxii / @r4wxii 2024/09/19 はてなのモバイル開発大紹介! 1

Slide 2

Slide 2 text

2 イベント計測

Slide 3

Slide 3 text

3 イベント計測 ● ユーザ・行動の分析や広告効果測定などに活用 される ○ アプリでの代表例はFirebase ● 様々なユーザが利用するアプリの運営に イベント計測は必要不可欠 ○ 当然ジャンプ+(GigaViewer for Apps)でも活用している

Slide 4

Slide 4 text

4 ジャンプ+ (GigaViewer for Apps)? マルチテナントって?

Slide 5

Slide 5 text

5 ジャンプ+は GigaViewer for Appsの テナントの1つ

Slide 6

Slide 6 text

6 ● 1つのコードベースで複数のメディアを運営 ○ 複数のメディア(テナント)、でマルチテナント ○ 複数のメディアのうちの1つがジャンプ+ GigaViewer for Appsはマルチテナント

Slide 7

Slide 7 text

GigaViewer for Appsはマルチテナント 7 ● マルチテナントであるGigaViewer for Apps ではどうイベント計測している? ○ メディアによって欲しい情報や利用する計測プラット フォームが異なるため、それぞれに対応するための機能 を持っている

Slide 8

Slide 8 text

8 それぞれに 対応するための 機能

Slide 9

Slide 9 text

9 ただ計測プラットフォー ムが異なるだけではない

Slide 10

Slide 10 text

10 異なるだけ ではないとは ● メディアによって計測PF が異なる ● あるメディアしか利用し ない計測PFがあれば、 複数メディアが利用する 計測PFもある

Slide 11

Slide 11 text

異なるだけではないとは ● 同一タイミングですべての計測PFにイベント 送信するわけではない ○ 計測PFによって得意なことが違うので使い分けられている ○ すべての計測PFに送信されるイベントがあれば、 ある計測PFにだけ送信されるイベントもある 11

Slide 12

Slide 12 text

12 ふ、複雑…

Slide 13

Slide 13 text

言い換えると 13

Slide 14

Slide 14 text

メディアごとに 適切な計測PFへ イベント送信する機能 14

Slide 15

Slide 15 text

15 今回はどのように 構築したか紹介

Slide 16

Slide 16 text

まずは方針を考える 16

Slide 17

Slide 17 text

17 方針を考える 上で前提の話

Slide 18

Slide 18 text

前提 ● GigaViewer for Appsはマルチモジュール ○ エントリポイントとなるメディアモジュール ■ そのメディア固有の機能も含まれている ■ 例:ジャンプ+ ○ 機能ごとのfeatureモジュール ■ その機能固有の処理が書かれている ■ メディア固有の処理がなく、単体性が保たれているので どのメディアでも使える ■ 例:作品詳細、初回無料、ダウンロード 18

Slide 19

Slide 19 text

前提 ● メディアモジュールから欲しい機能の モジュールに依存することでアプリを構成する ○ 用意した機能の中からメディアが欲しい機能を取捨選択 するイメージ ○ そのメディアに存在しない機能の混入を防げる ■ メディア固有の処理が共通部分に含まれないようにしているため 19

Slide 20

Slide 20 text

20 以上を踏まえて

Slide 21

Slide 21 text

方針① 21

Slide 22

Slide 22 text

● メディア・計測PFを意識しないでイベント送信 ○ 共通機能はどの計測PFを使うか知りたくない ■ 必要な計測PFというのはメディアの関心なので共通機能に 持ち込みたくない ■ 大半のイベントは共通機能から送信される ○ 1度の処理で必要な計測PF全てに送信されてほしい ■ すべてのイベントで毎回計測PFごとに呼び出すのは手間 22 方針①

Slide 23

Slide 23 text

23 方針②

Slide 24

Slide 24 text

● 拡張性を持たせる ○ 今後機能やメディアが増えて、新しいイベント・計測PF も増える可能性があるため ■ イベント追加はiOSとAndroidのOS間で足並みを揃えたい ■ 計測PFの追加を楽に行いたい 方針② 24

Slide 25

Slide 25 text

25 方針を元に 設計を考える

Slide 26

Slide 26 text

26 Androidを例にして説明 (元の設計は1つなのでiOSも大体同じ)

Slide 27

Slide 27 text

27 イベントの定義

Slide 28

Slide 28 text

28 イベントの定義 ● 各イベントはyamlに定義 ○ 定義するものはイベント名, パラメータ, パラメータの型 ○ yamlを元に各OSの実装に適したコードを自動生成 ■ 自動生成されたものをEventとする ○ yamlはメディア×計測PFごとに分ける ■ 共通のものとメディア固有のもので分けたい ○ 同じyamlをソースにしているのでOS間で定義が揃う

Slide 29

Slide 29 text

yamlから自動生成 events: - name: example - params: - name: param type: Parameter 29 data class ExampleEvent( private val param: Parameter, ): Event { override val eventName = “example” override val params = mapOf(“param” to param) }

Slide 30

Slide 30 text

イベントの定義 ● 生成された各計測PFのEventをまとめる ○ ここは現状温かみのある人間の手作業… ○ まとめた一塊をEventProxyとする ○ このEventProxyは同一タイミングで送信される すべての計測PFのEventをもつ 30

Slide 31

Slide 31 text

イベントの定義 ● 例えばあるボタンの押下数を計測したい ○ メディア1 ■ AとBという計測PFを利用していて、Aだけでイベント計測したい ■ どの画面で押下されたかも知りたい ○ メディア2 ■ Cという計測PFを利用していて、Cでイベント計測したい ■ どの画面で押下されたかは不必要 ○ この場合EventProxyはAとCのEventを持つ 31

Slide 32

Slide 32 text

イベントの定義 data class TapButtonEventProxy( private val screen: String, ): EventProxy { override val platformA: Event? = A.TapButtonEvent(screen), override val platformB: Event? = null, override val platformC: Event? = C.TapButtonEvent(), } 32

Slide 33

Slide 33 text

33 このEventProxyを 送信処理へ渡すだけで 各計測PFに送信 されたい

Slide 34

Slide 34 text

34 各地から呼び出す イベント送信処理の実装

Slide 35

Slide 35 text

35 イベント送信処理 ● イベント送信という1つの機能として抽象化 ○ EventProxyを渡してイベント送信するinterface ○ この機能をEventTrackerとする ● interfaceは共通モジュールに定義、 実装は各メディアモジュールに任せる ○ メディアごとに実装することでどの計測PFを利用するか というメディア固有の処理を扱える

Slide 36

Slide 36 text

イベント送信処理 ● メディアで実装するEventTrackerの中身 ○ 実態はイベントの振り分け ■ EventProxyが持っている送信が必要なEventを取り出す ■ 取り出したEventを送信先となる計測PF固有の処理へ渡す ■ 実際に計測PFへイベントが送信されるのは渡した先の処理 36

Slide 37

Slide 37 text

イベント送信処理 ● メディア1で実装するEventTracker ○ EventProxyがAもしくはBのEventを持っていた場合 それぞれの固有処理へ渡す ● メディア2で実装するEventTracker ○ EventProxyがCのEventを持っていた場合Cの固有処理 へ渡す 37

Slide 38

Slide 38 text

● 呼び出し側の動き ○ 送信したいタイミングでEventProxyをEventTracker へ渡すだけ ○ EventTrackerはメディアの実装がDIされるので 共通機能から呼んでもメディア固有の処理が走る ■ 呼び出し側はEventProxyさえ知っていればいいので どの計測PFへ送信するか意識しなくてよい 38 イベント送信処理

Slide 39

Slide 39 text

図にするとこんな感じ 39

Slide 40

Slide 40 text

40 使用する計測PFを どのように切り替えるか

Slide 41

Slide 41 text

41 おわかりですね?

Slide 42

Slide 42 text

メディアごとに依存 モジュールを変える! 42

Slide 43

Slide 43 text

メディアごとに依存モジュールを変える ● 計測PFごとにfeatureモジュールを作成する ○ 計測PFへイベント送信するという1つの機能として抽象化 ○ メディアモジュールから依存することでその機能を 使えるようになる ○ 各計測PFのSDKはこのfeatureモジュールからのみ依存 ■ 計測PF固有の処理がfeatureモジュールに閉じたものになり、 メディア固有ではなくなる 43

Slide 44

Slide 44 text

メディアごとに依存モジュールを変える ● 計測PF固有のイベント送信処理も抽象化し、 共通モジュールにinterfaceで定義する ● 各メディアで実装したEventTrackerから 抽象化した処理を呼び出せばよい 44

Slide 45

Slide 45 text

45 計測PF固有処理のコード class PlatformA @Inject constructor( // 計測プラットフォームの SDKがDIされる private val sdk: SDK, ): AnalyticsPlatform { override fun track(event: Event) { // SDKを使った計測プラットフォーム固有の送信処理 } }

Slide 46

Slide 46 text

46 メディアで実装するEventTracker class EventTrackerImpl @Inject constructor( // 本来はメディアが依存する複数の計測 PFがDIされる private val platform: AnalyticsPlatform, ): EventTracker { override fun track(eventProxy: EventProxy) { // それぞれの Eventを送信する処理 platform.track(eventProxy.event) } }

Slide 47

Slide 47 text

図にするとこんな感じ 47

Slide 48

Slide 48 text

似た話聞いたな 🤔 48

Slide 49

Slide 49 text

49 なにが似ているのか ● EventTracker ○ イベント送信というGigaViewer for Appsの機能として ● featureモジュール ○ 計測PFへイベント送信するという機能として

Slide 50

Slide 50 text

50 要は

Slide 51

Slide 51 text

メディアごとに 適切な計測PFへ イベント送信する機能 51

Slide 52

Slide 52 text

メディアごとに 適切な計測PFへ イベント送信 する機能 52

Slide 53

Slide 53 text

これで考えることは できたので 53

Slide 54

Slide 54 text

54 方針に沿っているか

Slide 55

Slide 55 text

55 方針① ● メディア・計測PFを意識しないでイベント送信 ○ ✅呼び出し側は共通モジュールに定義されている interfaceさえ知っていればいい ○ ✅EventProxyをEventTrackerへ渡すだけ ○ ❌抽象化したはずのEventProxyがすべての計測PFを 知ってしまっているのが少し気になる ■ 各Eventのパラメータが多いとEventProxyのパラメータが膨大に なる問題もある

Slide 56

Slide 56 text

方針② ● 拡張性を持たせる ○ ✅yamlに追記することで管理可能 ○ ✅計測PFの追加は新たにfeatureモジュールを 作成すればOK ○ ✅新規メディアはfeatureモジュールに依存するだけ ○ ❌EventTrackerを実装しないといけない ■ 計測PF固有の送信する処理も抽象化しているのでメディア毎に 実装しなくてもいいはず… 56

Slide 57

Slide 57 text

57 まとめ

Slide 58

Slide 58 text

58 まとめ ● ひとことで機能といってもマルチテナントだと 複雑になってしまうことがよくある ○ メディアの機能開発でもGigaViewer for Appsを開発 しているという意識 ■ うまく抽象化して実装できるとよい ○ マルチテナント設計を意識した開発はとても楽しい