EF configurations are sick! (and work great with DDD)

iamprovidence
5 min readMay 7, 2023

--

Entity Framework has established itself as a mature product that can be used to solve any problem. Despite that, not many developers have been using it effectively. Simply because they don’t know about its full possibilities.

Even though, it works perfectly for small projects, when stuff gets more complicated developers just refuse to use it. I have seen multiple unsuccessful attempts to collaborate DDD and EF. Some of those are so desperate, that developers would just avoid EF’s features and create separate models for Domain and Data Access layers, with additional mapping logic in between.

Stay with me if you want to know how to get rid of code duplication and configure complex mapping from your DDD model to Database. We will briefly review next topics:

  • Global query filters
  • Owned Entity Types
  • Backing Fields
  • Excluded Properties
  • Shadow Properties
  • Value Conversions

If you are ready, let’s begin.

A little bit about our Domain

Imagine you are working in an online store. Your task is to design a domain model and configure persistent layers for it. After some time, you end up with something below (do not focus much here, we will discover it later):

There is nothing extraordinary. Just a few classes that follow DDD guidelines. So when you are finished with your domain, it is time to configure EF.

Global Query Filters

One of the first requirements for our model was to support soft delete. Since it is important for our customer that nothing is deleted, you had to add a few interfaces:

Quickly enough, you have realized that it creates horrible API. Every time when you fetch your data, it is important to add a filter based on IsDeleted field. Bruh… 😨

I wish we could specify a global filter that would be automatically added to all the queries 🤔 Wait a second, it is actually possible 😁 For that you need to add QueryFilter and it can be done like this:

There is no longer need to filter data every time it is fetched. However, it is still important to add that line every time we would like to configure soft delete for a new entity. It is not that big of a deal, but even that could be improved.

Not a lot of people know you can configure your entities dynamically using reflection. So the code above could be replaced with this:

It might look monstrous and not worth it. It might not only look like that... What I want to say, you should consider all pros and cons of such approach yourself.

It actually could be extremely powerful when you want to configure some aspect across multiple entities. For example, precision and scale for all decimal type:

This way you get rid of duplication and consolidate the logic in one place, which make it easier for changes. On the other hand, newcomers may be surprised why their decimal type have configuration they have not added 😁

Owned Entity Types

As expected, our Order has an Address where it should be shipped. For Order it is important to have an Id, while Addresses can be distinguished from each other by their attributes. Seems like an ideal candidate for ValueObject:

Logically, it makes no sense to store each city with each address and reference them by a foreign key.

What we would like to store it in the same Order table with other properties. For that, Owned Entity can be used:

Which will result in next DB structure:

You are free to name those columns the way you like. You can also put them in separate table. The nicest thing, it is not necessary to use the Include method:

Backing Fields

With DDD, it is prohibited to expose data through your properties, and it is encouraged to use private fields instead. It is a classic OOP model which can contain data in the form of fields, and behavior, in the form of methods.

In our case, we have two private fields: _orderDate and _orderItems. It is useful to restrict the access to the data by application code, so developers don’t break business invariants and avoid inconsistent state of our modal.

However, the database still should be able to read from and write to the model without any restrictions at all.

EF is smart enough to discover a field that has a property with similar name (like OrderItems). In other cases, you can define your own configuration:

Excluded Properties

By convention, all public properties with a getter and a setter will be included in the data model.

Which sometimes may not be desired, like with DebugMessage. To have property in your Domain model and not in Data model use Ignore:

Shadow Properties

Sometimes you may want it the other way around. To have a column in database and not in your Domain model.

You have already seen such behavior with foreign key:

We can not access the aggregate from the child entity, so there is no foreign key and navigation property. However, it still will be created in a database.

Let’s do something more interesting. We can define our own column:

Of course, it is possible to update it from code:

And obviously to retrieve it:

If you do not like working with ChangeTracker and static EF.Property scares you, then configure IndexerProperty to access it with the indexer:

Value Conversions

Sometimes your domain model can contain complex types as objects, List or enum (not a regular enum, don’t be silly. The one that was refactored to class hierarchies 😁)

By default, EF supports only primitive or entity types, so you have to figure out how to convert that value to something that can be stored. Gladly, we do not need to do it by ourselves. Conversion can be made on EF level.

There are tons of in-build converters for primitive types BoolToStringConverter, NumberToStringConverter, StringToDateTimeConverter and so on. But when those are not enough, you can write your own conversion expression or define your own converter by inheriting ValueConverter<TModel,TProvider>.

Also notice, that we had to specify ValueComparer. This is done so EF can track and detect changes correctly. For primitive types, you don’t need to do that.

Conclusion

I hope you find this article somehow useful 🙃. As you see, the combination of Domain-Driven Design and Entity Framework offers a powerful framework without a need to create a separate Data Modal Layer. You can focus on designing your domain, without worrying about underlying storage.

Let me know in the comment section what other EF features you find useful?💬
Give this article a clap or a few if you like it 👏
You can support me with a link below ☕️
And don’t forget to follow to receive more tricks on your beloved frameworks ✅

--

--

iamprovidence

👨🏼‍💻 Full Stack Dev writing about software architecture, patterns and other programming stuff https://www.buymeacoffee.com/iamprovidence