1

In ddd there is the concept of aggregate root.

The Aggregate Root is the parent Entity to all other Entities and Value Objects within the Aggregate.

It seems to me that User should be the root aggregate in most system where an user is interacting with the system. I've an hard time conceptualizing an aggregate root otherwise as any sort of action is usually taken by the person interacting with the software, ie: the user.

Unless the system is not user facing, ie: a car software, where the car might be the aggregate root.

Now the User can have different name depending on the context, SaleAgent, Client etc. but ultimately they are all "users" of the system and might even all extend a base class User. Or even above the user the biggest aggregate root is probably the System itself.

Would that be a fair assessment ?

3
  • 1
    Please explain the downvote, so I can rewrite the question more precisely.
    – Ced
    Commented Aug 20, 2022 at 11:43
  • 2
    "In ddd there is the concept of aggregate root" - I feel you and a lot of other people are focusing on the wrong thing, cause everyone keeps talking about aggregate roots, and not the aggregates themselves. The concept that's important is the aggregate (a relatively small encapsulated bundle of collaborating objects), that has some sort of an interface (set of public methods and properties) through which it interacts with any code outside of it. This interface is represented by a "root" object. An aggregate shouldn't be a big, unmanageable sprawling tree. Commented Aug 20, 2022 at 16:36
  • Don't confuse root composition with aggregate root. In deed, any application has a "main" that acts like a sort of Aggregation root, but conceptually it's not. Think in aggregated roots as small complexity boundaries strictly related to the domain.
    – Laiv
    Commented Aug 20, 2022 at 18:12

2 Answers 2

1

It seems to me that User should be the root aggregate in most system where an user is interacting with the system.

Would that be a fair assessment ?

Probably not. It's normal for our systems to document relationships between aggregates.

More likely candidates for the aggregate root are

  • the action itself
  • the instance of the process this action is participating in
  • one of the domain entities modified by the action

Part of the point of aggregates is that we don't need every scrap of information in our database when making a change; we only need what we need, and therefore we only need the domain entities that manage the information that we need, and the aggregate root is going to be that domain entity we've decided to use as the entry point to communicate with the rest.

I tend to think of it in terms of locks: when we are trying to compute the effects of some new information on our domain model, we need to lock some of the existing information against concurrent change. An aggregate, in effect, is a collection of locks that we need to hold in order to make a change safely.

There's some tension: if we are missing a lock we need to hold, a concurrent data change can mess us up. If we are holding a lock we don't need, then we are blocking other changes in the system, slowing the whole thing down.

Modeling is largely about figuring out where we need locks, and understanding where we can use unlocked data instead of locked data.

In this framing, your question becomes: don't we need to lock the customer for most changes? Maybe in some domains, but as a general rule? no, we don't.


Note: since Evans first introduced the AGGREGATE pattern in his book, there's been a certain amount of debate over whether aggregates are best understood as consistency boundaries or transaction boundaries. The distinction roughly translates to "can a single transaction span multiple aggregates"?

So take care, both in your own thinking and when getting the opinions of others, to make sure that you understand which perspective is most aligned with the viewpoint.

9
  • the action itself would you mind giving an example?
    – Laiv
    Commented Aug 22, 2022 at 15:11
  • @VoiceOfUnreason the issue I have with not having the user as an aggregate root, is that things do not seem logical. For example I could have supplier.accept(order) (Supplier is the user in this context). Or if I don't use the supplier then it would be something like orderService.acceptOrder(order) which does not seem very domain like. This is the crux of my question really, things do not do stuff, usually it's a person doing something.
    – Ced
    Commented Aug 22, 2022 at 18:03
  • @Ced in what way User enforces Order's invariants? To my understanding User is not even a domain concept. It's an application concept. Orders don't even need to be correlated with Users. Not even customers. Only billing and shipping Addresses (usually), stock (availability) etc. What does your ubiquitous language of the domain says about users? Are they users or customers? Customer is only someone who already bought something while users are (usually) actors that interacts with the app.
    – Laiv
    Commented Aug 23, 2022 at 9:41
  • @Laiv this was an example out of my head, so I'll take one that is closer to what we are building, which is a booking system. There are users which can be host or renters depending on the context. Ultimately those are both user. What I mean is that having renter.book(hotel) sounds more like domain talk. This has nothing to do about invariants, there might or might not be domain invariants to be respected here, this is outside this scope to me. BookingService.book(user, hotel) would sound less like domain talk.
    – Ced
    Commented Aug 23, 2022 at 22:35
  • 1
    Aggregate (the lifecycle management pattern) has very little to do with how we write code optimized for domain literate audiences. And it really doesn't help matters that we try to use subject-verb-object grammar in programming languages that want recipient.message() Commented Aug 23, 2022 at 23:39
4

I think you are mistaken about the concept. Like said in this excellent answer it’s a word for a complicated concept. The user can very often be “an” aggregated root, but in complex systems you often have hundreds of aggregated roots, that can have a hierarchy on their own.

If you connect all methods to a User object you will get something similar to a God object.

In the case of stackoverflow i.e. I want to query questions with or without the context of a User. I prefer to do that consistently with something like a QuestionRepository and sometimes passing a User, in contrast to sometimes calling a method on the User and sometimes somewhere else.

Note that it often takes a while before you really understand your domain very well. I often change my aggregate roots over the years, because I understand the domain better.

Don’t worry about making mistakes. Just make sure that you make it as easy as possible to change your underlying structures if you grow your knowledge about the domain along the way.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.