CQRS Read models in a NoSql (Mongo DB)

你离开我真会死。 提交于 2019-12-11 07:17:49

问题


Hi its my fist time with DDD/CQRS. I've read multiple sources of knowledge and Im still confused a bit, maybe someone could help :)

Lets assume simple case that we have products and clients (possibly different bounded contexts). A client can buy a product and he wants to see all products that he purchased.

In this case I realize I need a UserPurchasesView view model with:

  • purchaseId (which is a mongo primary key)
  • userId,
  • product: {id, name, image, shortDescription, [maybe some others]}
  • prize
  • timestamp

Now ... the problem is that My domain is producing an event like UserPurchasedProduct(userId, productId). I could enrich an event with a prize, product name or maybe something else but not all fields. Im getting to a point where enriching seems to be wrong.

In this point I realize I need something like ProductDetailsView:

  • productId (primary key)
  • prize
  • name
  • shortDescription
  • logo

This view is maintained by events like: ProductCreated, ProductRenamed, ProductImageChanged

And now we have 2 options ...

  1. Look into the ProductDetailsView when UserPurchasedProduct event comes in, take all needed product details and save it in UserPurchasesView for faster reads. This solution looks not that bad but it introduces some extra coupling and it seems to me these views cannot be scaled well when needed. Also both views must be rebuilt together when replying all events from the event store (rebuilding is also more tricky in that case).
  2. Keep only the productId in the UserPurchasesView and read multiple views when user queries his purchases. This is some extra processing that would have to be done somewhere. In the frontend, in the backend controller or in some read model high level API. UPDATE: I also realized that I would also need to keep at least the prize and maybe name of the product in the UserPurchasesView (in case it changes) but sometimes you need the value from the time of a purchase and sometimes you need the recent value. Scenario depends on a business but we could imagine both.

None of these solutions looks perfect to me. Am I wrong, am I missing something or is it just the way to do it? Thanks!


回答1:


You understand well.

So you have to choose between coupling between the read models and coupling between UI and individual read models.

One of the main advantages of CQRS/ES is the posibility to create blazing fast read models (views if you like), without any joins, the perfect cache as I saw it called. I personally have chosen every time the first approach, with full data denormalisation. The views are very fast and models very clean and clear. This is the perfect solution if you want to optimize the read side of your application (and I think you should). By listening to the right events you can keep these read models in sync with the rest of the application.




回答2:


There is a 3rd option:

The projection responsible for the UserPurchasesView view not only listens to UserPurchasedProduct events, but also to ProductCreated, ProductRenamed, ProductImageChanged - any product related events that affect the UserPurchasesView. Now, as well as the UserPurchasesView collection for the read model that it is responsible for, it also needs a private collection to maintain the bits of products it is interested in: ({id, name, image, shortDescription, [maybe some others]}), so that when a new purchase event comes in, you have somewhere to get the initial state of those product fields from. Since your UserPurchasesView needs to listen to some of those product events anyway in order to keep up to date when a product changes, this isn't really much extra work, and avoids any dependency on another projection (ProductDetailsView). The cross-projection dependency also has a potential problem due to eventual consistency - what if the product isn't even in the product details view yet when the UserPurchasedProduct event comes through?

To avoid any concurrency issues, it's simplest to have each projection managed only by a single process and a single thread. That way, as long as the projection can receive events in-order across streams (so that it is guaranteed to see the product creation before the product purchase), you won't have issues with seeing a purchase before the product exists. If you introduce sharding or any other multi-threading to your projection, it gets more complicated.



来源:https://stackoverflow.com/questions/42999404/cqrs-read-models-in-a-nosql-mongo-db

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!