Usually whenever I want to fetch an aggregate root by ID I just use some type of Repository::findByID(…) function
Whenever I started with DDD I thought factories where just a pattern to build new objects, but after meeting some aggregates needing one or two extra queries to load, I realized that Factory::objectWithID(…) were useful to also create instances of objects already existing in the database.
Now I have a tree relationship of entities, like Project->Task. The number of relations is huge and I have no framework providing lazy loading.
Since Tasks can be nested, have complexity of their own, and should not be fully retrieved in one query, I made Project and Task different aggregate roots
How should I retrieve and persist Tasks?. It seems that a ProjectFactory is not the solution this time because a Project does not contain the whole Task tree.
I still want some aggregate root like features for my Project, and since I am avoiding queries inside my entities, I decided to write the relationships inside a Project-Service-Aggregate. Now I can retrieve a single Project with a Repo::findByID() function, but fetching a Task looks like
ProjectAggregationService *service = ProjectAggregationService.new(SomeProject)
Task task_1 = service.findTask(...)
Task task_1_child_2 = service.findTaskChild(task_1,2)
I am puzzled at this point because:
- I have a service to represent entity relationships.
- I am declaring many instances of a service, whereas before, services tended to be quite unique objects.
- Not having a clear object tree like Project.tasks[1].subtask[3] make the code above look like more complex than necessary.
Basically I could summarize my question to: Did I take the right approach?
I would greatly appreciate any comment on my reasoning. I am mostly concerned about degenerating my code with overblown complexity, but I still think that keeping references to queries and repos outside my entities implementation is a good goal.
2
I think logic like this is supposed to go into repository of specific aggregate. I would question if Project and Task need to be different aggregate roots. Aggregate roots should be decided based on business needs, not on (perceived) complexity. Either way I would create either ProjectRepository::getTasks(project)
or TaskRepository::getTask(project, taskId)
. This way, you will keep number of services to minimum while still keeping logic where it is logical to look for it.
2