1

I'm looking a way to improve the mapping mechanisms between the model classes from the different layers in my App.

I've model classes, the ones that I use in my App, in the business logic layer such as a Presenter. For example a User class.

I've server-side entities, these are the ones that I use to deserialize the responses from the server-side endpoints, through Retrofit or any other technology. These ones usually contain the Gson annotations for the fields. For example a ApiUserResponse class.

And I've DTO entities, these are the ones that represent a DB table in my App. I'm using Realm right now, but I've worked with ORMlite and Room. These types of classes also contain DB related annotations. For example a UserDTO class.

My cache repositories (the ones that fetch data from the DB) and my network repositories (the ones that fetch data from the server-side) both expose RxJava methods, returning the responses wrapped in Observable classes.

What I'm doing right now to parse the DTO entities and the server-side entities is add an extended function to the Observable class which receives a functions as argument. And that's the mechanism that I'm using to parse the DTOs and server-side entities into model classes. For example:

myLoginRepository.getUser("someId")
    .mapTo(::myMappingFunction)

...

fun myMappingFunction(userResponse: ApiUserResponse): User {
    return User(userResponse.id, userResponse.name, userResponse.lastname)
}

Internally the only thing that the mapTo extended function does is using a flatMap to flat the Observable stream and using a map to parse the collection.

I think that this is a pretty good way to implement the mapping between model-related classes, but I'm looking a way to improve it or do something completely different but that require less coding. Obviously this example is pretty straight forward, but currently I'm having a Mappings.kt file which is growing like crazy with each new mapping function.

Any suggestion?

4gus71n
  • 3,717
  • 3
  • 39
  • 66

3 Answers3

1

Maybe http://modelmapper.org/ is something for you. This way you can spare your own mapping functions and you can even specify some sophisticated mapping functions. However it may cost you something in regards to performance (looks like reflection access all the way?).

Note: I didn't use the library myself yet, but it looks promising enough to recommend it. In the last project I helped out, they built their own modelmapper which probably wasn't such a good idea (lots of corner cases forgotten, which then led to strange behaviour later on or manually mapped the wrong fields; however your variant can suffer from that too).

Just found another article regarding mapping frameworks and their performance: http://www.baeldung.com/java-performance-mapping-frameworks doesn't look that good for ModelMapper

You may also just want to generate your mapping functions. Similar to what you have now. Or just use a live template for it?

Manually writing your mappings may sooner or later introduce mistakes you are not identifying that easily anymore, except you test them all accordingly. Which you do, right? ;-)

Roland
  • 22,259
  • 4
  • 57
  • 84
  • Yeah, f*** it, seems that there's no other way around other than having the mappings like I have now. And yes, I have UT :) – 4gus71n Jul 23 '18 at 14:01
  • If you want to keep your DTOs and you don't want to use reflection or any of the mentioned frameworks then no... no easy way ;-) If reflection would be ok for you, then there might be an "easy-to-use" (not necessarily easy-to-write) approach ;-) – Roland Jul 23 '18 at 14:08
  • Nah, is just like you said, if you start using this reflaction-based libraries, then you are open for bugs, plus the performance is going to go down. I prefer to stick with what I'm doing right now. – 4gus71n Jul 23 '18 at 14:33
  • Depending on what you use it for, that might be an acceptable approach. Sometimes it's more convenient to stick with a library then to write tons of mapping functions. However: as soon as you stick to any model mapping utility or to your mapping functions you probably don't question your DTOs anymore (amount/usefulness). Don't weight the performance too early. You already know an approach that might be more performant, but your Mapping.kt is going to be bloated. You can probably even generate a performant one later on. I would start with the the most convenient way and only switch if necessary – Roland Jul 23 '18 at 14:55
0

You can convert your DTO object to entity or vice versa with converter.

Converter

ConversionService

You can implement Converter interface,It is a functional interface and you must implement convert method, then convert your DTO to entity, Where you want to convert them, just you should inject ConversionService and use convert method.

-6

You should only have one class that representes both the entities and the model class from the network

If you want to use Room database and Retrofit for Api calls you should use like this

@Entity("tableName = something")
data class Something{

    @SerializedName("name")
    val name : String,
    @SerializedName("number")
    val number : Int}
sushildlh
  • 8,986
  • 4
  • 33
  • 77
José Nobre
  • 4,407
  • 6
  • 20
  • 40
  • And what if the server-side responses are completely different from the DB model. I cannot build the DB relationships based on the responses from the server-side. What if I have a `schedules` field in my UserDTO but the schedules are fetched from a completely different endpint. And that's a simple case. I want to map my DB relationships in a proper manner. And I don't wanna have my DB mapping based on the server-side response (which may change) – 4gus71n Jul 20 '18 at 22:56
  • Imagine that I've an ImageGallery collection which has a collection of Tag classes, and I also have an Image collection within the ImageGallery, and each Image class also contains a collection of Tags. Having the DB mapping and the server-side annotations for a model like that is literally impossible since you need to have many-to-many middle classes like ImageGalleryTag and ImageTag. – 4gus71n Jul 20 '18 at 22:56
  • For that you should use a mapper in order to save what you want in the database :) – José Nobre Jul 21 '18 at 10:11