jletroui / TransitioningToEventSourcing

Series of projects supporting the "Transitioning to event sourcing" series of posts
http://julienletrouit.com/?p=9
39 stars 9 forks source link

Loading Aggregate in EventHandler causes "Collection was modified" error. #1

Closed jptillman closed 12 years ago

jptillman commented 12 years ago

I'm using your code to learn CQRS/ES, and I've created a small domain with 2 aggregates, Case and Person. A Case maintains a reference to a Person (who is "participating" in the Case). So, when I submit a new case from my UI, two aggregates get created, a Person and a Case, in that order.

In my CaseReceived event handler (to update my query database), I start with this:

public void Handle(NewCaseReceivedEvent evt)
        {
            var personRepos = CurrentContainer.Container.Build<IRepository<Person>>();
            var participant = personRepos.ById(evt.ParticipantId);

This appears fine at first, but in the call to ById(), the code appears to add the retrieved Person to the aggregate cache a second time. It was already added once when it was created. That cache modification causes an error, since it's a HashSet currently being accessed in a foreach loop by the event repository handling code: "Collection was modified; enumeration operation may not execute."

The code appears to assume that one never loads an aggregate in event handlers.

This leads me to two questions:

I'm still trying to get a handle on the practical application of these concepts and I seem to be constantly tripping over bugs I create by incorrectly using the infrastructure.

jletroui commented 12 years ago

Hi,

Thank you for your feedback! Yes, loading an aggregate in an event handler is something strange to do, since aggregate are event producers, not consumers. It essentially hides the fact that your command is actually consumed by 2 aggregates, hinting at a wrong aggregate boundary. Usually, the need to react to an event within your model is a sign of a naive domain model. So obviously, this is not supported by this simple architecture. Let me know if you would like more information!

jptillman commented 12 years ago

Thanks for the assistance. I think you're right about the boundary. My need to access the Person info in the Case creation event was due to the fact that I'm storing the Person data in the Case record in my denormalized database (unique Person per Case, so no foreign key here). This should have been a clue that Person is not an aggregate, but a dependent Entity which I should be storing as part of the Case!