It can be challenging sometimes to know how best to persist value objects to a data store, especially if you are using a RDBMS. There are a variety of options to choose from, however, depending on your needs and constraints.
Examples below are based on my Ruby port of the DDD sample app. Here is a class diagram showing the
Cargo aggregrate, which consists of the
Cargo entity (as the aggregate root) and a number of value objects, such as
RouteSpecification that are also part of the
Cargo aggregate. This is based on examples given in Eric Evans’s Domain-Driven Design book.
Inline Value Object’s Fields to Entity’s Table
This is the simplest arrangement, both in terms of ORM mapping and database schema design. It also is an easy way to support refactoring a value object out of an entity, since only the objects and the ORM mapping would need to change; there is no change to the underlying database schema.
An example of this would be refactoring properties such as
Arrival Deadline out of their original location in the
Cargo entity and into a new
RouteSpecification value object. The
RouteSpecification value object, having a one-to-one, relationship with the
Cargo entity, lends itself very naturally to this kind of approach. The same would be true for the
Itinerary value object.
Create Database Table for each of Value Object Type and Represent Data as Field
In the DDD sample app, a single
Itinerary value object has many
Leg value objects. So an example of this approach would be to persist the
Legs value object to a
Legs database table that has a foreign key back to the Itinerary database table. This is a common approach for 1-Many relationships. However, see the next serialization approach as perhaps a more effective solution in many cases.
Serialize the Value Object and Store in Field in Entity’s Table
This is a less common than creating a separate table for the value object as in the previous approach, but serialization is actually the method I would recommend in most cases where you have a one-to-many relationship. For example, persist
Itinerary and all its
Leg value objects as a serialized object grap and then inline it into the Entity’s table within the Cargo database table.
Use an appropriate serialization format such as plain text, XML, YAML or JSON. Databases such as Postgres and SQL Server provide native format for such approaches. For a Postgres example, see Vaughn Vernon’s post The Ideal Domain-Driven Design Aggregate Store? For an example of manipulating XML with SQL Server, see Manipulating XML Data in SQL Server.
Querying and indexing options may be limited as in SQL Server 2008 XML columns, or even non-existent in many RDMS’s. An ORM will typically need custom code or a custom type, such as in Hibernate, to manage the serialization/deserialization of each collection.
Serializing objects to text-based formats gives you human-readable database data, but is comparatively slow and potentially huge in size compared to regular database columns. It is important to verify that this approach can handle the performance needs before going too far with it. Also, if the serialized value object stored in the DB is opaque (e.g., the only query performed on the column is to serialize/deserialize based on primary key lookep) then consider using a much faster (and typically smaller) format like binary.
Combined RDMS and Document Database Approach
In the Java space, SpringSource has been doing some very interesting work for the Spring.Data.Commons and related projects in the area of what they call 'cross-store persistence.'  Portions of your object are persisted to a RDBMS and other portions are persisted to NoSQL stores (and the framework handles persisting and rehydrating the right parts to/from the right data store for you). These are all OSS solutions.
Use a Document Database
CargoRepository could use an
Itinerary document Id stored in a
Cargo field to retrieve the
Itinerary value object stored in a document database such as MongoDB, RavenDB or CouchDB. Note that many document databases offer transparency of query into the contents of the 'document' (i.e. serialized value object in this case), which removes the need to predict which properties, etc. of the value object need to be stored separately in the table as additional fields just so that they can be queried.
|O-R||Represent Data in Fields||Serialize Object||Reference to Other Storage|
Inline in Entity Table
* Good O-R mapper support * Straightforward object/DB schema versioning * Factoring fields into Value Object doesn’t change DB schema
* Version management of serialization * Some performance concerns * Query limitations * Field length limitations
* Hybrid approach – store document Id in Entity’s table and lookup in Repository * Queries supported by document databases
* Good O-R mapper support, but must put ID field in Value Object * Straightforward object/DB schema versioning
About the Author
If you are looking for an expert hands-on team coach and design mentor in Domain-Driven Design (DDD), BDD with Cucumber, or lean/agile process Paul is available for consulting and training through his company, Virtual Genius LLC.