Data layer modules for local storage, remote API, repositories, session management, and data sources
The data layer implements the domain layer’s gateways and handles all data operations. It’s organized into specialized modules for different data sources and concerns.
File:data/local/src/main/java/es/mobiledev/data/local/article/dao/ArticleDao.kt:11Data Access Object for article operations.
@Daointerface ArticleDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun saveFavoriteArticle(article: ArticleDbo) @Delete suspend fun removeFavoriteArticle(article: ArticleDbo) @Query("SELECT * FROM articles") suspend fun getFavoriteArticles(): List<ArticleDbo> @Query("SELECT * FROM articles WHERE id = :articleId") suspend fun getFavoriteArticleById(articleId: Long): ArticleDbo?}
@Entity(tableName = "articles")data class ArticleDbo( @PrimaryKey val id: Long, val title: String, val authors: String, // JSON serialized val url: String, val imageUrl: String, val newsSite: String, val summary: String, val publishedAt: String, val updatedAt: String,)
The “Dbo” suffix stands for “Database Object”. These objects are separate from domain models (Bo) to maintain separation of concerns.
data class ArticleDto( val id: Long, val title: String, val authors: List<AuthorDto>, val url: String, val imageUrl: String, val newsSite: String, val summary: String, val publishedAt: String, val updatedAt: String,)
The “Dto” suffix stands for “Data Transfer Object”. These objects represent the API’s JSON structure and are mapped to domain models (Bo) before reaching the domain layer.
class ArticleRepository( private val remote: ArticleRemoteDataSource, private val local: ArticleLocalDataSource,) : ArticleGateway { override suspend fun getArticles( limit: Long, offset: Long ): Flow<ArticleResponseBo> = flowOf(remote.getArticles(limit = limit, offset = offset)) override suspend fun getArticleById(id: Long): Flow<ArticleBo> = flowOf(remote.getArticleById(id = id)) override suspend fun getFavoriteArticles(): Flow<List<ArticleBo>> = flowOf(local.getFavoriteArticles()) override suspend fun saveFavoriteArticle(articleBo: ArticleBo) = local.saveFavoriteArticle(articleBo) override suspend fun removeFavoriteArticle(articleBo: ArticleBo) = local.removeFavoriteArticle(articleBo) override suspend fun isArticleFavorite(id: Long) = flowOf(local.isArticleFavorite(id))}
The repository pattern abstracts data sources from the domain layer. It decides whether to fetch from local cache or remote API, handling data synchronization transparently.
interface ArticleLocalDataSource { suspend fun saveFavoriteArticle(article: ArticleBo) suspend fun removeFavoriteArticle(article: ArticleBo) suspend fun getFavoriteArticles(): List<ArticleBo> suspend fun isArticleFavorite(id: Long): Boolean}
interface PreferencesDataSource { suspend fun saveLastOpenTime(timeInMillis: Long) suspend fun getLastOpenTime(): Long}
Data source interfaces provide an additional layer of abstraction, making it easy to swap implementations (e.g., switching from Room to SQLDelight) without affecting repositories.