Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define DDD Aggregate root for hierarchical data structure?

I'm currently trying to adapt Domain-driven-design principles to my development practices. And I have stuck on how to define an aggregate root for data that are organized in hierarchies.

Let's take for instance folder structure - each folder can have 0..N sub-folders, and sub-folder 0..N can also have 0..N sub-folders and so on.

I have invariants on a folder and all it's direct and indirect subfolders - deleting folder should result in deleting all of it's sub-folders

Would that be DDD valid approach to have let's say Aggregate root "Folder hierarchy", that contains 1 "Folder" entity (that would be "header" folder for that folder hierarchy) and each Folder entity has 0..N Folder entities (sub-folders)

Would that be a valid DDD? Would that be effective? Since I have read that DDD advocates to have small Aggregates, but this "Folder Hierarchy" would potentially be a huge aggregate...

Is Aggregate Root with Deep Hierarchy appropriate in DDD?

Effective Aggregate Design by Vaughn Vernon

Any advice how to make this both DDD valid and effective?

EDIT

Let's have a bit different example of objects that have tree-like structure. Let's say I need to develop a task tracking system and this system needs tasks to have sub-tasks non-fixed levels deep - all tasks are from functionality/behaviour perspective the same - each task can have 0..1 parent task and 0..N child tasks.

Having Task as an aggregate root (with all it's child-task hierarchy) would not follow DDD recommendation's to have small Aggregates - right?

What would be a good design for Task according to DDD principles? And how to implement invariants on a Task (with all it's child-task hierarchy) if Task (with it's hierarchy) is not an aggegate?

like image 582
Prokurors Avatar asked Oct 31 '22 21:10

Prokurors


1 Answers

You should model your aggregates around invariants. One rule of thumb is that aggregate should be loaded in one go to memory (with all it's child objects) no lazy loading.

Do you really have an invariant that requires whole hierarchy to be loaded? Is there situation where you need to traverse all nodes in hierarchy? To answer this question you need to think about use cases of your aggregate.

If you need all data then your aggregate has proper size it just couldn't be smaller. If your invariant require only small portion of graph then maybe you are missing some domain concept that will describe this graph part.

If you care only about updating time of ancestors then perhaps your task can contain only Parent property you don't need child collection. Then you can perform things like

public void RegisterTime(TimeSpan time)
{
    TimeSpent += time;
    // maybe more stuff here
    Parent.RegisterTime(time)
}

Then your repository will get only Task with all of his ancestors and aggregate will be small enough.

I'm just guessing because it always depends of use cases.

like image 182
Machet Avatar answered Nov 15 '22 04:11

Machet