Keep EntityFramework state objects valid by encapsulating it in domain entities

There are already a lot of great posts (f.e. by Vaughn Vernon) about how to use EntityFramework with Domain Entities.
So why am I writing about? Because I think it is important not to leak your state objects to your entire application.

EntityFramework cannot map private fields, so you need to make all mapped properties public.
The domain entity is responsible to make sure that the state object is always in a valid state.
EntityFramework does not work with ValueObjects, so you cannot use ValueObjects in a nice way in your state. So another plus for Domain Entities.
A state object does not necessarily need to be an entity, it could also be a ValueObject when you don’t care about uniquely identifying the ValueObject. The state will still have an id, but you can hide it in your ValueObject.

In this example I’m talking about products, they are stored with entity framework as ProductState entities.

    public class ProductState
    {
        public Guid Id { get; set; }
        public string ProductCode { get; set; }
        public string Title { get; set; }
    }

Constructing

It starts with the constructors. I usually create two constructors, one for reviving the entity from the database and one for newly constructing the entity. Every constructor should result in a valid Product and ProductState.
The following constructor revives the product from it’s state.

    public class Product
    {
        private readonly ProductState _state;
        public Product(ProductState productState)
        {
            Assert.NotNull(productState, nameof(productState));

            _state = productState; 
        }
    }

The state is provided from the repository. It is already in valid state, a null check is sufficient.
Noticed the nameof? This new trick can come in very handy f.e. for logging purposes.
In this example, a product is valid when it has a ProductCode, this is the unique identifier of the product. The constructor for creating a new product is as follows:

    public class Product
    {
        private readonly ProductState _state;
        public Product(ProductCode productCode)
        {
            Assert.NotNull(productCode, nameof(productCode));

            _state = new ProductState
            {
                Id = Guid.NewGuid(),
                ProductCode = productCode.Value,
            };
        }
    }

The Product entity is responsible for instantiating a ProductState and make sure it is in valid state. The Id is the primary key for EntityFramework, the product code is used as surrogate identifier.

Exposing data

Now that we have instantiated a Product, we can use it in our application.
The private (and readonly) state is used as backing variable for every get or set method/property.
The methods in Product look as follows

        public ProductCode GetProductCode()
        {
            return new ProductCode(_state.ProductCode);
        }

        public string GetProductTitle()
        {
            return _state.ProductTitle;
        }

        public void SetProductTitle(string title)
        {
            Assert.NotNull(title, nameof(title));
            if (title.Length > 255)
            {
                throw new ArgumentException("ProductTitle cannot be more than 255 characters.");
            }
            _state.ProductTitle = title;
        }

There is no Set method for ProductCode? Correct! The ProductCode is the identifier for the Product, it is immutable. A different product code is a different product, so this requires instantiating a new/differnt Product.
The ProductTitle does not identifies the Product, so there is a set method for the ProductTitle. In this set method, there are some business rules; in this example the ProductTitle cannot be null and should not be more than 255 characters. This makes sure that the state object could never get an invalid title in the ProductTitle property.
I prefer void as return type for set methods. When the provided data is invalid I throw exceptions. Returning a bool to indicate if the operation went succesfull has some disadvantages, f.e.:
– It does not give any detail of what went wrong
– It suggests we can still continue normally.
In this example I use methods for the Get operations, this could as well have been properties.

Attaching the State Entity to EntityFramework

Unfortunately, There is a downside to this. Now that the Product creates the ProductState, we need to attach it to the DbContext before EntityFramework will pick it up.
So we do need to expose the inner state entity. I always try to make it internal so not everybody can reach it, but there are (many) situations when internal is not enough and you need to make it public.

        internal ProductState InnerState
        {
            get { return _state; }
        }
The following two tabs change content below.
I'm a software developer from Utrecht. Interested in DDD, continuous delivery, new technologies & frameworks.

Latest posts by Vincent Keizer (see all)

Leave a Reply

Your email address will not be published. Required fields are marked *