Room And WorkManager Skeleton
This skeleton adds a local source of truth and one reliable background job without turning v1 into an enterprise architecture.
Entity And DAO
@Entity(tableName = "transactions")
data class TransactionEntity(
@PrimaryKey val id: String,
val occurredAtEpochMillis: Long,
val title: String,
val amountMinor: Long,
)
@Dao
interface TransactionDao {
@Query("SELECT * FROM transactions ORDER BY occurredAtEpochMillis DESC")
fun observeTransactions(): Flow<List<TransactionEntity>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun upsert(transaction: TransactionEntity)
}
Database
@Database(
entities = [TransactionEntity::class],
version = 1,
exportSchema = true,
)
abstract class AppDatabase : RoomDatabase() {
abstract fun transactionDao(): TransactionDao
}
Repository
class TransactionRepository(
private val dao: TransactionDao,
) {
val transactions: Flow<List<TransactionRow>> =
dao.observeTransactions().map { rows -> rows.map { it.toRow() } }
suspend fun saveManualTransaction(input: ManualTransactionInput) {
dao.upsert(input.toEntity())
}
}
Worker
class CleanupWorker(
context: Context,
params: WorkerParameters,
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return Result.success()
}
}
Enqueue
val cleanup = OneTimeWorkRequestBuilder<CleanupWorker>()
.setConstraints(
Constraints.Builder()
.setRequiresBatteryNotLow(true)
.build(),
)
.build()
WorkManager.getInstance(context).enqueueUniqueWork(
"transaction-cleanup",
ExistingWorkPolicy.KEEP,
cleanup,
)
What To Test First
- DAO query returns expected ordering.
- Repository maps entities to UI rows.
- Migration test exists before
versionchanges. - Worker returns success for the happy path.
- Unique work policy is intentional.
Gotcha: Room’s
exportSchema = trueis not noise. It is the evidence you need to review and test migrations.