Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Clean up your data repository

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on. →

Clean up your data repository

A presentation about Architecture and how to keep your data layer organized.
Droidcon Turin 2019

Avatar for Alessandro Recchi

Alessandro Recchi

April 05, 2019

Other Decks in Programming

Transcript

  1. Copyright © 2019 Accenture All rights reserved. Who we are

    Alessandro Recchi Antonello Fodde Android Software Engineer Android Software Engineer “One line of code, one ⌘ + ⌥ + L to clean it up” “Everything that Antonello says it’s probably wrong”
  2. Copyright © 2019 Accenture All rights reserved. C I want

    this page to load faster. How can we do it? A C Sure. The page is pretty new and uses a modern architecture. Let’s put a DB as a cache. It will take no time Nice!
  3. Copyright © 2019 Accenture All rights reserved. @Entity(indices = arrayOf(

    Index(value = arrayOf("uniqueKey"), unique = true ))) data class Entry( @PrimaryKey(autoGenerate = true) val entryId: Int, var uniqueKey: String?, var storeId: String?, @ColumnInfo(name = "entry_name") val name: String?, @JsonAdapter(StringFromUrlObjDeserializer::class) val image: String?, @ColumnInfo(name = "entry_code") var code: String?, @Embedded val color: Color?, val variants: List<Variant>?, @SerializedName("productMarker") @Embedded(prefix = "product_") val productMarker: Marker?, @SerializedName("showPrice") val showPriceMarker: Boolean, @Embedded(prefix = "ordinary_") val price: Price?, ) : AbstractComponentModel() { fun createUniqueIndex() { uniqueKey = code + "_" + storeId } } What went wrong...
  4. Copyright © 2019 Accenture All rights reserved. @Entity(indices = arrayOf(

    Index(value = arrayOf("uniqueKey"), unique = true ))) data class Entry( @PrimaryKey(autoGenerate = true) val entryId: Int, var uniqueKey: String?, var storeId: String?, @ColumnInfo(name = "entry_name") val name: String?, @JsonAdapter(StringFromUrlObjDeserializer::class) val image: String?, @ColumnInfo(name = "entry_code") var code: String?, @Embedded val color: Color?, val variants: List<Variant>?, @SerializedName("productMarker") @Embedded(prefix = "product_") val productMarker: Marker?, @SerializedName("showPrice") val showPriceMarker: Boolean, @Embedded(prefix = "ordinary_") val price: Price?, ) : AbstractComponentModel() { fun createUniqueIndex() { uniqueKey = code + "_" + storeId } } Gson
  5. Copyright © 2019 Accenture All rights reserved. @Entity(indices = arrayOf(

    Index(value = arrayOf("uniqueKey"), unique = true ))) data class Entry( @PrimaryKey(autoGenerate = true) val entryId: Int, var uniqueKey: String?, var storeId: String?, @ColumnInfo(name = "entry_name") val name: String?, @JsonAdapter(StringFromUrlObjDeserializer::class) val image: String?, @ColumnInfo(name = "entry_code") var code: String?, @Embedded val color: Color?, val variants: List<Variant>?, @SerializedName("productMarker") @Embedded(prefix = "product_") val productMarker: Marker?, @SerializedName("showPrice") val showPriceMarker: Boolean, @Embedded(prefix = "ordinary_") val price: Price?, ) : AbstractComponentModel() { fun createUniqueIndex() { uniqueKey = code + "_" + storeId } } Room
  6. Copyright © 2019 Accenture All rights reserved. private fun deleteItemFromList(code:

    String) { repository.deleteItem(code) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ myFavouriteRepository.deleteItemDB(code) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({}, {}) }, {}) } Leaked responsibilities
  7. Copyright © 2019 Accenture All rights reserved. Leaked responsibilities private

    fun deleteItemFromList(code: String) { repository.deleteItem(code) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ myFavouriteRepository.deleteItemDB(code) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({}, {}) }, {}) }
  8. Copyright © 2019 Accenture All rights reserved. Separation of Concerns

    Principle “separation of concerns … even if not perfectly possible is yet the only available technique for effective ordering of one’s thoughts” Edsgar Dikjstra, 1974
  9. Copyright © 2019 Accenture All rights reserved. The Clean Architecture

    “Good architecture makes the system easy to understand, easy to develop, easy to maintain, and easy to deploy” Robert C. Martin, Clean Architecture UI Controllers Use Case Entity Repository DB External interfaces Devices Web Presenters
  10. Copyright © 2019 Accenture All rights reserved. The Clean Architecture

    UI Controllers Use Case Entity Repository DB External interfaces Devices Web Presenters
  11. Copyright © 2019 Accenture All rights reserved. The Clean Architecture

    UI Use Case Presenter DataSource Entity Repository
  12. Copyright © 2019 Accenture All rights reserved. The Clean Architecture

    UI Use Case Presenter DataSource Entity Repository
  13. Copyright © 2019 Accenture All rights reserved. The Clean Architecture

    UI Use Case Presenter DataSource Entity Repository
  14. Copyright © 2019 Accenture All rights reserved. The Clean Architecture

    UI Use Case Presenter DataSource Entity Repository
  15. Copyright © 2019 Accenture All rights reserved. Data Flow UI

    Use Case Presenter DataSource Entity Repository
  16. Copyright © 2019 Accenture All rights reserved. UI Presenter Use

    Case Entity Repository Data Source Layers Presentation MVVM View View Model Model
  17. Copyright © 2019 Accenture All rights reserved. UI Presenter Use

    Case Entity Repository Data Source Layers Presentation Domain MVVM View View Model Use Case Entity
  18. Copyright © 2019 Accenture All rights reserved. UI Presenter Use

    Case Entity Repository Data Source Layers Presentation Domain Data MVVM View View Model Use Case Entity Repository Remote Data Source Local Data Source Repository
  19. Copyright © 2019 Accenture All rights reserved. Feature 1 Project

    Structure Data Domain Presentation Screaming Architecture
  20. Copyright © 2019 Accenture All rights reserved. UI Presenter Use

    Case Entity Repository Data Source Dependency Rule “Nothing in an inner circle can know anything at all about something in an outer circle” Robert C. Martin, Clean Architecture
  21. Copyright © 2019 Accenture All rights reserved. UI Presenter Use

    Case Entity Repository Data Source Layers Presentation Domain Data MVVM View View Model Use Case Entity Repository Remote Data Source Local Data Source Repository
  22. Copyright © 2019 Accenture All rights reserved. Inversion of Control

    Domain Use Case Repository Data RepositoryImpl Domain Use Case Data Repository
  23. Copyright © 2019 Accenture All rights reserved. UI Presenter Use

    Case Entity Repository Data Source Layers Presentation Domain Data MVVM View View Model Use Case Entity Repository Remote Data Source Local Data Source Repository
  24. Copyright © 2019 Accenture All rights reserved. UI Presenter Use

    Case Entity Repository Data Source Layers Presentation Domain Data MVVM View View Model Use Case Entity RepositoryImpl Remote Data Source Local Data Source Repository Repository
  25. Copyright © 2019 Accenture All rights reserved. UI Presenter Use

    Case Entity Repository Data Source Data Mappers Presentation Domain Data Network Model DB Model
  26. Copyright © 2019 Accenture All rights reserved. UI Presenter Use

    Case Entity Repository Data Source Data Mappers Presentation Domain Data Domain Model Domain Model asDomainModel Network Model DB Model
  27. Copyright © 2019 Accenture All rights reserved. UI Presenter Use

    Case Entity Repository Data Source Data Mappers Presentation Domain Data Domain Model UI Model Domain Model UI Model asDomainModel asUIModel Network Model DB Model
  28. Copyright © 2019 Accenture All rights reserved. “O Software Architect,

    you must see the future. You must guess-intelligently. You must weigh the costs and determine where the architectural boundaries lie, and which should be fully implemented, and which should be partially implemented, and which should be ignored.” Robert C. Martin, Clean Architecture
  29. Copyright © 2019 Accenture All rights reserved. UI Presenter Use

    Case Entity Repository Data Source Layers Presentation Domain Data MVVM View View Model Use Case Entity Repository Remote Data Source Local Data Source Repository
  30. Copyright © 2019 Accenture All rights reserved. UI Presenter Use

    Case Entity Repository Data Source Layers Presentation Domain Data MVVM View View Model Use Case Entity Repository Remote Data Source Local Data Source Repository
  31. Copyright © 2019 Accenture All rights reserved. UI Presenter Use

    Case Entity Repository Data Source Layers Presentation Domain Data MVVM View View Model Use Case Entity Repository Remote Data Source Local Data Source Repository
  32. Copyright © 2019 Accenture All rights reserved. UI Presenter Use

    Case Entity Repository Data Source Layers Presentation Domain Data MVVM View View Model Use Case Entity Repository Remote Data Source Local Data Source Repository
  33. Copyright © 2019 Accenture All rights reserved. Data Layer Data

    Repository Remote Data Source Local Data Source Repository
  34. Copyright © 2019 Accenture All rights reserved. Data Layer Data

    Repository Remote Data Source Local Data Source Repository
  35. Copyright © 2019 Accenture All rights reserved. Data Layer Data

    Repository Remote Data Source Local Data Source Repository
  36. Copyright © 2019 Accenture All rights reserved. Single Source of

    Truth Single Source of Truth, in short SSOT, refers to the concept where certain data has only one official source to be used by data consumers (i.e. humans and software) for the true current version of that data. Source: https://www.dragon1.com/concepts/single-source-of-truth
  37. Copyright © 2019 Accenture All rights reserved. WANT TO BE

    UPDATED ABOUT ALL JAKE ONLINE ACTIVITIES? YOU WILL NEVER MISS A SINGLE JAKE QUOTE! Showcase App Jakebook “I love Jake!” by @lollipop.lover77 “thanks to this app I became a better developer!” by @honeycomb “The app crashes on Android 9…” by @PisForPankakes
  38. Copyright © 2019 Accenture All rights reserved. class TwitterDataSourceImpl @Inject

    constructor(private val twitterService: TwitterService) : TwitterDataSource override fun getTimeline(firstTweetId: Long?, lastTweetId: Long?, pageSize: Int?): Single<List<Feed>> { } } Single Remote Data Source
  39. Copyright © 2019 Accenture All rights reserved. class TwitterDataSourceImpl @Inject

    constructor(private val twitterService: TwitterService) : TwitterDataSource override fun getTimeline(firstTweetId: Long?, lastTweetId: Long?, pageSize: Int?): Single<List<Feed>> { return twitterService .getUserTimeline(...) } } Single Remote Data Source
  40. Copyright © 2019 Accenture All rights reserved. class TwitterDataSourceImpl @Inject

    constructor(private val twitterService: TwitterService) : TwitterDataSource override fun getTimeline(firstTweetId: Long?, lastTweetId: Long?, pageSize: Int?): Single<List<Feed>> { return twitterService .getUserTimeline(...) } } Single Remote Data Source
  41. Copyright © 2019 Accenture All rights reserved. @GET("https://api.twitter.com/1.1/statuses/user_timeline.json") fun getUserTimeline(

    @Header("Authorization") auth: String, @Query("screen_name") name: String? = null, @Query("count") totalResults: Int? = null, @Query("max_id") resultFromId: Long? = null, @Query("since_id") resultBeforeId: Long? = null ): Single<List<TwitterUserTimelineItem>> Single Remote Data Source
  42. Copyright © 2019 Accenture All rights reserved. @GET("https://api.twitter.com/1.1/statuses/user_timeline.json") fun getUserTimeline(

    @Header("Authorization") auth: String, @Query("screen_name") name: String? = null, @Query("count") totalResults: Int? = null, @Query("max_id") resultFromId: Long? = null, @Query("since_id") resultBeforeId: Long? = null ): Single<List<TwitterUserTimelineItem>> Single Remote Data Source
  43. Copyright © 2019 Accenture All rights reserved. Single Remote Data

    Source data class TwitterUserTimelineItem( val id: Long, val created_at: String?, val text: String?, val user: User? ) data class Feed( val id: String, val image: String, val name: String, val content: String, val date: Date, val source: FeedSource ) TwitterUserTimelineItem.asDomainModel() Domain Data
  44. Copyright © 2019 Accenture All rights reserved. Single Remote Data

    Source fun TwitterUserTimelineItem.asDomainModel() = Feed( id = "twitter_$id", image = user?.profile_image_url_https ?: "", name = user?.screen_name ?: "", content = text ?: "", date = created_at?.let { TWITTER_DATE_FORMAT.parse(it) } ?: Date(0), source = TWITTER ) Domain Data
  45. Copyright © 2019 Accenture All rights reserved. class TwitterDataSourceImpl @Inject

    constructor(private val twitterService: TwitterService) : TwitterDataSource override fun getTimeline(firstTweetId: Long?, lastTweetId: Long?, pageSize: Int?): Single<List<Feed>> { return twitterService .getUserTimeline(...) } } Single Remote Data Source
  46. Copyright © 2019 Accenture All rights reserved. class TwitterDataSourceImpl @Inject

    constructor(private val twitterService: TwitterService) : TwitterDataSource override fun getTimeline(firstTweetId: Long?, lastTweetId: Long?, pageSize: Int?): Single<List<Feed>> { return twitterService .getUserTimeline(...) .map { list -> list.map { it.asDomainModel() } } } } Single Remote Data Source
  47. Copyright © 2019 Accenture All rights reserved. Single Remote Data

    Source class SingleSourceFeedRepository @Inject constructor( private val twitterRemoteDataSource: TwitterDataSource, private val roomLocalDataSource: LocalDataSource ) : FeedRepository() { override fun getFeed(): Flowable<List<Feed>> { refreshFeed() return roomLocalDataSource.getFeed() } override fun refreshFeed() { twitterRemoteDataSource.getTimeline() .subscribe({ roomLocalDataSource.insertFeeds(it) }, { ... }).bindToLifecycle(this) } }
  48. Copyright © 2019 Accenture All rights reserved. Single Remote Data

    Source class SingleSourceFeedRepository @Inject constructor( private val twitterRemoteDataSource: TwitterDataSource, private val roomLocalDataSource: LocalDataSource ) : FeedRepository() { override fun getFeed(): Flowable<List<Feed>> { refreshFeed() return roomLocalDataSource.getFeed() } override fun refreshFeed() { twitterRemoteDataSource.getTimeline() .subscribe({ roomLocalDataSource.insertFeeds(it) }, { ... }).bindToLifecycle(this) } }
  49. Copyright © 2019 Accenture All rights reserved. Single Remote Data

    Source class SingleSourceFeedRepository @Inject constructor( private val twitterRemoteDataSource: TwitterDataSource, private val roomLocalDataSource: LocalDataSource ) : FeedRepository() { override fun getFeed(): Flowable<List<Feed>> { refreshFeed() return roomLocalDataSource.getFeed() } override fun refreshFeed() { twitterRemoteDataSource.getTimeline() .subscribe({ roomLocalDataSource.insertFeeds(it) }, { ... }).bindToLifecycle(this) } }
  50. Copyright © 2019 Accenture All rights reserved. Single Remote Data

    Source class SingleSourceFeedRepository @Inject constructor( private val twitterRemoteDataSource: TwitterDataSource, private val roomLocalDataSource: LocalDataSource ) : FeedRepository() { override fun refreshFeed() { ... } }
  51. Copyright © 2019 Accenture All rights reserved. class DoubleSourceFeedRepository @Inject

    constructor( private val twitterRemoteDataSource: TwitterDataSource, private val gitHubRemoteDataSource: GitHubDataSource, private val roomLocalDataSource: LocalDataSource ) : FeedRepository() { override fun refreshFeed() { ... } } Double Remote Data Source
  52. Copyright © 2019 Accenture All rights reserved. class DoubleSourceFeedRepository @Inject

    constructor( private val twitterRemoteDataSource: TwitterDataSource, private val gitHubRemoteDataSource: GitHubDataSource, private val roomLocalDataSource: LocalDataSource ) : FeedRepository() { override fun refreshFeed() { Single.zip( twitterRemoteDataSource.getTimeline(), gitHubRemoteDataSource.getPage(), BiFunction<List<Feed>, List<Feed>, List<Feed>> { twitterFeeds, githubFeeds -> twitterFeeds.plus(githubFeeds) }) .subscribe({ roomLocalDataSource.insertFeeds(it) }, { ... }).bindToLifecycle(this) } } Double Remote Data Source
  53. Copyright © 2019 Accenture All rights reserved. class DoubleSourceFeedRepository @Inject

    constructor( private val twitterRemoteDataSource: TwitterDataSource, private val gitHubRemoteDataSource: GitHubDataSource, private val roomLocalDataSource: LocalDataSource ) : FeedRepository() { override fun refreshFeed() { Single.zip( twitterRemoteDataSource.getTimeline(), gitHubRemoteDataSource.getPage(), BiFunction<List<Feed>, List<Feed>, List<Feed>> { twitterFeeds, githubFeeds -> twitterFeeds.plus(githubFeeds) }) .subscribe({ roomLocalDataSource.insertFeeds(it) }, { ... }).bindToLifecycle(this) } } Double Remote Data Source
  54. Copyright © 2019 Accenture All rights reserved. class DoubleSourceFeedRepository @Inject

    constructor( private val twitterRemoteDataSource: TwitterDataSource, private val gitHubRemoteDataSource: GitHubDataSource, private val roomLocalDataSource: LocalDataSource ) : FeedRepository() { override fun refreshFeed() { Single.zip( twitterRemoteDataSource.getTimeline(), gitHubRemoteDataSource.getPage(), BiFunction<List<Feed>, List<Feed>, List<Feed>> { twitterFeeds, githubFeeds -> twitterFeeds.plus(githubFeeds) }) .subscribe({ roomLocalDataSource.insertFeeds(it) }, { ... }).bindToLifecycle(this) } } Double Remote Data Source
  55. Copyright © 2019 Accenture All rights reserved. class TwitterPagedDataSource @Inject

    constructor(twitterService: TwitterService) : ItemKeyedDataSource<String, Feed>(), TwitterDataSource by TwitterDataSourceImpl(twitterService) { override fun loadInitial(params: LoadInitialParams<String>, callback: LoadInitialCallback<Feed>) { } override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<Feed>) { } override fun loadBefore(params: LoadParams<String>, callback: LoadCallback<Feed>) { } } Paged Data Source
  56. Copyright © 2019 Accenture All rights reserved. class TwitterPagedDataSource @Inject

    constructor(twitterService: TwitterService) : ItemKeyedDataSource<String, Feed>(), TwitterDataSource by TwitterDataSourceImpl(twitterService) { override fun loadInitial(params: LoadInitialParams<String>, callback: LoadInitialCallback<Feed>) { } override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<Feed>) { } override fun loadBefore(params: LoadParams<String>, callback: LoadCallback<Feed>) { } } Paged Data Source
  57. Copyright © 2019 Accenture All rights reserved. class TwitterPagedDataSource @Inject

    constructor(twitterService: TwitterService) : ItemKeyedDataSource<String, Feed>(), TwitterDataSource by TwitterDataSourceImpl(twitterService) { override fun loadInitial(params: LoadInitialParams<String>, callback: LoadInitialCallback<Feed>) { remoteCall(params, callback) } override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<Feed>) { remoteCall(params, callback) } override fun loadBefore(params: LoadParams<String>, callback: LoadCallback<Feed>) { remoteCall(params, callback) } } Paged Data Source
  58. Copyright © 2019 Accenture All rights reserved. class RemotePagedRepository @Inject

    constructor( private val twitterPagedDataSource: TwitterPagedDataSource ) : PagedRepository() { override fun getPagedFeed(): LiveData<PagedList<Feed>> = object : DataSource.Factory<String, Feed>() { override fun create(): DataSource<String, Feed> = twitterPagedDataSource } .toLiveData(config) } Paged Data Source
  59. Copyright © 2019 Accenture All rights reserved. class RemotePagedRepository @Inject

    constructor( private val twitterPagedDataSource: TwitterPagedDataSource ) : PagedRepository() { override fun getPagedFeed(): LiveData<PagedList<Feed>> = object : DataSource.Factory<String, Feed>() { override fun create(): DataSource<String, Feed> = twitterPagedDataSource } .toLiveData(config) } Paged Data Source
  60. Copyright © 2019 Accenture All rights reserved. class RemotePagedRepository @Inject

    constructor( private val twitterPagedDataSource: TwitterPagedDataSource ) : PagedRepository() { override fun getPagedFeed(): LiveData<PagedList<Feed>> = object : DataSource.Factory<String, Feed>() { override fun create(): DataSource<String, Feed> = twitterPagedDataSource } .toLiveData(config) } Paged Data Source
  61. Copyright © 2019 Accenture All rights reserved. class RemotePagedRepository @Inject

    constructor( private val twitterPagedDataSource: TwitterPagedDataSource ) : PagedRepository() { override fun <O> getPagedFeed(mapper: (Feed) -> (O)): LiveData<PagedList<O>> = object : DataSource.Factory<String, Feed>() { override fun create(): DataSource<String, Feed> = twitterPagedDataSource }.map { i -> mapper.invoke(i) } .toLiveData(config) } Paged Data Source
  62. Copyright © 2019 Accenture All rights reserved. abstract class PagedViewModel

    constructor(pagedRepository: PagedRepository) : LifecycleViewModel() { val pagedFeed: LiveData<PagedList<UIFeedItem>> = pagedRepository.getPagedFeed { it.asUIModel() } } Paged Data Source
  63. Copyright © 2019 Accenture All rights reserved. abstract class PagedViewModel

    constructor(pagedRepository: PagedRepository) : LifecycleViewModel() { val pagedFeed: LiveData<PagedList<UIFeedItem>> = pagedRepository.getPagedFeed { it.asUIModel() } } Paged Data Source
  64. Copyright © 2019 Accenture All rights reserved. Paging with Room

    @Dao interface RoomFeedDao { @Query("SELECT * FROM feed ORDER BY date desc") fun getFeed(): Flowable<List<RoomFeed>> @Query("SELECT * FROM feed ORDER BY date desc") fun getAllPaged(): DataSource.Factory<Int, RoomFeed> @Query("DELETE FROM feed") fun deleteAll() @Insert(onConflict = OnConflictStrategy.IGNORE) fun insertFeed(feed: RoomFeed) @Insert(onConflict = OnConflictStrategy.IGNORE) fun insertFeedList(data: List<RoomFeed>?) } interface LocalDataSource { fun getFeed(): Flowable<List<Feed>> fun insertFeeds(list: List<Feed>) fun getPagedFeeds(): DataSource.Factory<Int, Feed> }
  65. Copyright © 2019 Accenture All rights reserved. interface LocalDataSource {

    fun getFeed(): Flowable<List<Feed>> fun insertFeeds(list: List<Feed>) fun getPagedFeeds(): DataSource.Factory<Int, Feed> } Paging with Room @Dao interface RoomFeedDao { @Query("SELECT * FROM feed ORDER BY date desc") fun getFeed(): Flowable<List<RoomFeed>> @Query("SELECT * FROM feed ORDER BY date desc") fun getAllPaged(): DataSource.Factory<Int, RoomFeed> @Query("DELETE FROM feed") fun deleteAll() @Insert(onConflict = OnConflictStrategy.IGNORE) fun insertFeed(feed: RoomFeed) @Insert(onConflict = OnConflictStrategy.IGNORE) fun insertFeedList(data: List<RoomFeed>?) }
  66. Copyright © 2019 Accenture All rights reserved. class LocalPagedRepository @Inject

    constructor( private val twitterRemoteDataSource: TwitterDataSource, private val roomLocalDataSource: LocalDataSource ) : PagedRepository() { override fun <O> getPagedFeed(mapper: (Feed) -> (O)): LiveData<PagedList<O>> { return roomLocalDataSource.getPagedFeeds() } } Paging with Room
  67. Copyright © 2019 Accenture All rights reserved. class LocalPagedRepository @Inject

    constructor( private val twitterRemoteDataSource: TwitterDataSource, private val roomLocalDataSource: LocalDataSource ) : PagedRepository() { override fun <O> getPagedFeed(mapper: (Feed) -> (O)): LiveData<PagedList<O>> { return roomLocalDataSource.getPagedFeeds() .map { i -> mapper.invoke(i) } .toLiveData( config) } } Paging with Room
  68. Copyright © 2019 Accenture All rights reserved. class LocalPagedRepository @Inject

    constructor( private val twitterRemoteDataSource: TwitterDataSource, private val roomLocalDataSource: LocalDataSource ) : PagedRepository() { override fun <O> getPagedFeed(mapper: (Feed) -> (O)): LiveData<PagedList<O>> { return roomLocalDataSource.getPagedFeeds() .map { i -> mapper.invoke(i) } .toLiveData( config, boundaryCallback = object : PagedList.BoundaryCallback<O>() { } ) } } Paging with Room
  69. Copyright © 2019 Accenture All rights reserved. class LocalPagedRepository @Inject

    constructor( private val twitterRemoteDataSource: TwitterDataSource, private val roomLocalDataSource: LocalDataSource ) : PagedRepository() { override fun <O> getPagedFeed(mapper: (Feed) -> (O)): LiveData<PagedList<O>> { return roomLocalDataSource.getPagedFeeds() .map { i -> mapper.invoke(i) } .toLiveData( config, boundaryCallback = object : PagedList.BoundaryCallback<O>() { override fun onZeroItemsLoaded() { fetchNextFeed() } override fun onItemAtEndLoaded(lastItem: O) { fetchNextFeed(lastFeedId) } } ) } } Paging with Room
  70. Copyright © 2019 Accenture All rights reserved. Give us the

    code https://github.com/alerecchi/droidcon-clean-repository Jakebook Repository:
  71. Copyright © 2019 Accenture All rights reserved. Get Connected https://twitter.com/_alerecchi

    https://twitter.com/m3rcvp https://github.com/alerecchi https://github.com/M3rcur14l