Working With Value Objects
page 2 of 3
by Steven Smith
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 20806/ 31

Finding Value Objects in the Design

In Domain Driven Design, Eric Evans describes Value Objects:

"An object that represents a descriptive aspect of the domain with no conceptual identity is called a VALUE OBJECT.  VALUE OBJECTS are instantiated to represent elements of the design that we care about only for what they are, not who or which they are."

One of the nice things about VALUE OBJECTs is that they can be treated as immutable.  That is, when created, they cannot be modified.  Some more examples of basic value objects are colors and strings (which as it happens are immutable in .NET).  At a higher level, a very common example of an object that should probably be captured as a Value Object but often isn't in my experience is customer data related to an order.

In a typical, naïve design of an order system, you might have something like this:

When this is persisted, naturally there will be foreign keys linking OrderItem to Product and Order to Customer.  But what happens when, a year later, you want to see what a customer ordered and the corresponding Customer and Product records have since been updated?  Maybe the price on the Product in question has gone up, or its name has changed.  Maybe the customer has changed their address or billing contact.  From the perspective of the Order at the time it was placed, the Product data and Customer data were important for their characteristics at that moment in time, not their identity.  One possible solution to this problem is to introduce an Invoice that is most likely a Value Object itself, which captures all of this snapshot data.  Another is to create separate Value Objects for the things the Order is concerned with that should be immutable from the Order's perspective.

You can think of the product and customer details related to the Order as being a snapshot of their values at the point in time at which the Order is created or placed.  Since it certainly makes sense that there should be Product and Customer entities, with identities, in our system, we need a new type to represent these snapshots, which we might simply choose to name FooSnapshot, like so:

    public class ProductSnapshot
        private readonly string _name;
        public string Name { get { return _name; } }
        private readonly decimal _unitPrice;
        public decimal UnitPrice { get { return _unitPrice; } }
        public ProductSnapshot(string name, decimal unitPrice)
            _name = name;
            _unitPrice = unitPrice;
        public ProductSnapshot(Product product)
            _name = product.Name;
            _unitPrice = product.UnitPrice;
        public override bool Equals(object obj)
            ProductSnapshot other = (ProductSnapshot) obj;
            return other != null
                   && this.GetType() == other.GetType()
                   && Name.Equals(other.Name)
                   && UnitPrice.Equals(other.UnitPrice);


Now we have a simple way to create an immutable copy of the Product fields we are interested in at the time the Order was created, and we can modify OrderItem to refer to a ProductSnapshot rather than to a Product.  One thing both our Product and our ProductSnapshot are missing is some kind of persistence-ignorant Stock Keeping Unit (SKU) number, which could be used to link a particular ProductSnapshot back to its parent Product if necessary.

Note that since Value Objects can be used interchangeably and should be immutable, we have made it impossible to change the properties of this object (without resorting to reflection) once it is created.  We have also overridden the Equals() operation so that we can compared two separate instances of ProductSnapshot and if all of their properties are equal, we will consider the two objects to be equal.  The code above is a modified version of code found in Jimmy Nilsson's excellent book, Applying Domain Driven Design and Patterns (chapter 5).

In terms of persistence, since the Value Object does not have an ID of its own, the simplest way to persist it is with the Entity it describes.  In this case, we could choose to store values directly in the OrderItem table.  This can result in duplicate data, but remember that the data is only duplicate by coincidence.  Its meaning is unique to each OrderItem row, since any later change to the unit price or name of the product should not affect this row.  It's no different than the quantity column of our OrderItem table, which likely has a lot of duplicate data as well.  I doubt that anybody would suggest that we move Quantity's values of 1, 2, 3, etc. off to another table and assign some identities to these values in order to normalize OrderItem.  That said, if space is a major concern, the snapshot values can be stored in a separate table and normalized, but care must be taken to ensure that this table is immutable, since otherwise it will be possible to corrupt the historic value of an OrderItem's product name and unit price.

View Entire Article

User Comments

Title: More Design Patterns   
Name: Steve Smith
Date: 2011-07-15 11:50:34 AM
I talk more about design patterns on my blog where I have a ton of links to more resources. Check it out here:
Common Design Patterns - Resources
Title: object   
Name: vijay
Date: 2011-07-15 8:10:18 AM
nice article

Community Advice: ASP | SQL | XML | Regular Expressions | Windows

©Copyright 1998-2024  |  Page Processed at 2024-06-14 1:59:36 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search