This is a long question so i am gonna go straight to the point. This is pseudo code for better illustration of the problem
DB Structure
User (UserID, Name, LastName)
Address(AddressID, UserID, Street, City, State, ZipCode) =>Many to One User relationship
Phone (PhoneID, UserID, Number, IsPrimary) =>Many to One User relationship
Domain Classes
class User:IEntity
{
public string Name {get;set;}
public string LastName {get;set;}
public ContactInfo{get;set;}
}
class Phone: IValueObject or IEntity? will see later.
{
public int id; // persistence ID, not domain ID
public string Number {get;set;}
}
class Address: IValueObject or IEntity? will see later.
{
public string Line1 {get;set;}
public string City {get;set;}
public string State {get;set;}
public string ZipCode {get;set;}
}
class ContactInfo: IValueObject or IEntity? will see later.
{
List<Address> Addresses {get;set;}
List<Phone> PhoneNumbers {get;set;}
}
So, so far we have a very basic representation of this domain and its models.
My question is the following. Let’s say that i want to Update one of the addreses or fix the area code for one of the numbers because of misspelling wnen it was initially typed in.
If i follow Evan’s bible about DDD, Value Objects should be immutable. Meaning, no changes to its properties or fields after it was created.
If that’s the case, then i guess, none of my classes are a ValueObject, since i can’t just recreate the whole ContactInfo class just because one portion of the string in the phone number is wrong. So, i guess that makes all my classes Entities?
Keep in mind that i have a “persistence id” for each of this classes since they are stored in a database.
Let’s say that i decide to make Phone a value object, since it’s easy to recreate in the constructor
public Phone(string newNumber)
so, it would be something like adding a method to User (agg root) AND contactinfo? (Demeter Law)
like…
User....
public void UpdatePrimaryPhoneNumber(string number)
{
this.ContactInfo.UpdatePrimaryPhoneNumber(number);
}
ContactInfo....
public void UpdatePrimaryPhoneNumber(string number)
{
var oldPhone = Phones.Where(p=>p.IsPrimary).Single();
var newPhone = new Phone(number, oldPhone.persistenceid???-> this is not part of the domain)
oldPhone = newPhone;
}
but i still have to deal with persistence id… grrrrr. what a headache.
Sometimes i feel when i read those blogs that most “ddd experts” that value objects are overused or i would say misused.
What would be the best solution to this scenario?
Thank you
An Entity has a rather unique and individual life-cycle. It has meaning when it stands alone.
The classic example of
Order/OrderItemmay help with this.If an
OrderItembecomes an Entity it would have a life-cycle of its own. However, this doesn’t make too much sense since it is part of anOrder. 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, anOrderItemrepresents someProductthat we are selling. AProducthas a life-cycle of its own. We can have an independent list ofProducts. How we model the link between anOrderItemand theProductis probably another discussion but I would denormalize theProductdata I require into theOrderItemand store the originalProduct.Idalso.So is the
Addressclass 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
Addresss and then only have a need for the link to thatAddressin yourUser. If this is the case then it is an Entity. If, however, yourAddressmakes sense only when it is part of yourUserthen 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
ContactInfoclass 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 relevantPhoneNumber. 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/Addwould 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 primaryPhoneNumber, 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.