Reputation: 6882
what would be the best way to organize my event streams in ES. With event stream I mean all events to an aggregate.
Given I have a project
with some data and a list of tasks
.
Right now I have a Guid
as AggregateID
as my streamID
.
So far I can
-> recreate the state for a given project with that ID
-> I can assemble a list of projects
with a custom projection
The question would be how to handle todos
?
should this also be handled below the project
stream id
or should it have it's own todo stream id
?
If a todo
has it's separate stream
how would one link it to the owning project
. How is the project
aware of all the todo streams
for a given project
.
Meaning all changes to the todo list
should be also recognized as Commands
and Events
(more Events) in the project.
And if I also want to allow free todo's
without a relation to a project. Does it require to have its own type and stream
to handle freeTodo
on top. And the list of all todos
whether project
related or not would be a projection of all todo
and freeTodo
related streams?
So I guess the main question is how do I handle nested aggregates and how would one define the event store streams and the linking for that?
Any tips, tricks, best practises or resources will be highly appreciated.
// EDIT Update
First of all thank you @VoiceOfUnreason for taken your time to answer this question in great detail. I added the tag DDD because I got that strange feeling it correlates with the bounded context question which is most of the times no black or white decision. Obviously the domain has more depth and details, I simplified the exampled. Down below I shared some more details which got me questioning.
In my first thought I defined an aggregate for todo
as well with a property for the project id
. I defined this project
property as option type (Nullable)
to cover the difference between project related and free todo's. But following use cases/ business rules got me rethinking.
The system should also contains free todo's
which allows the user to schedule personal tasks
not project related (do HR training, etc). All todo's
should appear either in their projects
or in a complete todo
list (both project related and free).
A project
can only be finished/closed if all todo's
are completed.
This would mix somehow information from aggregate project
with information from aggregate todo
. So no clear bounds here. My thoughts would be: a) I could leverage the todo read model
in the project aggregate
for validation. b) define some sort of listed structures for todo's
within the project aggregate scope (if so how). This would handle a todo within the context of project and defines clear bounds
c) Have some sort of service which provides todo
infos for project validation which somehow refers to point a.).
And all feels really coupled =-/
It would be great if you or someone finds the time to share some more details and opinions here. Thanks a million.
Upvotes: 1
Views: 777
Reputation: 57214
Reminder: the tactical patterns in ddd are primarily an enumeration of OO best practices. If it's a bad idea in OO, it's probably a bad idea in DDD.
the main question is how do I handle nested aggregates
You redesign your model.
Nested aggregates are an indication that you've completely lost the plot; aggregate boundaries should never overlap. Overlapping boundaries are analogous to an encapsulation violation.
If a todo has it's separate stream how would one link it to the owning project.
The most likely answer is that the Todo would have a projectId property, the value of which usually points to a project elsewhere in the system.
How is the project aware of all the todo streams for a given project.
It isn't. You can build read models that compose the history of a project and the history of the todos to produce a single read-only structure, but the project aggregate -- which is responsible for insuring the integrity of the state within the boundary -- doesn't get to look inside the todo objects.
Meaning all changes to the todo list should be also recognized as Commands and Events (more Events) in the project.
No, if they are separate aggregates, then the events are completely separate.
Under some circumstances, you might use the values written in an event produced by the todo as arguments in a command dispatched to the project, or vice versa, but you need to think of them as separate things having a conversation that may, or may not, ever come to agreement.
Possibilities: it might be that free standing todo items are really a different thing from the todo items associated with a project. Check with your domain experts -- they may have separate terms in the ubiquitous language, or in discussing the details you may discover that they should have different terms in the UL.
Alternatively, todo's can be separate aggregates, and the business adapts to accept the fact that sometimes the state of project and the state of the todo don't agree. Instead of trying to prevent the model from entering a state where the aggregates disagree, you detect the discrepancy and mitigate the problem as necessary.
Upvotes: 7