English 中文(简体)
Showing changes in View when using CQRS & DDD with Domain Events & ServiceBus
原标题:

I m a little confused about the flow in a system using domain events to build the read model. Particularly, how do we deal with the fact that the user expects data (and its view) to change when they complete a command, but due to our system architecture (non-blocking calls to publish events) the actual database might not change before the page is reloaded?

I m hoping to move the design of one of our systems more inline with CQRS using events and a service bus.

Let s say my flow goes as such:

  1. User clicks button in View to perform Task of removing Payment Method from their Account.

  2. Controller calls PaymentMethodRemovalService, passing it accountId & paymentMethodId.

  3. Controller uses AccountRepository to retrieve Account and calls account.RemovePaymentMethod(id)

  4. Account validates that operation can occur and publishes event PaymentMethodRemovedMessage(accountId, paymentMethodId)

  5. Because event publishing is asynchronous, we now have to return from service and return view from the controller - but our actual data isn t updated yet!

  6. A handler, IHandle< PaymentMethodRemovedMessage >, hears the event and removes the actual row from the DB

So, what s a guy to do?

I could simply, say, remove the div that was displaying the payment method. This may work in an AJAX scenario, but what if I m using Post-Redirect-Get to support non-JavaScript clients. Then I will be firing my Get and reading the data from the Query side of things, potentially before it s updated.

Do I just show a notification saying their request to remove the payment method has been submitted? (which doesn t seem to friendly, makes sense for submitting an order, but not for, say, changing an address).

Is there a way to reconcile implementing changes as decoupled asynchronous events and showing the user data that reflects their current change?

EDIT: My question is quite similar to CQRS, DDD synching reporting database I have to say, the answer given there and also alluded to here, has a bit of a smell to it - wrangling the UI to show an update that s out of band with the read DB, so to speak. I was hoping for something a little cleaner.

最佳回答

If you are locked into the Request/Response model you could adopt a Correlated Request/Response pattern. For specifics, check out the Async Pages sample in the NSB download. This demonstrates Request/Response in an ASP.NET setting. There are some ASP.NET MVC examples out there if that is more your thing.

When you introduce asynchronous processing you are accepting that the data will be stale. One can argue that the moment you query the DB the data is already old(network latency, rendering time etc.). Since the data is already old, we must ask how old is good enough?

One more thing to consider is that your processing is a bit out of order. Step 3 should be validating the command prior to sending it to the server and then steps 3, 4 would come after 6. The database should only be updated if the command is valid and the event should only be published if the database is successfully updated.

问题回答

This problem is referred to as Eventual Consistency . There are clear ways to approach it. The first point to make is that every system has eventual consistency whether they are using async events or not. It s just that you have opted to make it explicit while most other systems just make the user wait until the update is complete. Both approaches are valid as long as they are explicitly chosen.

A while back I wrote a blog post on this subject which you may find helpful. You can find it here: 4 Ways to Handle Eventual Consistency on the UI

Here is a short version:

Option 1 - Use a confirmation screen. If you use a banking app on your phone you ve probably seen this in action. Transfer some money and at the end of the process, you are taken to a confirmation screen before being taken back to your accounts. This gives the system time to get back up to date.

Option 2 - Fake it. If the operation the user is attempting is highly likely to succeed, then faking it can be a legitimate approach. This is often used when purchasing tickets which are in high demand. Your transaction may go through, but later the company discovers your ticket had already been sold milliseconds before. The benefit to the company is that they have the sale rather than risking losing both customers. So long as the system is built carefully this situation should rarely occur and therefore not be a problem overall.

You can also use correlation id s to allow you to subscribe to success or failure of operations.

Anyway, hope that provides some food for thought.





相关问题
DDD - Returning entity in response to a service operation

I am new to domain driven development & have a simple question. If a service needs to generate some entity as a response to an operation then how should it be done? One of the ways is to inject ...

Domain Driven Design efforts in dynamic languages? [closed]

Are you aware of any DDD efforts in a dynamic language ? Practical resources on DDD tend to decrease quite dramatically when straying from enterprise-oriented solutions (a google search exluding C#, ....

Accessing domain objects in the view

If I don t want to expose the internal state of my Domain Objects, but I need to display them, I can think of three approaches. Which of these is the most "correct" (if any?). The "DTO/ViewModel ...

DDD screen cast question?

I wathced a screen cast on DDD by Greg Young the other day which spoke about persisting all state transitions of an object, instead of it s state when saved, then to load it "replay" all these ...

How to fluent-map this (using fluent nhibernate)?

I have two tables in my database "Styles" and "BannedStyles". They have a reference via the ItemNo. Now styles can be banned per store. So if style x is banned at store Y then its very possible that ...

热门标签