Skip to content

Instantly share code, notes, and snippets.

@JH108
Last active May 26, 2022 20:03
Show Gist options
  • Save JH108/51f62b70fc074214745db6502d508c27 to your computer and use it in GitHub Desktop.
Save JH108/51f62b70fc074214745db6502d508c27 to your computer and use it in GitHub Desktop.
Orthogonality and DRY with Room Database
data class DB(val entities: Map<String, Entity>, val daos: List<Dao>)
typealias ForeignKey = Pair<String, String>
class PersonDao(val db: DB) {
fun getAll(): List<PersonEntity> {
val peopleTable = db.entities["people"]
return peopleTable ?: listOf()
}
}
class DriverDao(val db: DB) {
fun getAll(): List<PersonEntity> {
val peopleTable = db.entities["drivers"]
return peopleTable ?: listOf()
}
}
interface Entity {
val id: UUID
val tableName: String,
val foreignKeys: List<ForeignKey>?
}
data class PersonEntity(
val firstName: String,
val lastName: String,
val age: Int,
override val id: UUID,
override val tableName: String = "people"
) : Entity
data class DriverEntity(
val licenseNumber: String,
val personId: UUID,
override val id: UUID,
override val tableName: String = "drivers",
override val foreignKeys: String = listOf("personId" to "people")
) : Entity
class DriversRepository(val personDao: PersonDao, val driverDao: DriverDao) {
fun getDrivers(): Driver {
val people = personDao.getAll()
val driverEntities = driverDao.getAll()
return driverEntities.map { driver ->
val person = people[driver.personId]
Driver(
licenseNumber = driver.licenseNumber,
firstName = person.firstName,
lastName = person.lastName,
age = person.age
)
}
}
}
data class Driver(
val licenseNumber: String,
val firstName: String,
val lastName: String,
val age: Int
)
fun main() {
val personId = UUID.getRandom()
val driverId = UUID.getRandom()
val driver1 = DriverEntity(
licenseNumber = "019285112",
personId = personId,
id = driverId
)
val person1 = PersonEntity(
id = personId,
age = 25,
firstName = "John",
lastName = "Doe
)
// Too much boilerplate...
}
class PeopleRepository(val personDao: PersonDao) {
fun getPeople(): List<Person> {
val people = personDao.getAll()
return people.map { person ->
Person(
id = person.id
firstName = person.firstName,
lastName = person.lastName,
age = person.age
)
}
}
}
data class Person(
val firstName: String,
val lastName: String,
val age: Int,
val id: UUID
)

Orthogonality and DRY

The core question is does this violate either principle? Am I repeating knowledge in the Person vs PersonEntity? Would the removal of the Person intermediate class result in violating the principal of orthogonality?

Answer: The knowledge of "what is a person" can be answered by the PersonEntity, this is different from the knowledge of "My screen needs to show Person.age, Person.firstName, and Person.lastName. This means we are not repeating the knowledge of what a person is within the Person data class, instead we are expressing the knowledge of what data (coincidentally obtained from a PersonEntity) that X screen needs.

We maintain Orthogonality because if we change the implementation of the database Entities, Daos, or the database itself then the UI depending on the PersonRepository would not be affected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment