I’d like to elaborate on an interesting comment to the article about domain model purity and lazy loading.

In that article, I made a case that proxy classes created by ORMs in order to enable lazy loading don’t violate domain model purity:

A base class is not responsible for what its subclasses are doing. As long as this base class doesn’t explicitly rely on its subclasses to communicate with the database (that is, doesn’t introduce abstract methods with Task's in their signature), it remains pure.

In the comments, someone pointed me to an older article I wrote about domain model isolation (which is a synonym for domain model purity) and asked:

Could you clarify the term "purity"? What is more important, the absence of explicit out-of-process dependencies or the absence or a real call to the database in domain logic?

This is a very good question and I totally understand where it comes from because I pondered on it myself for quite a while. Let’s look at the sample code from the article once again:

public class User : Entity
{
    private List<LoginSession> _loginSessions;
    public virtual IReadOnlyList<LoginSession> LoginSessions
        => _loginSessions.ToList();

    public virtual void RegisterSession(DateTime now)
    {
        LoginSession session = _loginSessions.Last();
        if (session.HappenedRecently(now))
            session.Update(now);
        else
            _loginSessions.Add(new LoginSession(this, now));
    }
}

This class is part of the domain model. User here relates to LoginSession as 1-to-many. An ORM (such an NHibernate or EF Core) creates a proxy class that inherits from User and overrides the LoginSessions property such that the collection of login sessions is loaded lazily — when and only when the client code refers to this collection.

In practice, it means that when the client code calls the RegisterSession method the first time, the login session collection is still empty. It is loaded when that method calls _loginSessions.Last().

There might not be an explicit reference to the database, but still, how the User domain class can be pure given that there is a call to that database in RegisterSession()?

After some deliberation, I decided that a picture is worth a thousand words. So, here is the mental model that will help you think about lazy loading and domain model purity:

[Enable Images]

This image shows the relation between the domain class and the proxy class created by the ORM. Notice the important point here:

It’s the UserProxy class that reaches out to the database, not User.

This proxy class is created by the ORM at runtime to override the behavior of the base User and enable lazy loading and itself is part of the application services layer, not the domain layer.

The User domain class doesn’t know anything about that proxy class and doesn’t depend on it in any shape and form — should you disable lazy loading, the domain class will work just as before.

Therefore, the hexagonal architecture principles remain intact: the app services layer (the proxy class) depends on the domain layer (the User class), but the opposite isn’t true. Hence, the User remains pure and fully isolated from the database.


Enjoy this message? Here are more things you might like:

Consulting and training — I offer consulting and group training for organizations on Unit Testing and Domain-Driven Design. Reply to this email with the word "consult" to discuss.

Unit Testing Principles, Patterns and Practices — A book for people who already have some experience with unit testing and want to bring their skills to the next level.
Learn more »

My Pluralsight courses — The topics include Unit Testing, Domain-Driven Design, and more.
Learn more »