| 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 @Entity with
schema-aware query-builder annotations
data/src/main/kotlin/co/anitrend/data/tag/entity/filter/TagQueryFilter.kt — dynamic Room
query filter built with the support-query-builder DSL
data/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 is Long?; implement IEntity<Int> only when
the surrogate PK is explicitly Int?.
- 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
id in interfaces or mapper inputs — Room fills it after
insert.
Migration checklist
When making schema-impacting changes:
- Bump
DATABASE_SCHEMA_VERSION in 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 manual Migration object for renames, type changes,
constraint changes, or any data transform that @AutoMigration cannot 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
from version) 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 |