Movies Database
Repository Types
Now the basics of the repository
module.
DTOs
We create Data Transfer Objects
(ActorDto
,
MovieDto
, and
RatingDto
)
to abstract and restrict how the data is used.
These DTOs are immutable, which will help Jetpack Compose optimize UI updates.
We also define extension functions
(such as these)
to convert between the entities defined in the data
module and the DTOs we expose
from this repository
module.
Note that the extension functions are marked internal
. This makes them accessible anywhere inside the repository
module, but not outside the module.
MovieRepository
The MovieRepository
interface defines how we communicate with a repository.
This allows different repository implementations (later we'll add a web-service implementation). Note that I'm not using vararg
in the MovieRepository
. This is because it simplifies our web services implementation when we do it later.
MovieDatabaseRepository
This is the concrete implementation of MovieRepository
that we use to work with the Room database.
Much of it is direct passthrough to the DAO.
The query functions that expose a Flow
transform the returned entities into DTOs.
The map
function on Flow
creates a new flow that calls the nested map
on the
list of entities to convert them into DTOs.
Code Changes
ADDED: /repository/src/main/java/com/androidbyexample/movie/repository/ActorDto.kt
package com.androidbyexample.movie.repositoryimport com.androidbyexample.movie.data.ActorEntityimport com.androidbyexample.movie.data.ActorWithFilmographyimport com.androidbyexample.movie.data.RoleWithMoviedata class ActorDto( val id: String, val name: String,)internal fun ActorEntity.toDto() = ActorDto(id = id, name = name)internal fun ActorDto.toEntity() = ActorEntity(id = id, name = name)data class ActorWithFilmographyDto( val actor: ActorDto, val filmography: List<RoleWithMovieDto>,)data class RoleWithMovieDto( val movie: MovieDto, val character: String, val orderInCredits: Int,)internal fun RoleWithMovie.toDto() = RoleWithMovieDto( movie = movie.toDto(), character = role.character, orderInCredits = role.orderInCredits, )internal fun ActorWithFilmography.toDto() = ActorWithFilmographyDto( actor = actor.toDto(), filmography = rolesWithMovies.map { it.toDto() } )
ADDED: /repository/src/main/java/com/androidbyexample/movie/repository/MovieDatabaseRepository.kt
package com.androidbyexample.movie.repositoryimport android.content.Contextimport com.androidbyexample.movie.data.MovieDaoimport com.androidbyexample.movie.data.createDaoimport kotlinx.coroutines.flow.mapclass MovieDatabaseRepository( private val dao: MovieDao): MovieRepository { override val ratingsFlow = dao.getRatingsFlow() .map { ratings ->// for each List<RatingEntity> that's emitted // create a list of RatingDto ratings.map { rating -> rating.toDto() } // map each entity to Dto } override val moviesFlow = dao.getMoviesFlow() .map { movies -> movies.map { it.toDto() } } override val actorsFlow = dao.getActorsFlow() .map { actors -> actors.map { it.toDto() } } override suspend fun getRatingWithMovies(id: String): RatingWithMoviesDto = dao.getRatingWithMovies(id).toDto() override suspend fun getMovieWithCast(id: String): MovieWithCastDto = dao.getMovieWithCast(id).toDto() override suspend fun getActorWithFilmography(id: String): ActorWithFilmographyDto = dao.getActorWithFilmography(id).toDto() override suspend fun insert(movie: MovieDto) = dao.insert(movie.toEntity()) override suspend fun insert(actor: ActorDto) = dao.insert(actor.toEntity()) override suspend fun insert(rating: RatingDto) = dao.insert(rating.toEntity()) override suspend fun resetDatabase() = dao.resetDatabase()}
ADDED: /repository/src/main/java/com/androidbyexample/movie/repository/MovieDto.kt
package com.androidbyexample.movie.repositoryimport com.androidbyexample.movie.data.MovieEntityimport com.androidbyexample.movie.data.MovieWithCastimport com.androidbyexample.movie.data.RoleWithActordata class MovieDto( val id: String, val title: String, val description: String, val ratingId: String,)internal fun MovieEntity.toDto() = MovieDto(id = id, title = title, description = description, ratingId = ratingId)internal fun MovieDto.toEntity() = MovieEntity(id = id, title = title, description = description, ratingId = ratingId)data class MovieWithCastDto( val movie: MovieDto, val cast: List<RoleWithActorDto>,)data class RoleWithActorDto( val actor: ActorDto, val character: String, val orderInCredits: Int,)internal fun RoleWithActor.toDto() = RoleWithActorDto( actor = actor.toDto(), character = role.character, orderInCredits = role.orderInCredits, )internal fun MovieWithCast.toDto() = MovieWithCastDto( movie = movie.toDto(), cast = rolesWithActors.map { it.toDto() } )
ADDED: /repository/src/main/java/com/androidbyexample/movie/repository/MovieRepository.kt
package com.androidbyexample.movie.repositoryimport kotlinx.coroutines.flow.Flowinterface MovieRepository { val ratingsFlow: Flow<List<RatingDto>> val moviesFlow: Flow<List<MovieDto>> val actorsFlow: Flow<List<ActorDto>> suspend fun getRatingWithMovies(id: String): RatingWithMoviesDto suspend fun getMovieWithCast(id: String): MovieWithCastDto suspend fun getActorWithFilmography(id: String): ActorWithFilmographyDto suspend fun insert(movie: MovieDto) suspend fun insert(actor: ActorDto) suspend fun insert(rating: RatingDto) suspend fun resetDatabase()}
ADDED: /repository/src/main/java/com/androidbyexample/movie/repository/RatingDto.kt
package com.androidbyexample.movie.repositoryimport com.androidbyexample.movie.data.RatingEntityimport com.androidbyexample.movie.data.RatingWithMoviesdata class RatingDto( val id: String, val name: String, val description: String,)internal fun RatingEntity.toDto() = RatingDto(id = id, name = name, description = description)internal fun RatingDto.toEntity() = RatingEntity(id = id, name = name, description = description)data class RatingWithMoviesDto( val rating: RatingDto, val movies: List<MovieDto>,)// only need the toDto(); we don't use this to do database updatesinternal fun RatingWithMovies.toDto() = RatingWithMoviesDto( rating = rating.toDto(), movies = movies.map { it.toDto() }, )