Slide 1

Slide 1 text

Repository pattern with Store4 Wasabeef #shibuya_apk

Slide 2

Slide 2 text

About me Daichi Furiya (降矢 大地) Google Developers Expert CATS, CyberAgent @wasabeef_jp wasabeef

Slide 3

Slide 3 text

Repository pattern with Store4

Slide 4

Slide 4 text

STORE4 - MIGRATING A LIBRARY FROM RXJAVA TO COROUTINES https://kotlinconf.com/talks/video/2019/126904/

Slide 5

Slide 5 text

そして、MVVM で設 計する上で、使いや すいライブラリが Jetpack には多く存 在します。 Architecture(MVVM) Activity/Fragment Repository ViewModel LiveData Local Source Room Remote Data Source Retrofit/okHttp https://developer.android.com/jetpack/docs/guide LiveData https://developer.android.com/jetpack/docs/guide https://developer.android.com/jetpack/docs/guide Jetpack

Slide 6

Slide 6 text

そして、MVVM で設 計する上で、使いや すいライブラリが Jetpack には多く存 在します。 Architecture(MVVM) Activity/Fragment Repository ViewModel LiveData Local Source Room Remote Data Source Retrofit/okHttp https://developer.android.com/jetpack/docs/guide LiveData https://developer.android.com/jetpack/docs/guide https://developer.android.com/jetpack/docs/guide Jetpack

Slide 7

Slide 7 text

そして、MVVM で設 計する上で、使いや すいライブラリが Jetpack には多く存 在します。 Architecture(MVVM) Activity/Fragment Repository ViewModel LiveData Local Source Room Remote Data Source Retrofit/okHttp https://developer.android.com/jetpack/docs/guide LiveData https://developer.android.com/jetpack/docs/guide https://developer.android.com/jetpack/docs/guide Jetpack

Slide 8

Slide 8 text

そして、MVVM で設 計する上で、使いや すいライブラリが Jetpack には多く存 在します。 Architecture(MVVM) Activity/Fragment Repository ViewModel LiveData Local Source Room Remote Data Source Retrofit/okHttp https://developer.android.com/jetpack/docs/guide LiveData https://developer.android.com/jetpack/docs/guide https://developer.android.com/jetpack/docs/guide Jetpack Jetpack Jetpack

Slide 9

Slide 9 text

ただ、Repository の実装については参 考にできるもの (iosched, plaid) はあれど Jetpack で はライブラリ化され ていません。 Architecture(MVVM) Activity/Fragment Repository ViewModel LiveData Local Source Room Remote Data Source Retrofit/okHttp https://developer.android.com/jetpack/docs/guide LiveData https://developer.android.com/jetpack/docs/guide https://developer.android.com/jetpack/docs/guide Jetpack Jetpack Jetpack ?????

Slide 10

Slide 10 text

Repository pattern? Activity/Fragment Repository ViewModel LiveData Local Source Room Remote Data Source Retrofit/okHttp https://developer.android.com/jetpack/docs/guide LiveData https://developer.android.com/jetpack/docs/guide https://developer.android.com/jetpack/docs/guide Store4 そこで現在は Dropbox が主体と なって開発されてい る Store4 を使うこ とで、簡単になりそ うなので今回注目し ています。

Slide 11

Slide 11 text

Repository pattern?

Slide 12

Slide 12 text

Microsoft によると ...

Slide 13

Slide 13 text

引用「データソースにアクセス するために必要なロジックをカ プセル化するクラスまたはコン ポーネント。共通のデータアク セス機能を一元化し、保守性を 向上させ、ドメインモデルレイ ヤーからデータベースにアクセ スするために使用されるインフ ラストラクチャまたはテクノロ ジーを分離します。」 Repository pattern? https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-design

Slide 14

Slide 14 text

Android の MVVM に合 わせて要約すると、 Repository を使うこと で、ローカルから取得す るかネットワークから取 得するかを ViewModel が意識する必要がないよ うにします。 Repository pattern? Activity/Fragment Repository ViewModel LiveData Remote Data Source Retrofit/okHttp Local Source Room https://developer.android.com/jetpack/docs/guide LiveData https://developer.android.com/jetpack/docs/guide https://developer.android.com/jetpack/docs/guide Store4

Slide 15

Slide 15 text

当たり前のことですが Repository を意識しな くていいのは ViewModel から使う側 の話で、実装において は意識して書かなけれ ばなりません。 Repository pattern? Activity/Fragment Repository ViewModel LiveData Local Source Room Remote Data Source Retrofit/okHttp https://developer.android.com/jetpack/docs/guide LiveData Store4

Slide 16

Slide 16 text

Repository pattern? Activity/Fragment Repository ViewModel LiveData Remote Data Source Retrofit/okHttp https://developer.android.com/jetpack/docs/guide LiveData On Memory Store4 さらにいうと、Local Source が SQLite だけ ではなくその前に On Memory に保存・取得す る仕組みを考えだすと、 実装さらに大変になって いきます。 Local Source Room

Slide 17

Slide 17 text

よくある課題 ...

Slide 18

Slide 18 text

例えば、同じ画面に複 数の Fragment がい て、それぞれ User データを取得するため に UserRepository か ら getUser をコールし たとします。 よくある課題 ... UserRepository#getUser() Activity A Fragment B Fragment C

Slide 19

Slide 19 text

よくある課題 ... UserRepository#getUser() Activity A Fragment B Fragment C そうすると、 UserRepository がシ ングルトンだとして も、それぞれがネット ワーク通信をして、 データを取得しにいき ます。

