Skip to content

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.RoleWithMovie
data 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.map
class 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.RoleWithActor
data 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.Flow
interface 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.RatingWithMovies
data 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() }, )