Reputation: 40092
In domain-driven design, we are encouraged to build rich entities which express real concepts in the domain.
Let's say we have an entity Vehicle
which references an aggregate root, Person
, by identity - a Vehicle
is owned by a Person
.
But, a Vehicle
can exist at a point of time without an owner, say just after it is manufactured.
How would you model this?
One way could be with a Nullable
type:
public class Vehicle : DomainEntity<Guid>
{
public Guid? Owner { get; private set; }
public Vehicle(...)
: base(Guid.NewGuid())
{
//...
}
public Vehicle(Guid owner, ...)
: base(Guid.NewGuid())
{
Owner = owner;
//...
}
}
Another way is to use multiple classes and inheritance:
public class Vehicle : DomainEntity<Guid>
{
public Vehicle(...)
: base(Guid.NewGuid())
{
//...
}
}
public class OwnedVehicle : Vehicle
{
public Guid Owner { get; private set; }
public OwnedVehicle(Guid owner, ...)
: base(Guid.NewGuid())
{
Owner = owner;
//...
}
}
I would much prefer to use the second approach, but can one really model aggregate roots like this?
What exactly would the aggregate root be; Vehicle
or OwnedVehicle
?
Would we now have two separate repositories or would the repository return the base type, Vehicle
? This would mean that we would need to cast - not ideal.
Upvotes: 4
Views: 1776
Reputation: 1948
As I understand it, a loose definition of an AggregateRoot is not being a subentity, and a subentity is something that can only be understood in reference to another parent entity. For example, a OrderLine only makes sense in terms of an Order.
A Person and a Car are not this - you can have a Person without a Car, and a Car without a Person in your example.
For cases like this, you have a Service that manages relationships between Aggregate Roots.
https://lostechies.com/jimmybogard/2008/08/21/services-in-domain-driven-design/
The operation relates to a domain concept that is not a natural part of an Entity or Value Object
The interface is defined in terms of other elements in the domain model
The operation is stateless
So in this case, you might have a CarOwnersipService, that can update Cars and People to make ownership happen, but who's details are not known to the outside world. A service makes sense here because depending on the application the rules may be different. For example, perhaps you can view who owns a car on a mobile device, but only assign ownership in another application?
What gives me pause here is the operation being stateless - if we consider an idempotent service to be stateless, it does satisfy the definition, but otherwise it does not.
Upvotes: 3