5

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?

Community
  • 1
  • 1
Prokurors
  • 2,458
  • 3
  • 40
  • 65
  • 1
    A folder hierarchy is not really an aggregate, it's more of a repository. You can think of each folder as being a grouping criteria for other folders. The folder concept itself is perhaps just a name and some id. And very probably that hierarchy is more of a query concern, i.e there's not much DDD in here. And probably [my post](http://blog.sapiensworks.com/post/2013/01/15/Domain-Driven-Design-Aggregate-Root-Modelling-Fallacy.aspx/) will help you a bit to understand what's going on. – MikeSW Aug 12 '15 at 21:16
  • @MikeSW I watched a Domain Driven Design Fundamentals course at Pluralsight recently and they recommended to have repository for an aggregate root. Folder was just an example of some hierachy. But take for instance Task instead - each Task can have multiple sub-tasks (and sub-task it's sub-task etc). And if a system tracks time spent on tasks - then spending time on a task, means time has been spent on all of it's ancestor Tasks (->parent task -> parent task -> etc.). So, I think this means some Invariants for a hierarchy of tasks, so how to model this if hierachy is not an aggregate root? – Prokurors Aug 13 '15 at 06:04
  • DDD is very subjective, so a design approach for modelling tasks will not apply when modelling a folder structure. There isn't a "this is how this certain thing is done" that can be applied broadly to everything that looks like a similar thing/problem. – Adrian Thompson Phillips Aug 13 '15 at 08:29
  • @AdrianThompsonPhillips ok, I am trying to understand DDD principles - in this case how to model hierarchically related objects in DDD. But I understand, that those rules/principles are not absolutelly universal. I will edit my question so that it would be about more concrete business case – Prokurors Aug 13 '15 at 10:54
  • 1
    Do not forget that rules such as "children must be removed when a parent is removed" can usually be made eventually consistent. It shouldn't matter much to have orphaned nodes for a few milliseconds. If it does then do not forget you can also modify more than a single aggregate in one transaction in special cases. If you have to do that a lot if might be an indicator that your AR boundaries are wrong however. – plalx Aug 17 '15 at 14:10

1 Answers1

2

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.

Machet
  • 1,090
  • 8
  • 17
  • When thinking about invariants, it seems, that having only ancestors (without hierarchy of descendants) could be enough, but in user interface I need to show a list of child Tasks - to have possibility to quickly access all sub-tasks. So from one perspective it would be logical to have navigation property to Child elements, but that would greatly increase size of the aggregate... – Prokurors Aug 17 '15 at 12:46
  • Ok, maybe solution would be to have a small Task aggregate (Task +ancestors) + dictionary of all tasks. Then this Aggregate could have list of Task keys to quickly get data about child Tasks from the dictionary... Maybe you've got some better ideas? – Prokurors Aug 17 '15 at 12:49
  • 2
    In general proper DDD models are not suitable for querying data to UI. Good idea is to have separate model for querying data (you can search about CQRS to get more detail about that, but you must be aware that CQRS has many forms). I usually create separate EF context (db first) for read purposes. Its refreshed from db so it doesn't require much maintenance. You can also create specific view that you can map to separate entity which you will use for UI purposes. – Machet Aug 17 '15 at 13:43
  • Ok, thanks - no other candidates to answer my question, and your answer is helpful - so I aceept it. – Prokurors Aug 30 '15 at 17:12