Pro Yearly is on sale from $80 to $50! »

Repository with Store4 [ja]

Repository with Store4 [ja]

6dd0483f1353a4a359e92633cfd65c64?s=128

Daichi Furiya (Wasabeef)

January 17, 2020
Tweet

Transcript

  1. Repository pattern with Store4 Wasabeef #shibuya_apk

  2. About me Daichi Furiya (降矢 大地) Google Developers Expert CATS,

    CyberAgent @wasabeef_jp wasabeef
  3. Repository pattern with Store4

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

  5. そして、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
  6. そして、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
  7. そして、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
  8. そして、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
  9. ただ、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 ?????
  10. 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 を使うこ とで、簡単になりそ うなので今回注目し ています。
  11. Repository pattern?

  12. Microsoft によると ...

  13. 引用「データソースにアクセス するために必要なロジックをカ プセル化するクラスまたはコン ポーネント。共通のデータアク セス機能を一元化し、保守性を 向上させ、ドメインモデルレイ ヤーからデータベースにアクセ スするために使用されるインフ ラストラクチャまたはテクノロ ジーを分離します。」

    Repository pattern? https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-design
  14. 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
  15. 当たり前のことですが 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
  16. 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
  17. よくある課題 ...

  18. 例えば、同じ画面に複 数の Fragment がい て、それぞれ User データを取得するため に UserRepository か

    ら getUser をコールし たとします。 よくある課題 ... UserRepository#getUser() Activity A Fragment B Fragment C
  19. よくある課題 ... UserRepository#getUser() Activity A Fragment B Fragment C そうすると、

    UserRepository がシ ングルトンだとして も、それぞれがネット ワーク通信をして、 データを取得しにいき ます。
  20. もし仮に、User データ がほとんどの場合にお いて短期間で不変な仕 組みであるなら、この ネットワーク通信を し、サーバに負荷をか けるのはもったいない かもしれません。 よくある課題

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

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

    A Fragment B Fragment C
  23. Store4 ...?

  24. Store4 ...? dropbox/Store nytimes/Store

  25. 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' }
  26. 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 で取得するイメージで簡単な説明をしていきます。
  27. StoreBuilder val store = StoreBuilder !!... Repository に相当する Store を

    StoreBuilder で実装していきます。 これは、DI で管理することになるかと思います。
  28. StoreBuilder val store = StoreBuilder .fromNonFlow<UserId, User> { key !->

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

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

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

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

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

    api.fetchUser(key).user }.build() 導入初期は Persister を指定しないで、On Memory キャッシュとしてだけ 使うのを試してもいいかもしれません。
  34. 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 内などで行います。
  35. 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() を使ってデータを取得することもできます。
  36. get or fresh ? get(key) 指定されたキーを元にメモリ内または ディスクキャッシュから取得 fresh(key) キャッシュを無視して最新を取得 get

    と fresh をどちらを使うかを意識しないといけないのは Repository pattern 概念としては難しいところがあるかもしれませんが … Pull-to-Refresh などの仕組みがある以上、必要なことだと思います。
  37. 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 }
  38. おしまい..

  39. 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
  40. twitter.com/wasabeef_jp wasabeef.jp github.com/wasabeef