name: room-entity-pattern description: 'Room entity, DAO, mapper, and migration pattern guide. Use when adding or changing persistence models, join tables, and schema migrations.'
Skill: Room Entity / DAO / Mapper / Repository Pattern
Overview
Persistence follows a strict four-file pattern for each domain entity. Reading an existing
implementation (e.g., tag) is the fastest way to understand what to produce for a new entity.
Key files to read
data/src/main/kotlin/co/anitrend/data/tag/entity/TagEntity.kt— Room@Entitywith schema-aware query-builder annotationsdata/src/main/kotlin/co/anitrend/data/tag/entity/filter/TagQueryFilter.kt— dynamic Room query filter built with the support-query-builder DSLdata/src/main/kotlin/co/anitrend/data/tag/— full package: entity, DAO, mapper, source, repository, usecase, koin
Surrogate-PK rules (join / connection tables)
These rules apply specifically when a table uses a Room-autogenerated surrogate PK (common for many-to-many join tables and local-only caches). They do not apply to entities whose PK is server-provided.
- Declare the surrogate PK as nullable:
@PrimaryKey(autoGenerate = true) val id: Long? = null. - Implement
IEntity<Long>when the surrogate PK isLong?; implementIEntity<Int>only when the surrogate PK is explicitlyInt?. - Define a composite unique index over the logical relationship columns:
@Index(value = ["tag_id", "media_id"], unique = true). - Use
@Insert(onConflict = REPLACE)for batch upserts keyed by the composite columns, not the surrogate PK. - Never require a non-null surrogate
idin interfaces or mapper inputs — Room fills it after insert.
Migration checklist
When making schema-impacting changes:
- Bump
DATABASE_SCHEMA_VERSIONin the Room database class. - Use
@AutoMigration(from = X, to = Y)for additive-only changes such as new tables or new columns with safe defaults. Provide a manualMigrationobject for renames, type changes, constraint changes, or any data transform that@AutoMigrationcannot express. - Export and inspect the schema JSON at
data/schemas/.../AniTrendStore/<version>.json; verify nullability, indices, and identity hash are as expected. - Build the module to confirm annotation processing and schema export succeed cleanly.
- Smoke-test against an older on-device DB (the
fromversion) to confirm migration applies without crashes. - If changing join tables, assert that multiple relationship rows persist and read back correctly (no silent row-collapse due to conflict strategy).
- Include a brief schema diff summary in the PR description.
Four-file pattern
| File | Responsibility |
|---|---|
XxxEntity.kt |
@Entity data class; mirrors DB columns; uses query-builder annotations |
XxxDao.kt |
@Dao interface; @Query, @Insert(onConflict=REPLACE), @Delete |
XxxMapper.kt |
Converts network model → entity; calls DAO upsert in persist() |
XxxRepository.kt |
Implements domain IXxxRepository; wires source + controller into DataState |