Is it ok to use interfaces as domain models instead of data classes on Android using Kotlin? (Clean Architecture and Domain Driven Design)

Viewed 485

I'm trying to apply clean architecture on my android projects, but it is quite complex, now I'm stuck on this, according to Uncle Bob you should use a model for every layer (domain, data, presentation - in this case), so, typically we create a data class for the domain layer, let's say "Movie":

data class Movie(
    private val id: Int = 0,
    private val name: String = "",
    private val imageUrl: String = ""
)

So, later in our presentation layer we should have a model to be able to retrieve data from the network, we'll name it MovieDto and it looks like this:

data class MovieDto(
    @Json(name = "id")
    private val id: Int = 0,
    @Json(name = "name")
    private val name: String = "",
    @Json(name = "image_url")
    private val imageUrl: String = ""
)

Then every time we fetch data using Retrofit we should be getting a MovieDto object in return, but our MovieRepository in our domain layer looks this way:

interface RepositoryMovies {
    suspend fun getMovie(id: Int): Movie
}

With the previous code we already stablished a contract agreeing to always return a Movie object (remember that inner layers should not know anything about outter layers, so our repository doesn't even know a MovieDto class exists), in order to be able to return a Movie object the repository should use a mapper to convert/map the MovieDto object comming from Retrofit to a Movie object. To avoid all of that I used an interface as my domain model, let me show you:

interface IMovie {
    val id: Int,
    val name: String,
    val imageUrl: String
}

The only thing we have to do next is to implement the IMovie interface on our MovieDto class:

data class MovieDto(
    @Json(name = "id")
    private val id: Int = 0,
    @Json(name = "name")
    private val name: String = "",
    @Json(name = "image_url")
    private val imageUrl: String = ""
) : IMovie

Now our repository should return a IMovie interface instead:

interface RepositoryMovies {
    suspend fun getMovie(id: Int): IMovie
}

With no need of mappers but still forcing "domain rules" by implementing the IMovie interface. I ask you to point me in the right direction, maybe I'm not seeing that this approach is not as flexible or scalable as I tought, what approach would you use? please feel free to comment any pros and cons of each approach. Thank you for your time.

0 Answers
Related