In the previous email, I elaborated on the article about domain model purity and lazy loading.

Just to re-iterate, here’s the sample code:

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));
    }
}

User and LoginSession are part of the domain model; User here relates to LoginSession as 1-to-many.

The User class here is pure even though an ORM (such an NHibernate or EF Core) creates a proxy class on top of it and overrides the LoginSessions property such that the collection of login sessions is loaded lazily from the database.

Here’s a picture that provides a mental model about lazy loading and domain model purity:

[Enable Images]

In short: it’s the UserProxy class that reaches out to the database, not User. Therefore, User itself remain pure.

This is the typical hexagonal architecture at play: the app services layer (the proxy class) depends on the domain layer (the User class), but the opposite isn’t true.

I received an interesting follow-up question that I thought I would share with you:

As we know, every Aggregate Root manages the consistency of all the Aggregate’s contents, including Entity objects like User in your example. Let’s assume the Aggregate Root now wants to create a new user, isn’t there a problem because an instance of the UserProxy must be created instead of an User (so that it can reach the database)?

It’s a great question because it shows the beauty of lazy loading and its implementation in ORMs:

You don’t need to create proxy classes on top of newly created entities.

The only reason why ORMs create proxy classes is to load additional data from the database during the business transaction (if needed). Since the domain object is new, there’s nothing to load from the database — the object hasn’t been persisted yet.

Therefore, all you need to do is instantiate the User as usual, without registering it with the ORM. It’s only when you load that object from the database, do you need the proxy. But since you are doing the loading via the ORM, that scenario is already covered — the ORM will create the proxy for you!

This is how the two scenarios would play out:

[Enable Images]
[Enable Images]

Notice how in the first scenario, the controller doesn’t need a proxy in order to save the user to the database.

Also notice how in the second scenario, the controller calls ReadDetails() on the user proxy, and this call gets split internally: part of it goes to the database to retrieve all the login sessions, and part is delegated to the underlying User domain class, since some of the user’s data (e.g email and name) was loaded eagerly, along with the user itself.

Once again, the User domain class doesn’t know anything about its proxy class and doesn’t depend on it in any shape and form. Should you disable lazy loading, the domain class would work just as before.


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

Workshops — I offer a 2-day workshop for organizations on Domain-Driven Design and Unit Testing. Reply to this email 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 »