 |
BOOK: Patterns, Principles and Practices of Domain-Driven Design
 | This is the forum to discuss the Wrox book Patterns, Principles and Practices of Domain-Driven Design by Scott Millett; ISBN: 978-1-118-71470-6 |
Welcome to the p2p.wrox.com Forums.
You are currently viewing the BOOK: Patterns, Principles and Practices of Domain-Driven Design section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
|
|
|
|

May 30th, 2015, 11:56 PM
|
|
Authorized User
|
|
Join Date: May 2015
Posts: 32
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Messaging, Event Sourcing and Concurrency
The Chapter 22 talks about Event Sourcing. That is a good example, but I did not see one where the messaging is also involved.
Let us take the Auction code on this page. The Place method at the end of the page has a call DomainEvents.Raise which can be considered as sending a message. If I use Event Sourcing, I should persist the event before making that call. Let us assume the call is EventStore.Persiste(event) As you explain in the chapter 22, persisting an event or events can cause concurrency issue. If there is an issue, the DomainEvents.Raise should not be called. If the persistence and the messaging are not one as atomic, the system can end up in an inconsistent state.
I may be able to put the EventStore.Persist in a TRY CATCH and control the flow so that the DomainEvents.Raise will be called only if the persistent operation is successful. Think about many numerous places in the app where we have to do this.
How to make the Event persistence and messaging as a single transaction in the code?
PS. Julie Lerman discusses a similar problem and a solution here.
Last edited by varghesep; May 31st, 2015 at 09:30 AM..
|
|

May 31st, 2015, 02:22 PM
|
|
Wrox Author
|
|
Join Date: May 2015
Posts: 59
Thanks: 1
Thanked 5 Times in 5 Posts
|
|
Hey,
I'm flattered by all your questions. It seems you are getting your money's worth from the book
I think I understand your question, but please correct me if I am wrong.
Firstly, it's important to be clear about internal vs external events. Internal events are published in the domain model and handled in-process - this is what DomainEvents.Raise does. External events are published asynchronously out-of-process.
In the example code you refer to, DomainEvents.Raise is using the DomainEvents patter, but it is not using Event Sourcing. For an event-sourced Entity, we don't raise events we record them and persist them at the end of the transaction.
With Event Sourcing and messaging, you are right though; how can you be sure an event is persisted to the Event Store and an external event is published within the same transaction. What if you save the event in the Event Store but then the Bus.Publish fails?
Here's the trick. When you are using Event Sourcing, you might not need a message bus and a queue. You can use the event stream as a queue. You can expose the list of events as an atom feed which other bounded contexts will poll as we showed in chapter 13's REST example.
We actually used Event Store for that example in chapter 13. In here we persist events to the event store: https://github.com/elbandit/PPPDDD/b...sController.cs and then in here: https://github.com/elbandit/PPPDDD/b...gController.cs we map an event stream onto an atom feed. No message bus or queues needed (but you do need to poll these feeds instead)
|
|

May 31st, 2015, 07:19 PM
|
|
Authorized User
|
|
Join Date: May 2015
Posts: 32
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Awesome Book
You understood my question, and thanks for the detailed explanation.
However I have to say that both internal and external events need to be recorded and raised. Let us take two aggregates Product and Category that live in the same Bounded Context. I'm updating the cost of a product. It sends an event called ProductCostUpdated. That can be recorded in the event store. But I want the Category aggregate handles the event and update the total cost of that category. Unless I have a poller (CategoryCostUpdatePoller), which I believe is anti-pattern if I have to follow the same in many places (aggregate to aggregate within the same Bounded Context), the total cost wont be updated. The best way is to record and then raise the event so the Category aggregate handle it immediately. That's where I think the transactional aspect comes into picture.
Last edited by varghesep; May 31st, 2015 at 07:56 PM..
|
|

June 1st, 2015, 03:46 AM
|
|
Wrox Author
|
|
Join Date: May 2015
Posts: 59
Thanks: 1
Thanked 5 Times in 5 Posts
|
|
Internal events need to be recorded if you are doing event sourcing, but not necessarily external events. With event sourcing, external events do not need to be pushed. Instead they are polled from the atom feed which is backed by the event stream. So in your transaction, you just need to take care of saving your events to the event store.
In the Product and Category example you are choosing to update the two aggregates inside the same transaction. Therefore, you can record all of the events on both aggregates and persist them together at the end of the transaction?
It's also possible to implement this in other ways where you don't have to update multiple aggregates within a transaction, even if you don't want to use a poller. You could use asynchronous messaging or CQRS. If you using Event Store, you could make the category total a projection based on the Product event streams.
|
|

