How to deal with value objects in Entity Framework?

domain-driven-design entity-framework

Question

How do I persist value objects in Entity Framework without polluting my domain model? EF (well, relational DBs in general) require me to define a key - which my value objects don't have out of the box, for example

public class Tag : ValueObject<Tag>
{
   private readonly string name;

   public Tag(string name)
   {
      this.name = name;
   }

   public string Name { get { return this.name; }}
}

On the other hand, I shouldn't address persistence concerns in the model. Am I really supposed to create another class that includes all the fields from the value object plus a key property and then map them to each other? I'd rather not.

Is there maybe a more elegant solution?

1
19
5/23/2015 3:06:59 AM

Accepted Answer

Vaughn Vernon writes about Persisting Value Objects (page 248) in his excellent book Implementing Domain-Driven Design.

ORM and Single Value Objects

The basic idea is to store each of the attributes of the Value in separate columns of the row where its parent Entity is stored. Said another way, a single Value Object is denormalized into its parent Entity's row. There are advantages to employing convention for column naming to clearly identity and standardize the way serialized objects are named.

ORM and Many Values Backed by a Database Entity

A very straightforward approach to persisting a collection of Value instances using an ORM and a relational database is to treat the Value type as an entity in the data model. (...) To accomplish this we can employ a Layer Supertype.

Sample bounded contexts in C# can be found here: https://github.com/VaughnVernon/IDDD_Samples_NET

14
5/23/2015 7:15:10 AM

Popular Answer

I'm currently working through some of these same challenges. I'm not really a fan of adding an Id to your base ValueObject<T> class since this gives an Id to all value objects whether or not they are needed plus as a value object by definition has no Id, it's anything inheriting that base type will no longer be a value object in the pure sense.

Before I go further, I would note that a key concept in coding DDD is that you don't have to be pure DDD everywhere, just so long as you know what concessions your making and their trade-offs. That being said, your approach can certainly be considered fine, however I believe it's adds a concession that may not really necessary. Primarily, this impacts the equality of your value objects. With the addition of Id, two Tags, even with the same name are no longer equal.

Here are my approaches to this situation: First the easy one, not really applicable to what I think your problem is but it's important. This is the single value object in the first part of Martin's answer.

  • Make the Value Object a Property of an Entity.

As long as your value object consists of just simple type properties, Entity Framework will map this just fine.

For Example:

    public class BlogEntry : Entity<Guid>
    {
         public String Text { get; private set; }
         public Tag Tag { get; private set; }

         // Constructors, Factories, Methods, etc
    }

Entity Framework will handle that just fine, what you will end up with is a single table BlogEntry that consists of simply of:

  • Id
  • Text
  • Tag_Name

Now I figure that's not really what your after in this case, but for many value objects it works out great. One I use frequently is a DateRange value object which consists of several properties. Then on my domain objects I simply have a property of the Type DateRange. EF maps those to the table for the domain object itself.

I bring this up because going back to the concession we made of adding Id to the ValueObject<T> base type, even though it Id may not be listed in your domain object concrete implementation, it is still there and will still get picked up by Entity Framework which for this, probably the most common value object use case no longer working quite as nicely.

OK, finally, onto your specific case (which I too have run into a few times). Here is how I have opted to handle the need for a entity to contain a list of Value Objects. Basically it boils down to expanding our understanding of the domain. Assuming the Tag value object is for recording Tag's in a blog post, the way I look at it is that a BlogPost contains a list of PostTag with the value of Tag. Yes it is one more class, but you don't need to add it for every value object, it's only needed when you have a list of value objects, and I think better expresses what is happening.

So here is an example of adding a list of a value object to an entity (using your value object of Tag above):

    public class BlogEntry : Entity<Guid>
    {
         public String Text { get; private set; }
         public ICollection<PostTag> PostTags { get; private set; }

         // Constructors:
         private BlogEntry(Guid id) : base(id) { }
         protected BlogEntry() : this(Guid.NewGuid()) { }

         // Factories:
         public static BlogEntry Create (String text, ICollection<PostTag> tags = null)
         {
             if(tags == null) { tags = new List<PostTag>(); }
             return new BlogEntry(){ Text = text, Tags = tags };
         }        

         // Methods:
         public void AddTag(String name)
         {
             PostTags.Add(PostTag.Create(name));
         }
    }

    public class PostTag : Entity<Guid>
    {
        // Properties:
        public Tag Tag { get; private set; }
        public DateTime DateAdded { get; private set; } // Properties that aren't relevant to the value of Tag.

        // Constructors:
        private PostTag(Guid id) : base(id) { }
        protected PostTag() : this(Guid.NewGuid()) { }

        // Factories:
        public static PostTag Create(Tag tag) 
        { 
            return new PostTag(){ Tag = tag, DateAdded = DateTime.Now };
        }

        public static PostTag Create(Tag tag, DateTime dateAdded) 
        { 
            return new PostTag(){ Tag = tag, DateAdded = dateAdded };
        }
    }

That will allow your BlogEntry to contain multiple tags without compromising value objects and Entity Framework will map it just fine without the need to do anything special.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow