An Entity has a rather unique and individual life-cycle. It has meaning when it stands alone.
The classic example of Order
/OrderItem
may help with this.
If an OrderItem
becomes an Entity it would have a life-cycle of its own. However, this doesn't make too much sense since it is part of an Order
. This always seems obvious when looking at an order but less so when looking at your own classes because there can be some references between classes. For instance, an OrderItem
represents some Product
that we are selling. A Product
has a life-cycle of its own. We can have an independent list of Product
s. How we model the link between an OrderItem
and the Product
is probably another discussion but I would denormalize the Product
data I require into the OrderItem
and store the original Product.Id
also.
So is the Address
class an Entity or a Value Object? This is always an interesting one in that we have that favourite of answers: it depends.
It will be context-specific. But ask yourself whether you have (or need) an independent list of Address
s and then only have a need for the link to that Address
in your User
. If this is the case then it is an Entity. If, however, your Address
makes sense only when it is part of your User
then it is a Value Object.
The fact that a Value Object is immutable does not mean you need to replace more than just the specific Value Object. I don't know if I would have a ContactInfo
class in your current design since it only wraps the two collections (Address
/PhoneNumber
) but I would keep it if there is more to it (probably is). So simply replace the relevant PhoneNumber
. If you have something like primary/secondary then it is as simple as:
AR.ReplacePrimaryPhoneNumber(new PhoneNumber('...'))
If it is a list of arbitrary numbers then a Remove
/Add
would be appropriate.
Now for the persistence Id
. You do not need one. When you have a primary/secondary scenario you know what your use case is and you can execute the relevant queries in your DB (to update the primary PhoneNumber
, for instance). If you have an arbitrary list you may go for add all new numbers in my list and delete those numbers from the DB not in my list; else just delete all the numbers and add everything you have. If this seems like a lot of heavy movement: it is. Event sourcing would move a lot of this to in-memory processing and it is something I will be pushing for seriously going forward.
I hope this all makes sense. Getting away from focusing on the data side of things is rather difficult but necessary. Focus on the domain as though you have no database. When you find friction then do your utmost to not pull database thinking into your domain but try to think about ways you could keep your domain clean and still use your DB of choice.