Slide 20

Slide 20 text

もし仮に、User データ がほとんどの場合にお いて短期間で不変な仕 組みであるなら、この ネットワーク通信を し、サーバに負荷をか けるのはもったいない かもしれません。 よくある課題 ... UserRepository#getUser() Activity A Fragment B Fragment C

Slide 21

Slide 21 text

もしこのデータを一定 のルールものキャッ シュする仕組みがあっ たらネットワークの負 荷を減らせるかもしれ ません。 よくある課題 ... UserRepository#getUser() Activity A Fragment B Fragment C

Slide 22

Slide 22 text

もしこのデータを一定 のルールものキャッ シュする仕組みがあっ たらネットワークの負 荷を減らせるかもしれ ません。 よくある課題 ... UserRepository#getUser() Activity A Fragment B Fragment C

Slide 23

Slide 23 text

Store4 ...?

Slide 24

Slide 24 text

Store4 ...? dropbox/Store nytimes/Store

Slide 25

Slide 25 text

build.gradle !// Set the source & target compatibilities to 1.8 android { compileOptions { sourceCompatibility 1.8 targetCompatibility 1.8 } !!... } dependencies { !// 2020.1.17 implementation 'com.dropbox.mobile.store:store4:4.0.0-alpha01' }

Slide 26

Slide 26 text

StoreBuilder typealias UserName = String typealias UserId = String typealias TimeMs = Long data class User( val id: UserId, val name: UserName, val birthday: TimeMs) val userId: UserId = "wasabeef" UserId を Key として Store で取得するイメージで簡単な説明をしていきます。

Slide 27

Slide 27 text

StoreBuilder val store = StoreBuilder !!... Repository に相当する Store を StoreBuilder で実装していきます。 これは、DI で管理することになるかと思います。

Slide 28

Slide 28 text

StoreBuilder val store = StoreBuilder .fromNonFlow { key !-> } fromNonFlow に 最新のデータを取得するためメソッドコールを定義します。

Slide 29

Slide 29 text

StoreBuilder val store = StoreBuilder .fromNonFlow { key !-> api.fetchUser(key).user } ここでは、Retrofit など Data source を使って API コールします。

Slide 30

Slide 30 text

StoreBuilder val store = StoreBuilder .from { key !-> api.fetchUser(key) } ここでは、Data source が既に Flow 化されているのであれば fromNonFlow {} !-> from {} ͱ͍ͯͩ͘͠͞ɻ

Slide 31

Slide 31 text

StoreBuilder val store = StoreBuilder .fromNonFlow { key !-> api.fetchUser(key).user }.persister( reader = !!..., writer = !!..., delete = !!..., ).build() データを永続化するときにどういう振る舞いを行うかを Persister として指定することができます。

Slide 32

Slide 32 text

StoreBuilder val store = StoreBuilder .fromNonFlow { key !-> api.fetchUser(key).user }.persister( reader = db.userDao()!::load, writer = db.userDao()!::update, delete = db.userDao()!::clear ).build() もちろん Room で使うことも出来ます。

Slide 33

Slide 33 text

StoreBuilder val store = StoreBuilder .fromNonFlow { key !-> api.fetchUser(key).user }.build() 導入初期は Persister を指定しないで、On Memory キャッシュとしてだけ 使うのを試してもいいかもしれません。

Slide 34

Slide 34 text

suspend fun Store.get(key) !// UserViewModel.kt viewModelScope.launch { userLiveData.value = try { val user = store.get("wasabeef") Result.Success(user) } catch(e: Exception) { Result.Error(e) } } StoreBuilder の Store 実装が終わっていれば、get() を使ってデータを取得します。 これは、サスペンド関数なので ViewModel の CoroutineScope 内などで行います。

Slide 35

Slide 35 text

suspend fun Store.fresh(key) !// UserViewModel.kt viewModelScope.launch { userLiveData.value = try { val user = store.fresh("wasabeef") Result.Success(user) } catch(e: Exception) { Result.Error(e) } } fresh() を使ってデータを取得することもできます。

Slide 36

Slide 36 text

get or fresh ? get(key) 指定されたキーを元にメモリ内または ディスクキャッシュから取得 fresh(key) キャッシュを無視して最新を取得 get と fresh をどちらを使うかを意識しないといけないのは Repository pattern 概念としては難しいところがあるかもしれませんが … Pull-to-Refresh などの仕組みがある以上、必要なことだと思います。

Slide 37

Slide 37 text

MemoryPolicy デフォルトのメモリポリシーでは上限 100 個、期限 24 時間となっています。 class MemoryPolicy internal constructor( val expireAfterWrite: Long, val expireAfterAccess: Long, val expireAfterTimeUnit: TimeUnit, private val maxSizeNotDefault: Long ) { !// expireAfterWrite = TimeUnit.HOURS.toSeconds(24), !// expireAfterTimeUnit = TimeUnit.SECONDS, !// maxSizeNotDefault = 100 }

Slide 38

Slide 38 text

おしまい..

Slide 39

Slide 39 text

Image Resources Photos: - https://unsplash.com - https://www.pexels.com - https://www.reddit.com/r/dragonquest/comments/9dm013/ new_desktop_background/ Illustrations: - http://www.chojugiga.com - https://www.irasutoya.com

Slide 40

Slide 40 text

twitter.com/wasabeef_jp wasabeef.jp github.com/wasabeef