存储库是否应该持有对领域项目的引用并了解接口的具体实现?
正如埃文斯在蓝皮书中所描述的那样; Repository 是实现所扮演的角色,以防止应用程序直接改变底层数据。类似地,Aggregate Root 是一个角色——我们不让应用程序接触实际实体,而只是它的有限部分。
存储库的实现是模型的一部分,因此它可以更多地了解所代表的具体实体;包括知道如何从它们中提取可以传递给持久性组件进行存储的状态表示。
要选择特定的上下文,假设我们正在为 TradeBook 建模,其中一个有趣的用例是客户下订单的用例。
在 Java 中,Repository 接口的实现——应用程序知道的位,可能看起来像
interface API.TradeBookRepository<TradeBook extends API.TradeBook> {
TradeBook getById(...);
void save(TradeBook);
}
interface API.TradeBook {
void placeOrder(...);
}
所以应用程序知道它可以访问存储库,但它对实现一无所知,只知道它的承诺
将提供支持 placeOrder 的一些东西。
所以应用程序代码如下所示:
API.TradeBookRepository<? extends API.TradeBook> repo = ....
API.TradeBook book = repo.getById(...);
book.placeOrder(...)
repo.save(book)
但是给定的存储库实现通常与本书的特定实现耦合;它们是配对的。
class LIFO.TradeBook implements API.TradeBook {
...
}
class LIFO.TradeBookRepository implements API.TradeBookRepository<LIFO.TradeBook> {
...
}
如何在不违反 OOP 原则的情况下重新水合聚合根?
在某种程度上,你不能。好消息是,at the boundaries, applications are not object oriented。
您放入持久存储的东西不是聚合根;这是状态的某种表示。我倾向于将其视为Memento。您真正拥有的是两个函数 - 一个将特定聚合根实现(例如:LIFO.TradeBook)转换为 Memento,另一个将 Memento 转换为聚合根。
关键思想:与迁移数据库相比,您可能想要更频繁地更改域模型很多。所以 Memento 需要设计成稳定的——实际上,Memento 是从旧域模型发送到新域模型的消息,所以很多 lessons of message verioning 都适用。