Room实体与领域模型是否应分离?业务逻辑场景下的疑问
兄弟,你这个问题戳中了很多Room初学者的痛点!大部分入门指南为了简化演示,直接把Room实体当成领域模型用,但实际做业务的时候,这俩的职责完全不同,强行绑定只会让你在业务逻辑上束手束脚。
核心解决方案:分离Room实体与领域模型
Room实体的唯一职责是精准映射数据库表结构,而领域模型的职责是承载业务逻辑、满足业务场景的结构需求。把它们拆分开,就能彻底解决你遇到的问题。
1. 分别定义两类模型
举个具体例子:假设你的业务需要一个带对象引用的Order类来做计算,那它就是你的领域模型;而对应的Room实体必须贴合数据库的关系型结构,用外键替代对象引用。
领域模型(专注业务逻辑)
data class Order( val id: String, val customer: Customer, // 直接持有Customer对象,方便业务计算 val items: List<OrderItem>, val totalAmount: Double ) { // 业务逻辑:比如计算折扣后金额 fun calculateDiscountedAmount(discountRate: Double): Double { return totalAmount * (1 - discountRate) } } data class Customer( val id: String, val name: String, val email: String ) data class OrderItem( val id: String, val productName: String, val quantity: Int, val price: Double )
Room实体(仅映射数据库)
@Entity(tableName = "orders") data class OrderEntity( @PrimaryKey val id: String, val customerId: String, // 用外键关联Customer表 val totalAmount: Double ) @Entity(tableName = "customers") data class CustomerEntity( @PrimaryKey val id: String, val name: String, val email: String ) @Entity( tableName = "order_items", foreignKeys = [ForeignKey( entity = OrderEntity::class, parentColumns = ["id"], childColumns = ["orderId"], onDelete = ForeignKey.CASCADE )] ) data class OrderItemEntity( @PrimaryKey val id: String, val orderId: String, // 外键关联Order表 val productName: String, val quantity: Int, val price: Double )
2. 实现模型转换:用Mapper类做桥梁
你需要一个专门的映射类,负责在领域模型和Room实体之间转换数据:
class OrderMapper { // 从Room实体转领域模型 fun mapToDomain( orderEntity: OrderEntity, customerEntity: CustomerEntity, itemEntities: List<OrderItemEntity> ): Order { val customer = Customer( id = customerEntity.id, name = customerEntity.name, email = customerEntity.email ) val items = itemEntities.map { itemEntity -> OrderItem( id = itemEntity.id, productName = itemEntity.productName, quantity = itemEntity.quantity, price = itemEntity.price ) } return Order( id = orderEntity.id, customer = customer, items = items, totalAmount = orderEntity.totalAmount ) } // 从领域模型转Room实体 fun mapOrderToEntity(order: Order): OrderEntity { return OrderEntity( id = order.id, customerId = order.customer.id, totalAmount = order.totalAmount ) } fun mapCustomerToEntity(customer: Customer): CustomerEntity { return CustomerEntity( id = customer.id, name = customer.name, email = customer.email ) } fun mapItemToEntity(item: OrderItem, orderId: String): OrderItemEntity { return OrderItemEntity( id = item.id, orderId = orderId, productName = item.productName, quantity = item.quantity, price = item.price ) } }
3. 在Repository层封装转换逻辑
Repository是连接数据库和业务层的中间层,在这里完成实体转换,对外只暴露领域模型,让业务层完全不用关心数据库细节:
class OrderRepository( private val orderDao: OrderDao, private val customerDao: CustomerDao, private val itemDao: OrderItemDao, private val mapper: OrderMapper ) { suspend fun getOrderById(orderId: String): Order? { // 从数据库获取所有关联实体 val orderEntity = orderDao.getById(orderId) ?: return null val customerEntity = customerDao.getById(orderEntity.customerId) ?: return null val itemEntities = itemDao.getByOrderId(orderId) // 转换为领域模型返回 return mapper.mapToDomain(orderEntity, customerEntity, itemEntities) } suspend fun saveOrder(order: Order) { // 把领域模型转成实体后存入数据库 val orderEntity = mapper.mapOrderToEntity(order) val customerEntity = mapper.mapCustomerToEntity(order.customer) val itemEntities = order.items.map { mapper.mapItemToEntity(it, order.id) } orderDao.insert(orderEntity) customerDao.insert(customerEntity) itemDao.insertAll(itemEntities) } }
这么做的好处
- 单一职责:实体只管数据库映射,领域模型只管业务逻辑,代码结构更清晰
- 灵活性:领域模型可以完全按照业务需求设计,不用受Room的注解、数据库结构约束
- 可测试性:业务逻辑可以单独写单元测试,不用依赖数据库环境
内容的提问来源于stack exchange,提问作者Eduardo Naveda
相关产品推荐
相关产品推荐

