我正在我的项目中创建一个存储库,它将负责User实体的所有存储操作。我将使用mongo作为db并使用mongoreactive作为客户端。我现在遇到的问题是类型。
trait UserRepository {
save(user: User) : ?
}
trait MongoUserRepository extends UserRepository {
save(user: User) : Future[WriteResult] = {
collection.insert(user)
}
}
我应该如何在我的域中为来自MongoReactive的WriteResult建模?我不希望它泄漏到我的域名中。是否存在任何现有模式或良好做法?
答案 0 :(得分:4)
我应该如何在我的域中为来自MongoReactive的WriteResult建模?我不希望它泄漏到我的域名中。是否存在任何现有模式或良好做法?
通常的做法是域将UserRepository
特性定义为持久性基础架构需要支持的服务提供者接口(spi)。从根本上说,它是表达模型对持久性施加的使用要求的一种方式。
使用Command Query Separation的语言,save
是一个命令:它是一个改变存储库状态的操作。因此,特征的实现应符合您实现命令的本地编码标准。
Greg Young (generic) repositories:
首先,存储库模式的目的究竟是什么?回顾[DDD,Evans],我们会看到它将一系列对象表示为内存中的一个集合,这样就可以解除域中的持久性问题。换句话说,目标是在持久性中将对象语义放在对象上。
所以你也可以到你的馆藏库寻找灵感。
但总的来说,最常见的选择看起来像
trait UserRepository {
save(user: User) : Unit
}
这就是您希望特定实现符合的合同。
在MongoUserRepository
中,您可以调整持久性解决方案的实现以满足合同。在这种情况下,这意味着解包Future
,检查WriteResult
是否有错误,如果写入不成功则抛出异常。
使用save(用户:用户):单位隐式地向客户端提出要求监视存储库故障的要求(例如:在数据库失败的情况下)
其他方式 - 存储库是服务提供商接口;这种设计不会限制客户,而是限制提供商。在六边形体系结构的术语中,我定义了一个secondary port并限制了二级适配器以符合端口的契约。
动机正是您所描述的动机:应将存储库使用者与与所选持久性解决方案交互所需的协议隔离开来。域模型位于业务领域的中间,适配器将业务领域与现实隔离开来。
Evans Chapter 6提出了“防止模型被管理(域对象)生命周期的复杂性所淹没”的管理挑战。存储库提供“查找和检索持久对象的方法,同时封装所涉及的巨大基础设施。存储库是防火墙。
我们在这里讨论的是关注点的分离;域模型描述了您的业务逻辑。我们希望能够清楚地看到业务案例,如果我们必须明确管理在可修改的内存集合在修改时发生灾难性事件时发生的事情,那将是不可能的。务实的答案是禁止并让应用程序处理它。
那就是说...当我写“符合你的本地编码标准”时,我故意对冲。如果您的本地指南使用railway oriented programming或message driven,那么请务必将其排成一行。域模型绝对没有理由关心存储是同步还是异步,本地还是远程等等。
但是如果你的域模型开始填满描述实现问题的匹配表达式,我会说你已经失去了某个地方的情节。
答案 1 :(得分:1)
当我实现相同的存储库时,我最终提取了我最感兴趣的WriteResult
值。在我的情况下,我最终获得了以下签名:
trait UserRepository {
save(user: User) : Future[Option[String]]
}
返回某些错误消息或。因此,实现将是这样的:
trait MongoUserRepository extends UserRepository {
save(user: User) : Future[Option[String]] = {
collection.insert(user).map(_.errmsg)
}
}
我最终得到了这个实现,因为在异常情况下我不会丢失异常消息。
替代选项可以是将插入结果映射到Boolean
:
trait UserRepository {
save(user: User) : Future[Boolean]
}
trait MongoUserRepository extends UserRepository {
save(user: User) : Future[Boolean] = {
collection.insert(user).map(_.ok)
}
}
但在这种情况下,您将丢失异常消息。有时它可能没问题,但这取决于你的具体情况。
更新:上面发布的答案适用于0.11版本。在0.12中,errmsg
中的WriteResult
方法已被删除。或者,您可以使用writeErrors
,如果Seq
不为空,则从所有errmsg
中提取所有WriteError
。
希望它有所帮助,先生!