DDD: Can a Value Object have lists inside them?
Asked Answered
B

1

10

I'm not well versed in domain driven design and I've recently started created a domain model for a project. I still haven't decided on an ORM (though I will likely go with NHibernate) and I am currently trying to ensure that my Value Objects should be just that.

I have a few VOs that have almost no behavior other than to encapsulate "like" terms, for instance:

public class Referral {
    public Case Case { get; set; } // this is the a reference to the aggregate root
    public ReferralType ReferralType { get; set; } // this is an enum
    public string ReferralTypeOther { get; set; }
} // etc, etc.

This particular class has a reference to "Case" which is two levels up, so if say I were going to access a Referral I could go: case.social.referral (Case, Social and Referral are all classes, there is a single Social inside a Case and there is a single Referral inside a Social). Now that I am looking at it as I type it, I don't think I need a Case in the Referral since it will be accessible through the Social entity, correct?

Now, there is no doubt in my mind this is something that should be a VO, and the method I plan to use to persist this to the database is to either have NHibernate assign it a surrogate identifier (which I am still not too clear on, if anyone could please elaborate on that too it would help me out, since I don't know if the surrogate identifier requires that I have an Id in my VO already or if it can operate without one) and/or a protected Id property that would not be exposed outside the Referral class (for the sole purpose of persisting to the DB).

Now on to my title question: Should a VO have a collection, (in my case a List) inside it? I can only think of this as a one-to-many relationship in the database but since there is no identity it didn't seem adequate to make the class an entity. Below is the code:

public class LivingSituation {
    private IList<AdultAtHome> AdultsAtHome { get; set; }
    public ResidingWith CurrentlyResidingWith { get; set } // this is an enum
} // etc, etc.

This class currently doesn't have an Id and the AdultsAtHome class just has intrinsic types (string, int). So I am not sure if this should be an entity or if it can remain as a VO and I just need to configure my ORM to use a 1:m relationship for this using their own tables and a private/protected Id field so that the ORM can persist to the DB.

Also, should I go with normalized tables for each of my classes, or not? I think I would only need to use a table per class when there is a possibility of having multiple instances of the class assigned to an entity or value object and/or there is the possibility of having collections 1:m relationships with some of those objects. I have no problem with using a single table for certain value objects that have intrinsic types but with nested types I think it would be advantageous to use normalized tables. Any suggestions on this as well?

Sorry for being so verbose with the multiple questions:

1) Do I need a surrogate identifier (with say NHibernate) for my value objects?

2) If #1 is yes, then does this need to be private/protected so that my value object "remains" a value object in concept?

3) Can a value object have other value objects (in say, a List) or would that constitute an entity? (I think the answer to this is no, but I'd prefer to be sure before I proceed further.)

4) Do I need a reference to the aggregate root from a value object that is a few levels down from the aggregate root? (I don't think I do, this is likely an oversight on my part when writing the model, anyone agree?)

5) Is it OK to use normalized tables for certain things (like nested types and/or types with collections as properties which would need their own tables anyway for the 1:m relationship) while having the ORM do the mapping for the simpler value objects to the same table that belongs to my entity?

Thanks again.

Barbados answered 30/11, 2009 at 7:6 Comment(3)
What is your most pressing question? Maybe you should go with that one first.Those
Domain-Driven Design isn't really a development technique anyway; it's a way to provide a common language between you and the customer.Those
I guess questions #1 and #2 have to do with the way NHibernate persists objects to the database, I can probably look that up on their website. But #3 and #4 are questions that I need clarity on because I would like to follow the patterns as closely as I can and I'm not sure about those. And question #5 also has to do with NHibernate and what is considered "best practice". They are all pressing because they are all related.Barbados
B
13

Take a look at the answers to related questions here and here


1) Yes - If you're storing VOs in their own table

2) If you can use a private/protected ID property, then great. Alternatively, you might use explicit interfaces to 'hide' the ID property.

But, reading into your question, are you suggesting that developers who see an ID property will automatically assume the object is an entity? If so, they need (re)training.

3) Yes it can, but with the following restrictions:

  • It should be quite rare
  • It should only reference other VOs

Also, consider this: VOs shouldn't stick around. Would it be easy/efficient to re-create the entire VO every time it's needed? If not, make it an Entity.

4) Depends on how you want to implement your Aggregate Locking. If you want to use Ayende's solution, the answer is yes. Otherwise, you would need a mechanism to traverse the object graph back to the Aggregate Root.

5) Yes. Don't forget that DDD is Persistence Ignorant (in an ideal world!).


However...

I believe Referral should be an Entity. Imagine these conversations:

Conversation 1:

  • Tom: "Hey Joe! Can you give me David Jone's referral?"
  • Joe: "Which one?"
  • Tom: "Sorry, I mean Referral No.123"

Conversation 2:

  • Tom: "Hey Joe! Can you give me David Jone's referral?"
  • Joe: "Which one?"
  • Tom: "I don't care - just give me any"

Conversation 1 suggests that Referral is an Entity, whereas conversation 2 suggests it's a VO.

One more thing: Does Referral.ReferralType change during it's lifetime (there's another hint that it should be an Entity)? If it doesn't change, consider using polyporphism and let NH handle it.

Hope that helps!

Barvick answered 30/11, 2009 at 8:23 Comment(3)
Thanks, I'd already seen those, and I can agree that they answer #1 and I guess #2 is an implementation detail that I can get by reading NHibernate's documentation. I would like some clarification on #3 and #4 as well as #5 (though as I stated in my earlier comment) I consider #5 to be more of a 'best practice' approach - should I do it that way or is there a better way?Barbados
Thanks! ReferralType is not going to change once the Referral is created, but you make a valid point with that conversation, it would make sense to give it an Id.Barbados
Thinking about it though, I think I am going to create an IValueObject interface and give it an Id property and have only the VOs that will have their own table derive from it and explicitly implement it. I will think hard about Referral and a few other types that were originally value objects but do need a type of identity since the information in them will be specific and should be ordered (to track progress etc), so they should be entities I think. I will read up on Ayende's post on aggregate locking as well. Thanks again!Barbados

© 2022 - 2024 — McMap. All rights reserved.