$30 off During Our Annual Pro Sale. View Details »

Repository with Store4 [ja]

Repository with Store4 [ja]

Daichi Furiya (Wasabeef)

January 17, 2020
Tweet

More Decks by Daichi Furiya (Wasabeef)

Other Decks in Technology

Transcript

  1. Repository pattern
    with Store4
    Wasabeef
    #shibuya_apk

    View Slide

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

    View Slide

  3. Repository pattern
    with Store4

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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
    ?????

    View Slide

  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 を使うこ
    とで、簡単になりそ
    うなので今回注目し
    ています。

    View Slide

  11. Repository pattern?

    View Slide

  12. Microsoft によると ...

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  17. よくある課題 ...

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. Store4 ...?

    View Slide

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

    View Slide

  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'

    }

    View Slide

  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 で取得するイメージで簡単な説明をしていきます。

    View Slide

  27. StoreBuilder
    val store = StoreBuilder

    !!...

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

    View Slide

  28. StoreBuilder
    val store = StoreBuilder

    .fromNonFlow { key !->

    }

    fromNonFlow に
    最新のデータを取得するためメソッドコールを定義します。

    View Slide

  29. StoreBuilder
    val store = StoreBuilder

    .fromNonFlow { key !->

    api.fetchUser(key).user

    }

    ここでは、Retrofit など Data source を使って API コールします。

    View Slide

  30. StoreBuilder
    val store = StoreBuilder

    .from { key !->

    api.fetchUser(key)

    }

    ここでは、Data source が既に Flow 化されているのであれば
    fromNonFlow {} !-> from {} ͱ͍ͯͩ͘͠͞ɻ

    View Slide

  31. StoreBuilder
    val store = StoreBuilder

    .fromNonFlow { key !->

    api.fetchUser(key).user

    }.persister(

    reader = !!...,

    writer = !!...,

    delete = !!...,

    ).build()
    データを永続化するときにどういう振る舞いを行うかを
    Persister として指定することができます。

    View Slide

  32. 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 で使うことも出来ます。

    View Slide

  33. StoreBuilder
    val store = StoreBuilder

    .fromNonFlow { key !->

    api.fetchUser(key).user

    }.build()
    導入初期は Persister を指定しないで、On Memory キャッシュとしてだけ
    使うのを試してもいいかもしれません。

    View Slide

  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 内などで行います。

    View Slide

  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() を使ってデータを取得することもできます。

    View Slide

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

    View Slide

  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

    }

    View Slide

  38. おしまい..

    View Slide

  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

    View Slide

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

    View Slide