I need to remove a id from collections that are hold by multiple aggregates. Lets say i have a EmployeeAggregate, that contains a collection of Hobbies'id. The aggregate is event sourced.
Lets' say somewhere in the app someone, in a basic crud app handling a Hobbies table, deletes one Hobbie row. How can i reflect the changes in all the EmployeeAggregates ?
Frow now in the event store i have events concerning EmployeeAggregate, events concerning Hobbies (with the HobbieWasDeletedEvent), but nothing to make the EmployeeAggregate handle this HobbieWasDeletedEvent.
Some ideas of what i could do:
solution 1: instead of dispatching a command for DeleteHobby, i loop over all Hobbies->getEmployee and dispatch a command for each match, handled by EmployeeAgregate. Drawback: what if the data is huge ? the loop might never end
solution 2: i dispatch only one command with an array of all the EmployeIds. Then i do my loop in the command handlers, reconstituting the aggregate for each iteration and calling the remove hobby method. I know that theorically one command => one aggregate, but are we talking about one command = one aggregate TYPE or one aggregate instance ? I'm ok with the fact that a RemoveHobbyFromEmployeeCommandHandler cannot act on something else than a an employee; but can it act on a collection of employees, which are of same type ?
solution 3: i do solution one (or solution two), but in a command sourcing way: instead of synchronously dispatching the command, i pass it to an async command bus and lets a worker dequeue them and pas them to handlers. Fire and forget.
solution 4: i should not care about notifying the aggregate that the hobby has been deleted, as it is not a domain problem. if business team needs to know that, i will end up with a check on projection side to ensure that all the hobbies ids of the collection exists before writing them in the readmodel. Drawback: if i reconstitute my EmployeeAggregate on the fly just to "dump" it and display it's values, it will still have the deleted hobby id in its hobbies collection. so it won't represent the reality. But is that really a use case ?
solution 5: would Sagas be helpful here ?
any other idea ?
[EDIT]
- i came up with another idea inspired by Dnomya's comment (see below): through Employee event handlers, i write a "local" readmodel. it's just a table keeping track of all the EmployeeId / HobbyId association, and it's updated only by some specific events (those who concern employee/hobby relations). Then, when a HobbyWasRemoved is handled, i get all the EmployeeIds from this local readmodel and do something. But what ? dispatch commands for each EmployeeId found ? can a event handler do this ? Isn't it the kind of stuff that a saga does ? But what if this collection of employee ids is huge ?
A -> List[B]. When aBis deleted, you want to remove it from everyList[B]for eachA. If you have a reverse dictionary:B -> List[A]you can know whichAyou have to update. This dictionary can be a view driven by the event triggered from your aggregate. The downside is that you have a bit of work implement that. - Dnomyar