2

I am desperately looking for a possability to convert a room model to a domain model to enable offline functionality to my app. I have successfully implemented this pattern as described in this codelab: https://codelabs.developers.google.com/codelabs/kotlin-android-training-repository/#0

But I use in my domain model the Paging library -> PagedList<com.trashmaster.muellmister.network.Place> and I need it to convert to PagedList<com.trashmaster.muellmister.data.Place> to insert data. enter image description here

In my ViewModel on the other side I need (convert room model to network model.

var properties: LiveData<PagedList<com.trashmaster.muellmister.network.Place>> (ViewModel)

Is there some kind best practices? Is it possible to merge network data model with room data model?

Online functionality works flawlessly but implementing offline functionality with Paging datasource in combination is hard.

Here is the path to my source

https://gist.github.com/DoomMortal/fa2c243d13d72a37988d12096cf8312a

Any help would be appreciated.

Thanks in advance

Biscuit
  • 4,840
  • 4
  • 26
  • 54

2 Answers2

3

Look at this link. As you can see in the link they used a mapper:

fun List<DatabaseVideo>.asDomainModel(): List<DevByteVideo> {
   return map {
       DevByteVideo(
               url = it.url,
               title = it.title,
               description = it.description,
               updated = it.updated,
               thumbnail = it.thumbnail)
   }
}

They are creating a domain model from a database model with this extension function and that's what you're missing

Biscuit
  • 4,840
  • 4
  • 26
  • 54
  • Thanks for your help. Yes I already tried it with that mapper but unfortunately map returns only a list of DevByteVideo´s and not a PagedList of DevByteVideo´s. I found out that I can merge both models into one with annotations from both without any compile errors. That schould not make any problems right? – Max Mustermann Jul 08 '20 at 21:32
  • 1
    In some cases `domain model` and `database model` are similar, in that case I think there is no point in creating a `domain model`, you should have a look at this post https://stackoverflow.com/questions/47903739/android-app-clean-architecture-should-data-layer-have-its-own-model-classes – Biscuit Jul 08 '20 at 22:13
  • Thanks for your help. I continued this approach but now I see a strange behaviour that retrieved data are not written into local database. There are no errors and in Android Studio 4.1 Database inspector are no data in the database. Updated my gist. The new Entity-Model is "MainModel" and I update the local db with "bufferPlaces" located in Repository accessed via ViewModel. – Max Mustermann Jul 08 '20 at 23:12
  • This mapper is the right way to convert between the models. The reason there are multiple "duplicate" looking models is because in Clean Architecture (which this pattern is from), the domain doesn't know about any specific framework. The mapper isolates the rest of your code from "Room" specific code, or "Retrofit" specific code. If any of your external dependencies are updated or deprecated, then only your data layer and the mapper need to be updated, not your domain or presentation code. That means you only update 1/3 of your app. It's for long lived projects, or changing ones. – Kabliz Sep 02 '21 at 04:39
  • @Biscuit there's always a point for seperating domain and database model – DennisVA Sep 28 '21 at 18:45
0

In my opinion, the best practice is as @Biscuit already answered, to write a mapper, which maps the object from your database to an easy to use model (your MainModel).
It also separates your code more, to follow clean architecture - separation of concerns.

The code you provided.

So first of all, I would move the code from MainModel.kt to a data class, which represents your Dao-response. Let's call it "DaoResultModel.kt".

@Entity
data class MainModel(
    @PrimaryKey @Json(name = "id") val id: Int,
    @Json(name = "userId") val userId: String,
    @Json(name = "ortId") val ortId: String,
    @Json(name = "plz") val plz: String,
    @Json(name = "name") val name: String,
    @Json(name = "picture") val picture: String,
    @Json(name = "streetId") val streetId: Int,
    @Json(name = "streetName") val streetName: String
)

This way, you may keep the MainModel class, and just remove the annotations.

data class MainModel(
    val id: Int,
    val userId: String,
    val ortId: String,
    val plz: String,
    val name: String,
    val picture: String,
    val streetId: Int,
    val streetName: String
)

Now write the mapper. Let's call it "DaoResultMapper.kt". This mapper defines an extension function, which maps your response from the dao to your MainModel.
Don't wrap it in a class. Just insert the functions, so they are still associated with your DaoResultModel.

fun DaoResultModel.mapToEntity() = MainModel(
    id = id,
    userId = userId,
    ortId = ortId,
    plz = plz,
    name = name,
    picture = picture,
    streetId = streetId,
    streetName = streetName
)

Now you call your function, after your database request.

var properties: LiveData<PagedList<com.trashmaster.muellmister.network.Place>> 

properties.value =
    videosRepository.data.mapToEntity()

At this point I'm not sure, because you're expecting a PagedList, so you might have to map every entry with mapToEntity(), but for that I'm missing your complete code.

Hope it helps!

Soul
  • 1