Room and DataStore solve different persistence problems.
DataStore fits small configuration values: selected theme, dismissed education, preferred units, or a last-used filter. It exposes transactional updates and a Flow without pretending preferences are tables.
Room fits records that need identity, relationships, indexes, queries, and migrations: workouts, health snapshots, scores, and sync tombstones.
@Entity(indices = [Index(value = ["userId", "day"], unique = true)])
data class DailyVitalsEntity(
@PrimaryKey val id: String,
val userId: String,
val day: LocalDate,
val steps: Long?,
val updatedAt: Instant,
)
Room verifies SQL at build time and can expose reactive queries. It also requires migration discipline and careful threading for transactions.
Do not serialize an ever-growing list of records into DataStore. Updating one item rewrites the value, queries require loading everything, and schema evolution becomes manual.
Likewise, creating a database table for one theme enum adds unnecessary machinery.
The decision comes from access patterns: key-value configuration versus structured data with independent lifecycles. Choosing by data shape keeps both tools simple.