For terminology in Domain Driven Design, start from "the blue book" -- Domain Driven Design by Eric Evans.
AGGREGATE
A cluster of associated objects that are treated as a unit for the purpose of data changes. External references are restricted to one member of the aggregate, designated as the root. A set of consistency rules applies within the aggregate's boundaries.
That last sentence, I think you can turn around -- the boundaries of the aggregate are defined by the consistency rules.
It's definitely the case that an aggregate has state. Each time the domain model changes, an aggregate is taken from one consistent state to another. The data that we persist is used to reconstruct this state. So in that sense, it is a real thing.
But the aggregate itself doesn't necessarily have a word in the ubiquitous language. It's a derived concept.
Broadly, we could put the entire domain model under a single aggregate, that enforces all of the consistency rules. We don't, because it that design doesn't scale: we can't change the domain model two different ways at the same time, even when the changes we are making don't share any consistency rules. It's a poor way to model a business that can do more than one thing at a time.
Instead, we decompose the consistency rules into sets, subject to the constraint that two rules that reference the same data must be part of the same set. (In doing this, we are also working with the ubiquitous language and the domain experts to determine if we are correctly describing the consistency rules).
To update the model, we identify the aggregate responsible for a piece of data and propose the change. If the aggregate verifies all of its local consistency rules, we know that the change is globally valid, and we can apply the change. This restores our ability to do more than one thing at a time - changes to data in different aggregates can't possibly conflict with each other, by construction.
Best practices suggest that most aggregates should contain only the root entity. So you can conflate the aggregate with the entity without too much risk. But my guess it there won't usually be anything in the ubiquitous language to hang on the cluster when it includes more than one entity; so you end up with the ShoppingCart aggregate maintaining the consistency rules for the ShoppingCart entity and the CartItems entity collection and....
Partial loading of an aggregate is broken when trying to apply a change -- how could a well designed aggregate possible validate all of its consistency rules with a subset of the data? It's certainly the case that, if you have a requirement where this makes sense, your modeling is broken somewhere.
But if you are doing a read, loading only some of the data guarded by the aggregate can make sense. Command Query Responsibility Separation (CQRS) takes this a step further; once the model has verified that the data satisfies the consistency rules, you can completely rearrange that data into whatever read only form makes your life easiest. Put another way, if you aren't concerned with data changes, you don't need to worry about the aggregate boundary at all.
The thing is, I don't think that complexity or size should be dictating if it's an Aggregate, Entity or Value Object, but rather its MEANING.
The thing is, these terms have definitions spelled out in the blue book
From chapter Five. A Model Expressed in Software
Does an object represent something with continuity and identity--something that is tracked through different states or even across different implementations? Or is it an attribute that describes the state of something else? This is the basic distinction between an ENTITY and a VALUE OBJECT.
From chapter Six. The Life Cycle of a Domain Object.
AGGREGATES mark off the scope within which invariants have to be maintained at every stage of the life cycle.
An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes.
It's entirely consistent to have an aggregate that contains only a single entity, that entity having only a single interesting property, and only simple behaviors that change that property from one value to another.
I would like to know your thoughts on my solution attempt.
The basic shape is fine: your WebApp
entity is also the root of the aggregate, which is consistent with the pattern as described by Evans.
Raising the domain event in the constructor may be an error; it's certainly a code smell (raising a domain event certainly counts as real work).
Best Answer
There seems to be a confusion between domain driven design concepts, API design and implementation approach.
Domain design concepts
First a few definitions of Evan's DDD reference book:
And keep in mind that this is about the domain. We don't care here how the relationships between the objects will be implemented. Therefore, from what you say:
User
and relatedPosts
belong to the same aggregate, andUser
is the root entity thereof. This says that whenever I want to refer to aPost
, however it is implemented, I have to access it via theUser
.Post
within the aggregate is an entity or a value object. The difference is about identity semantics: is a post that is updated (e.g. correction of a typo) still the same post or would if be considered as another post ? Personally I'd opt for the entity, but up to you to see what fits best your domain.API design & implementation model
The domain model is independent of any specific implementation. But in order to implement it, you'll have to define:
The domain model shall not be a prison that imposes you to blindly do things that hamper performance, but a guide to structure better your software.
In your specific case, you could for example use lazy loading: your API may give the impression that everything is loaded at once with the
User
, but in reality, theposts
will not be loaded until someone tries to access it via theUser
(thanks to encapsulation).But you could also define an API that seems to give direct access to the entities in the aggregate, but which enforce the access to
Post
to go viaUser
, so that you have the opportunity to safeguard the consistency of the related objects.You could also, as you suggest yourself, create different kind of
User
objects in your application to determine what is to be loaded and what could be changed. But then it's an implementation model and no longer the conceptual domain model anymore.