CQRS读取NoSql中的模型(Mongo DB)

时间:2017-03-24 12:22:55

标签: domain-driven-design cqrs

嗨我的第一次使用DDD / CQRS。我已经阅读了多种知识来源,我仍然感到困惑,也许有人可以提供帮助:)。

让我们假设我们有产品和客户(可能是不同的有界上下文)的简单情况。 客户可以购买产品,并希望看到他购买的所有产品。

在这种情况下,我意识到我需要一个 UserPurchasesView 视图模型:

  • purchaseId(这是一个mongo主键)
  • userId,
  • 产品:{id,name,image,shortDescription,[可能是其他人]}
  • 时间戳

现在......问题是我的域名正在生成像 UserPurchasedProduct(userId,productId)这样的事件。我可以用奖品,产品名称或其他东西来丰富一个活动,但不是所有领域。我到了富裕似乎是错误的地步。

在这一点上,我意识到我需要像 ProductDetailsView

这样的东西
  • productId(主键)
  • 名称
  • SHORTDESCRIPTION
  • 标志

此视图由以下事件维护:ProductCreated,ProductRenamed,ProductImageChanged

现在我们有两个选择......

  1. 当UserPurchasedProduct事件进入时,查看ProductDetailsView,获取所有需要的产品详细信息并将其保存在UserPurchasesView中以便更快地读取。这个解决方案看起来并不坏,但它引入了一些额外的耦合,在我看来,这些视图在需要时无法很好地扩展。在回复事件存储中的所有事件时,必须同时重建两个视图(在这种情况下重建也更棘手)。
  2. 在UserPurchasesView中仅保留productId,并在用户查询其购买时读取多个视图。这是一些必须在某处完成的额外处理。在前端,后端控制器或某些读取模型的高级API中。更新:我还意识到我还需要在UserPurchasesView中保留至少奖品和产品名称(如果它发生变化),但有时您需要购买时的价值,有时您需要最近的价值。场景取决于业务,但我们可以想象两者。
  3. 这些解决方案对我来说都不是很完美。我错了,我错过了什么或者只是这样做的方式?谢谢!

2 个答案:

答案 0 :(得分:3)

你理解得很好。

因此,您必须在读取模型之间进行耦合以及在UI和各个读取模型之间进行耦合。

CQRS / ES的一个主要优点是可以创建超快速读取模型(视图,如果你喜欢),没有任何连接,完美的缓存,正如我所看到的那样。我个人每次都选择第一种方法,完全数据非规范化。视图非常快,模型非常干净清晰。 这是一个完美的解决方案如果你想优化应用程序的读取方面(我认为你应该)。 通过聆听正确的事件,您可以使这些读取模型与应用程序的其余部分保持同步。

答案 1 :(得分:2)

有第三种选择:

负责UserPurchasesView视图的投影不仅会监听UserPurchasedProduct事件,还会监听ProductCreated,ProductRenamed,ProductImageChanged - 任何影响UserPurchasesView的产品相关事件。现在,除了它负责的读取模型的UserPurchasesView集合之外,它还需要一个私有集合来维护它感兴趣的产品:( {id,name,image,shortDescription,[也许还有其他]因此,当新的购买事件进入时,您可以从某处获得这些产品字段的初始状态。由于您的UserPurchasesView无论如何都需要收听其中一些产品事件,以便在产品发生变化时保持最新状态,这不是额外的工作,并且避免了对其他投影的依赖(ProductDetailsView)。由于最终的一致性,交叉投影依赖性也存在潜在的问题 - 当UserPurchasedProduct事件发生时,如果产品甚至不在产品详细信息视图中,该怎么办?

为了避免任何并发问题,最简单的方法是让每个投影只由一个进程和一个线程管理。这样,只要投影可以跨流顺序接收事件(以便保证在产品购买之前看到产品创建),您就不会在产品存在之前看到购买产生问题。如果为投影引入分片或任何其他多线程,则会变得更复杂。