Domain driven design 存储库如何适应CQR?

Domain driven design 存储库如何适应CQR?,domain-driven-design,cqrs,ddd-repositories,Domain Driven Design,Cqrs,Ddd Repositories,根据Fowler(),存储库“在域和数据映射层之间进行调解,就像内存中的域对象集合一样。”因此,例如,在我的Courier Service应用程序中,当提交新运行时,我的应用程序服务将创建一个新的运行聚合根对象,使用请求中的值填充它,然后在调用工作单元将更改保存到数据库之前将其添加到RunRepository。当用户想要查看当前运行的列表时,我查询同一存储库并返回一个表示信息的非规范化DTO 但是,当查看CQR时,查询不会命中同一存储库。相反,它可能会直接违反数据存储,并且总是被非规范化。我的命

根据Fowler(),存储库“在域和数据映射层之间进行调解,就像内存中的域对象集合一样。”因此,例如,在我的Courier Service应用程序中,当提交新运行时,我的应用程序服务将创建一个新的运行聚合根对象,使用请求中的值填充它,然后在调用工作单元将更改保存到数据库之前将其添加到RunRepository。当用户想要查看当前运行的列表时,我查询同一存储库并返回一个表示信息的非规范化DTO

但是,当查看CQR时,查询不会命中同一存储库。相反,它可能会直接违反数据存储,并且总是被非规范化。我的命令端将演变为NewRunCommand和处理程序,它将创建并填充一个NewRun域对象,然后将信息持久化到数据存储中

因此,第一个问题是,如果我们不维护域对象的内存中集合(缓存,如果您愿意),那么存储库在CQRS模型中的位置如何

考虑这样一种情况,提交给我的应用程序服务的信息只包含一系列ID值,服务必须解析这些ID值才能构建域对象。例如,请求包含分配给跑步的快递员的ID。服务必须根据ID值查找实际的Courier对象,并使用AssignCourier方法(验证Courier并执行其他业务逻辑)将该对象分配给NewRun

另一个问题是,考虑到查询分离和可能缺少存储库,应用程序服务如何执行查找以查找Courier域对象

更新

根据丹尼斯评论后的一些额外阅读和思考,我将重新表述我的问题

在我看来,CQR鼓励存储库仅仅是数据访问和数据存储机制的外表。它们提供集合的“外观”(如Fowler所描述的),但不管理内存中的实体(正如Dennis所指出的)。这意味着存储库上的每个操作都是通过的,是吗

工作单元如何适应这种方法?通常,UoW用于提交对存储库所做的更改(对吗?),但是如果存储库没有在内存中维护实体,那么UoW的角色是什么


关于“写入”操作,命令处理程序是否会引用同一个存储库、不同的存储库或UoW而不是存储库?

我已经了解了CQRS系统,这些系统在命令端维护一个简单的键值存储来表示应用程序的状态,以及其他仅关联消息的系统(使用某种saga)并使用查询存储来表示应用程序状态。无论采用哪种方法,这些方法都无疑涉及到持久化技术,但在这些情况下,存储库模式将是一种不必要的抽象

不过,我在CQR方面的经验仅限于事件源,我们重播了过去的事件,以重建封装和强制执行业务逻辑和不变量的聚合。在这种情况下,存储库模式是一种熟悉的抽象,可以提供一种更简单的方法来检索这些聚合

关于查询方面,我建议尽可能靠近数据存储,我的意思是避免在UI(不管是什么)和数据存储之间出现任何存储库、服务或外观等

看看这些方法在使用中的例子可能会有所帮助。也许可以看看以下项目:

在NES的情况下,存储库仅仅提供了一个熟悉的界面,用于直接向工作单元添加和读取聚合

更多可能有帮助的链接:


我不确定这有多正统,但在当前的项目中,我有一个用于聚合实体根的存储库。该存储库只有两个方法,Get和ApplyEvents

所有事件都为其类型实现了一个公共接口——对于订单,有OrderEvents等。我个人将每个事件的业务逻辑放入一个多态方法中,因此添加新类型的事件变得非常容易

对于Get,存储库转到事件存储,并获取该类型范围内的所有事件(例如,单个存储位置订单)。然后,它会重播事件,以获得给定所有事件的实体当前状态。它也可以从快照中工作,因此您不会在每次加载时都重新创建每个事件。您还可以拥有一个通用事件存储库,以提取出存储事件的方式,并根据规范检索事件

ApplyEvents接收事件列表,然后根据这些事件更改实体的状态并返回。请注意,您为存储库提供了重新创建实体的选项,而不仅仅是更改实体!这对于功能类型的编程很有效,但意味着最好避免对象相等(obj1==obj2)在C#或Java中,我认为只有ValueObject才应该具有相等性,而不是实体

下面是它在实践中的工作方式(C#)-我有订单,我想添加一个item.currentOrder.Items返回一个空列表。然后我就这样做了

Assert.IsFalse(newEvent.Items.Any())
IOrderEvent newEvent = eventFactory.CreateOrderItemEvent(myItemID);
currentOrder = orderRepository.ApplyEvents(currentOrder, newEvent);
Assert.IsTrue(newEvent.Items.Any())
我现在应该看到currentOrder。项目有一个条目


这里的缺点是,我的所有处理都是通过事件完成的,而不是将我的业务逻辑放在实体中在多个系统上工作,这实际上效果很好。

Fowler在中的定义与Eric Evan在DDD boo中描述的存储库模式略有不同