Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Please clarify how create/update happens against child entities of an aggregate root

Tags:

After much reading and thinking as I begin to get my head wrapped around DDD, I am a bit confused about the best practices for dealing with complex hierarchies under an aggregate root. I think this is a FAQ but after reading countless examples and discussions, no one is quite talking about the issue I'm seeing.

If I am aligned with the DDD thinking, entities below the aggregate root should be immutable. This is the crux of my trouble, so if that isn't correct, that is why I'm lost.

Here is a fabricated example...hope it holds enough water to discuss.

Consider an automobile insurance policy (I'm not in insurance, but this matches the language I hear when on the phone w/ my insurance company).

Policy is clearly an entity. Within the policy, let's say we have Auto. Auto, for the sake of this example, only exists within a policy (maybe you could transfer an Auto to another policy, so this is potential for an aggregate as well, which changes Policy...but assume it simpler than that for now). Since an Auto cannot exist without a Policy, I think it should be an Entity but not a root. So Policy in this case is an aggregate root.

Now, to create a Policy, let's assume it has to have at least one auto. This is where I get frustrated. Assume Auto is fairly complex, including many fields and maybe a child for where it is garaged (a Location). If I understand correctly, a "create Policy" constructor/factory would have to take as input an Auto or be restricted via a builder to not be created without this Auto. And the Auto's creation, since it is an entity, can't be done beforehand (because it is immutable? maybe this is just an incorrect interpretation). So you don't get to say new Auto and then setX, setY, add(Z).

If Auto is more than somewhat trivial, you end up having to build a huge hierarchy of builders and such to try to manage creating an Auto within the context of the Policy.

One more twist to this is later, after the Policy is created and one wishes to add another Auto...or update an existing Auto. Clearly, the Policy controls this...fine...but Policy.addAuto() won't quite fly because one can't just pass in a new Auto (right!?). Examples say things like Policy.addAuto(VIN, make, model, etc.) but are all so simple that that looks reasonable. But if this factory method approach falls apart with too many parameters (the entire Auto interface, conceivably) I need a solution.

From that point in my thinking, I'm realizing that having a transient reference to an entity is OK. So, maybe it is fine to have a entity created outside of its parent within the aggregate in a transient environment, so maybe it is OK to say something like:

auto = AutoFactory.createAuto(); auto.setX auto.setY

or if sticking to immutability, AutoBuilder.new().setX().setY().build()

and then have it get sorted out when you say Policy.addAuto(auto)

This insurance example gets more interesting if you add Events, such as an Accident with its PolicyReports or RepairEstimates...some value objects but most entities that are all really meaningless outside the policy...at least for my simple example.

The lifecycle of Policy with its growing hierarchy over time seems the fundamental picture I must draw before really starting to dig in...and it is more the factory concept or how the child entities get built/attached to an aggregate root that I haven't seen a solid example of.

I think I'm close. Hope this is clear and not just a repeat FAQ that has answers all over the place.

like image 237
christian Avatar asked Jan 10 '11 17:01

christian


People also ask

Can an aggregate root reference another aggregate root?

What you cannot do is reference anything inside the other aggregate root.

Can aggregate roots share entities?

As you rightly pointed out, Entity instances shouldn't be shared between aggregates, as one aggregate wouldn't be aware of changes to the entity made through another aggregate and couldn't enforce its invariants.

What is aggregate root in domain driven design?

Aggregate Root is the mothership entity inside the aggregate (in our case Computer ), it is a common practice to have your repository only work with the entities that are Aggregate Roots, and this entity is responsible for initializing the other entities. Consider Aggregate Root as an Entry-Point to an Aggregate.

What is aggregate root in C#?

Aggregate root are cluster / group of objects that are treated as a single unit of data. I am sure lots of developers are already using this pattern unknowingly, via this short note I would like to inform you formally what you are doing.


2 Answers

Aggregate Roots exist for the purpose of transactional consistency.

Technically, all you have are Value Objects and Entities.

The difference between the two is immutability and identity.

A Value Object should be immutable and it's identity is the sum of it's data.

Money // A value object
{
    string Currency;
    long Value;
}

Two Money objects are equal if they have equal Currency and equal Value. Therefore, you could swap one for the other and conceptually, it would be as if you had the same Money.

An Entity is an object with mutability over time, but whose identity is immutable throughout it's lifetime.

Person // An entity
{
    PersonId Id; // An immutable Value Object storing the Person's unique identity
    string Name;
    string Email;
    int Age;
}

So when and why do you have Aggregate Roots?

Aggregate Roots are specialized Entities whose job is to group a set of domain concepts under one transactional scope for purpose of data change only. That is, say a Person has Legs. You would need to ask yourself, should changes on Legs and changes on Person be grouped together under a single transaction? Or can I change one separately from the other?

Person // An entity
{
    PersonId Id;
    string Name;
    string Ethnicity;
    int Age;
    Pair<Leg> Legs;
}

Leg // An entity
{
    LegId Id;
    string Color;
    HairAmount HairAmount; // none, low, medium, high, chewbacca
    int Length;
    int Strength;
}

If Leg can be changed by itself, and Person can be changed by itself, then they both are Aggregate Roots. If Leg can not be changed alone, and Person must always be involved in the transaction, than Leg should be composed inside the Person entity. At which point, you would have to go through Person to change Leg.

This decision will depend on the domain you are modeling:

Maybe the Person is the sole authority on his legs, they grow longer and stronger based on his age, the color changes according to his ethnicity, etc. These are invariants, and Person will be responsible for making sure they are maintained. If someone else wants to change this Person's legs, say you want to shave his legs, you'd have to ask him to either shaves them himself, or hand them to you temporarily for you to shave.

Or you might be in the domain of archeology. Here you find Legs, and you can manipulate the Legs independently. At some point, you might find a complete body and guess who this person was historically, now you have a Person, but the Person has no say in what you'll do with the Legs you found, even if it was shown to be his Legs. The color of the Leg changes based on how much restoration you've applied to it, or other things. These invariants would be maintained by another Entity, this time it won't be Person, but maybe Archaeologist instead.

TO ANSWER YOUR QUESTION:

I keep hearing you talk about Auto, so that's obviously an important concept of your domain. Is it an entity or a value object? Does it matter if the Auto is the one with serial #XYZ, or are you only interested in brand, colour, year, model, make, etc.? Say you care about the exact identity of the Auto and not just it's features, than it would need to be an Entity of your domain. Now, you talk about Policy, a policy dictates what is covered and not covered on an Auto, this depends on the Auto itself, and probably the Customer too, since based on his driving history, the type and year and what not of Auto he has, his Policy might be different.

So I can already conceive having:

Auto : Entity, IAggregateRoot
{
    AutoId Id;
    string Serial;
    int Year
    colour Colour;
    string Model
    bool IsAtGarage
    Garage Garage;
}

Customer : Entity, IAggregateRoot
{
    CustomerId Id;
    string Name;
    DateTime DateOfBirth;
}

Policy : Entity, IAggregateRoot
{
    string Id;
    CustomerId customer;
    AutoId[] autos;
}

Garage : IValueObject
{
    string Name;
    string Address;
    string PhoneNumber;
}

Now the way you make it sound, you can change a Policy without having to change an Auto and a Customer together. You say things like, what if the Auto is at the garage, or we transfer an Auto from one Policy to another. This makes me feel like Auto is it's own Aggregate Root, and so is Policy and so is Customer. Why is that? Because it sounds like it is the usage of your domain that you would change an Auto's garage without caring that the Policy be changed with it. That is, if someone changes an Auto's Garage and IsAtGarage state, you don't care not to change the Policy. I'm not sure if I'm being clear, you wouldn't want to change the Customer's Name and DateOfBirth in a non transactional way, because maybe you change his name, but it fails to change the Date and now you have a corrupt customer whose Date of Birth doesn't match his name. On the other hand, it's fine to change the Auto without changing the Policy. Because of this, Auto should not be in the aggregate of Policy. Effectively, Auto is not a part of Policy, but only something that the Policy keeps track of and might use.

Now we see that it then totally make sense that you are able to create an Auto on it's own, as it is an Aggregate Root. Similarly, you can create Customers by themselves. And when you create a Policy, you simply must link it to a corresponding Customer and his Autos.

aCustomer = Customer.Make(...);
anAuto = Auto.Make(...);
anotherAuto = Auto.Make(...);
aPolicy = Policy.Make(aCustomer, { anAuto, anotherAuto }, ...);

Now, in my example, Garage isn't an Aggregate Root. This is because, it doesn't seem to be something that the domain directly works with. It is always used through an Auto. This makes sense, Insurance companies don't own garages, they don't work in the business of garages. You wouldn't ever need to create a Garage that existed on it's own. It's easy then to have an anAuto.SentToGarage(name, address, phoneNumber) method on Auto which creates a Garage and assign it to the Auto. You wouldn't delete a Garage on it's own. You would do anAuto.LeftGarage() instead.

like image 117
Didier A. Avatar answered Sep 28 '22 08:09

Didier A.


entities below the aggregate root should be immutable.

No. Value objects are supposed to be immutable. Entities can change their state.

Just need to make sure You do proper encapsulation:

  • entities modifies themselves
  • entities are modified through aggregate root only

but Policy.addAuto() won't quite fly because one can't just pass in a new Auto (right!?)

Usually it's supposed to be so. Problem is that auto creation task might become way too large. If You are lucky and, knowing that entities can be modified, are able to divide smoothly it into smaller tasks like SpecifyEngine, problem is resolved.


However, "real world" does not work that way and I feel Your pain.

I got case when user uploads 18 excel sheets long crap load of data (with additional fancy rule - it should be "imported" whatever how invalid data are (as I say - that's like saying true==false)). This upload process is considered as one atomic operation.

What I do in this case...

First of all - I have excel document object model, mappings (e.g. Customer.Name==1st sheet, "C24") and readers that fill DOM. Those things live in infrastructure far far away.

Next thing - entity and value objects in my domain that looks similar to DOM dto`s, but only projection that I'm interested in, with proper data types and according validation. + I Have 1:1 association in my domain model that isolates dirty mess out (luckily enough, it kind a fits with ubiquitous language).

Armed with that - there's still one tricky part left - mapping between excel DOM dtos to domain objects. This is where I sacrifice encapsulation - I construct entity with its value objects from outside. My thought process is kind a simple - this overexposed entity can't be persisted anyway and validness still can be forced (through constructors). It lives underneath aggregate root.

Basically - this is the part where You can't runaway from CRUDyness.
Sometimes application is just editing bunch of data.

P.s. I'm not sure that I'm doing right thing. It's likely I've missed something important on this issue. Hopefully there will be some insight from other answerers.

like image 39
Arnis Lapsa Avatar answered Sep 28 '22 09:09

Arnis Lapsa