June 1st, 2015, 06:24 AM
|
|
Authorized User
|
|
Join Date: May 2015
Posts: 32
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Thank you Nick for the explanation. I'm trying to digest your idea into internal events and external events.
|
|

June 1st, 2015, 06:26 AM
|
|
Wrox Author
|
|
Join Date: May 2015
Posts: 59
Thanks: 1
Thanked 5 Times in 5 Posts
|
|
Cool. Just let me know if you need me to explain it in more detail or provide further examples.
|
|

June 1st, 2015, 06:41 AM
|
|
Authorized User
|
|
Join Date: May 2015
Posts: 32
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Raise Vs Record Vs Persist
Just to clarify, isn't recording and persisting different? I understand that raising is equivalent to DomainEvents.Raise which raise the event. Is recording equivalent to adding that event to the aggregate root in the Events Collection but not persisted to the storage media?
|
|

June 1st, 2015, 06:43 AM
|
|
Wrox Author
|
|
Join Date: May 2015
Posts: 59
Thanks: 1
Thanked 5 Times in 5 Posts
|
|
Recording it means storing it in memory ready for it to be persisted at the end of the transaction. So you can record multiple events, and persist them all atomically at the end of the transaction.
|
|

June 1st, 2015, 08:17 AM
|
|
Authorized User
|
|
Join Date: May 2015
Posts: 32
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Fault Tolerence
I think I was not clear about the fault tolerance aspect in the example that I presented earlier. Let us take an example of three aggregate roots A, B & C in the same component in the same Bounded Context. Let us forget about any external systems or events at this point except there is an external command coming to a app service which a domain service react on it and then the aggregate root A react on it. To support the event sourcing all three aggregate roots should have DomainEvents (member of the base class) which is clear from your chapter on Event Sourcing.
We can think of two scenarios here. One scenario is three aggregates receiving commands and they raise events which are processed by the same aggregate. They are intra-aggregates and not inter aggregates. In that case we can save all the events as a single transaction.
But take the case where the aggregate B should react on an event that aggregate A raises. The aggregate C also wants to react on this event (if B successfully processed) from aggregate B. I think all three aggregates can work with a pub-sub mode or reactive (Rx).
As I understand when the event from aggregate A is raised and before B react on it, it needs to be persisted. This is because if the system crashes and brought up again, it can start from there. That is the case also when the event is raised from aggregate B and before aggregate C react on it.
In the Event Sourcing chapter, there is an example which I believe has only one aggregate. In that example, it makes sense to persist the events as a batch. But when there are multiple aggregates are involved and they all react on others and want to support fault tolerance, shouldn't we persist the internal events (event that aggregate B reacted which is generated by aggregate A as well as the event that the aggregate C reacted which is generated by the aggregate B) too? Then, don't we have to think about if the persistence fails and what to do with the next event that need to be raised. This leads to the original question I asked in this thread.
I appreciate your feedback.
Last edited by varghesep; June 1st, 2015 at 08:28 AM..
|
|

June 1st, 2015, 08:55 AM
|
|
Wrox Author
|
|
Join Date: May 2015
Posts: 59
Thanks: 1
Thanked 5 Times in 5 Posts
|
|
Got it. You have an event-sourced domain model with 3 aggregates A, B, and C. A fires an event and you want B and C to handle the event asynchronously for fault-tolerance... and to avoid the complications of updating multiple aggregates in a single transaction as we recommend.
Here are some options:
1. Use a message bus or a queue locally within the bounded context
2. Create a poller or a batch job that regularly polls the event streams of aggregate A. You mentioned writing pollers could be inefficient, but you can reuse a lot of functionality, so I would investigate that option before ruling it out. You'd be using the Event Store as a queue basically.
3. Use a projection to update the event streams of aggregate B and C. However, this is a very contentious option because you would be bypassing the domain model and should think very carefully before doing that.
|
|
 |
|