5

Hi I have the following senario which I dont understand how to get eventual consistency with:

  1. User 1 uses Task based ui to change customer name
  2. App Service calls operation on the aggregate
  3. Aggregate fires event on customername changed
  4. bus sends message using nservicebus
  5. NServicebus service dies
  6. User 2 gets aggregate and calls change address
  7. Aggregate operation called
  8. Domain event fired
  9. Message put on bus
  10. Bus restarts
  11. Message 2 picked up first
  12. Message 2 processed and other Bounded context updated with new address
  13. Message 1 picked up now which is wrong order
  14. What happens now

In 13 would there be an optimistic concurrency error if we pass the version of the aggregate in the event?

If so Message 1 new gets applied to object in the other context. How do we even maintain consistency?

This is the issue that is preventing me applying events in my domain. All help welcome.

The essential idea is to update another aggregate in another context. I am just stuck on the concurrency technicalities of this.

We are not using event sourcing or CQRS in the sense of commandhandler and commands push on the bus. It is only the event processing we want to happen asynchronously as we have an existing design which we do not wish to change.

Blair

Blair Davidson
  • 901
  • 12
  • 35

4 Answers4

3

Generally you would be queuing the messages. If they are going into a queue you will get proper ordering. If you want to use something that does not support ordering with your servicebus then add a sequence number to your events so the other side can properly reorder them. TCP has been doing this since 1981 http://www.ietf.org/rfc/rfc793.txt :)

Greg Young
  • 2,513
  • 1
  • 21
  • 13
  • I am not sure what you mean. I send the event via nservicebus so there is no ordering. If I add an sequence number to my events how do I do the ordering at the other end? What am I missing there must be something obvious I am not seeing. – Blair Davidson Mar 02 '13 at 12:17
  • Also with NServicebus with multiple threads there is no guarantee they will be processed in order. – Blair Davidson Mar 02 '13 at 12:19
  • Keep track of the sequence number on the other end. Don't process the message until the sequence number matches `last processed sequence number + 1`. – Martijn van den Broek Mar 02 '13 at 12:21
  • 1
    nservicebus can certainly write to a queue (I believe it defaults to msmq). If you are writing to a queue your ordering will be at the point of write, for multiple threads this can be handled with a fine grained lock on the aggregate (or in the database if you are using optimistic concurrency as an example) – Greg Young Mar 02 '13 at 12:21
  • for reordering you would just put a sequence number on them. Event0, Event1, Event2, Event3 ... on the other side if you get out of order you wait. – Greg Young Mar 02 '13 at 12:22
  • What do you mean fine grained lock on the aggregate or Optimistic concurrency ? – Blair Davidson Mar 02 '13 at 12:23
  • What are you using to generate the numbers like 1,2,3 is this a service that the client uses? Or aggregate? – Blair Davidson Mar 02 '13 at 12:24
  • Does this need to be stored in the db i mean the numbers to keep them between app restarts? – Blair Davidson Mar 02 '13 at 12:25
  • I dont want to use event sourcing either – Blair Davidson Mar 02 '13 at 14:28
  • The easiest way to achieve this with NSB is with a small saga on the receiving end which keeps track of the last event processed. But it is important also to think about the quantity of messages you expect to see come through when picking your data structure. – JimmyP Mar 03 '13 at 23:23
0

Oops: I just noticed that you actually used 2 different tasks in your example (customer name and then customer address). Of course this would be a non-issue since the order really should not matter. But I'll leave my answer just in case your intention was to have two of the same type of change.

As always a couple of questions/issues come to mind:

For one thing your example does not require an endpoint to fail since 2 users could update the address at the same time and the order of the processing can be random. So the question becomes which one is the correct address. So resolving that requires quite a bit more analysis. For one thing I guess we could assume that a customer would not move so frequently that 2 users are updating the address at the same time (or even near it) --- :)

Even so, maybe the first address is the correct one.

I think for such a scenario you want to identify whether concurrency, or even some other tolerance, is an issue. So maybe an address may only be changed once a day and any other change requires some other interaction. So some exception handling would be an option.

You really should not be taking this down to a technical level to try and resolve it but rather look at the process/business implications.

For a simple solution I would go with matching message send date to the last operation date for a specific type. So for ChangeAddressCommand when processing a message you could compare to the current LastAddressChange and if the message was sent before the last change date then reject it.

Eben Roux
  • 12,983
  • 2
  • 27
  • 48
0

A similar issuee with NServiceBus is discussed here. The OP proposes the use of IBus.HandleCurrentMessageLater() to spin until the other message arrives. This can work, but can be problematic since you never know how long you have to wait.

A more involved option would be to use a saga which would wait until all messages leading up to a particular version have arrived. In this case, sequencing is done based on version and is only possible if all changes in aggregate version are published to the other BC. Suppose Message 1 operated on version 2 of the aggregate. It then it increments the version of the aggregate and publishes an event stating it has operated on version 2. Message 2 operates on version 3 of the aggregate and publishes an event stating it has operated on version 3. When the NServiceBus endpoint in the other BC receives Message 2 before Message 1, it knows that the last received message operated on version 1 of the aggregate, so it needs one that has operated on version 2. It will start a saga which is waiting for the the next message. Once it receives Message 1, the saga will apply Message 1 and then Message 2 and release the saga state. If using version for sequencing isn't acceptable, another sequencing strategy can be used.

eulerfx
  • 36,769
  • 7
  • 61
  • 83
0

According to this you should ask yourself:

What is the business impact of having a failure?

In your current case, you have this problem once by one million requests. I don't think it would have a huge impact on the business if you'd accept both of the requests as valid.

inf3rno
  • 24,976
  • 11
  • 115
  • 197