Map Value objects to database with EF Core

Value objects are centric for a good DDD implementation. Entity framework has now a great feature to customize the way it is mapped to a database

Introduction

In the context of Domain-Driven Design (DDD) development, it is essential to use value objects to check invariants as close to the source as possible. It is also good practice to strongly type all properties of our domain. For this reason, it is common to use value objects for Id properties, rather than plain integers or strings. This allows us to check for nullity, format, or other invariants directly within the value object class.

When it comes time to store the Id in a relational database, the question arises as to how to store this value object.

Presentation of the issue

Let’s take an example of a Person class that would look like that :

public class Person
{
    public PersonId Id { get; private set; }
}

Here, the Id property is represented by the PersonId class, which could look like this in its simplified version :

public class PersonId
{
    public int Value { get; private set; }

    public PersonId(int value)
    {
        if (value <= 0)
        {
            throw new ArgumentException("PersonId value should be > 0");
        }
				Value = value;
    }
}

Now, what happens if we create an Entity Framework Db context with the Person class an an entity and generate the corresponding migration ?

The database diagram that was generated with the default behavior of Entity Framework
Note that I had to tweek a bit the DbContext options to bypass some default restrictions and generate this database.

It created two tables in our database ! One for the Person class and one for the PersonId class and created a FK between the two. That’s of course not what we want for storage as the PersonId is only useful to check some invariants.

So how can we actually store the value of the PersonId as the database Id of the Person class / table ? There is actually a very easy solution since EF Core 2.1 : Value Conversion.

The easy solution : Value Conversion

Value conversion is a fairly simple solution that allows you to precise what transformation should be applied when transforming your objects to database specifics, in both directions.

In our case, we want the Value property of the PersonId class to be used directly as the primary key for our Person entity. Value conversions can be specified in the OnModelCreating method  of our DbContext. Here is how we would apply it in out context :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<Person>()
        .HasKey(p => p.Id);

    modelBuilder.Entity<Person>()
        .Property(p => p.Id)
        .HasConversion<int>(personId => personId.Value, dbId => new PersonId(dbId));
}

This configuration tells two things :

  • Use the property Id of the Person entity as a primary key (even if currently it is an object)
  • Whenever you are translating the Person class to or from the database, you have to go through conversion for the Id property. The HasConversion method takes a first parameter of the conversion from PersonId to int (from classes to database) and the second parameter to convert an int to the PersonId equivalent (from database to classes)

Conclusion

EF Core has evolved significantly in recent months/years to facilitate the implementation of DDD best practices, and the Value Conversion technique is one of them. This technique is not limited to just the ID case as presented here, but rather, it allows for a more general approach to completely ignoring how data is stored in the database and focusing on the domain, while keeping it ignorant of the persistence.

If you found the content informative, don't forget to subscribe to my newsletter to stay informed of upcoming content.

See you soon and stay safe !