0

I am trying to separate my domain model and associated business logic from my persistence model, particularly such that the domain model has no dependency on the ORM. One apparent way to do that was to make an ORM-coupled subclass of my POCO domain model object, with persistence layer specific properties and logic in that subclass. In this way, I can easily test my domain model, and write domain services that know nothing about my persistence layer. Using private setters, I can write behavior-based methods and enforce invariants.

The argument was made however that this is a flawed approach and that my DAOs should not be structurally tied to my domain model. I am trying to understand if this is so and why. For instance, I quickly found this article that makes a similar argument--but found that what I had already done to address problems I saw answered 85-90% of the objections, and is in fact very similar to the author's #2 solution (though that doesn't even separate the two into subclass/superclass).

This can be accomplished partly just by using POCOs with EF Core, but I wanted to enforce the decoupling somewhat by having my domain model and services in its own project/assembly with no dependency on EF. My application service layer however, which will know about the persistence layer and the domain model, can depend on both in its own project/assembly. Thus, I can have the superclass (domain model) in one project and the coupled subclass (EF entity) in another, as a boundary to control dependencies.

What's wrong with this approach?

Here's part of a domain model class, for what it's worth:

namespace App.Core
{
    public class Event
    {
        public String Title { get; private set; }
        public DateTimeOffset Start { get; private set; }
        public DateTimeOffset End { get; private set; }
        public Boolean IsShownOnCalendar { get; private set; }
        public Team InitiatingTeam { get; private set; }
        public Person InitiatingPerson { get; private set; }

        public IEnumerable<Tag> Tags { get; }
        public IEnumerable<TagAction> TagActions { get; }
        public IEnumerable<Artifact> Artifacts { get; }

        protected Event() { }

        // ... behavior methods, business logic...
    }
}

And part of the subclass which is mapped to a DB via EF:

namespace App.Repository.Entities
{
    public class Event : App.Core.Event
    {
        public int Id { get; private set; }
        public int InitiatingTeamId { get; private set; }
        public int InitiatingPersonId { get; private set; }
        public DateTimeOffset LastModified { get; private set; }
    }
}

This started as a Code Review question about a Builder pattern to support this structure, but the whole thing was arguably to make this somewhat-locked-down model arrangement work...so is there a larger reason that it shouldn't be made to work?

  • I like this question, though I guess some people here will think it is too broad. – Doc Brown Aug 23 at 20:27
  • 1
    Vaughn Vernon has a blogpost that sort of discusses this topic. The separated interface approach he describes is more or less (if you are careful with the design) the same thing as deriving from the domain object. – Filip Milovanović Aug 25 at 13:26
  • @FilipMilovanović Thanks! Yes it is similar. I'm annoyed though that his first objection to the interface design is that of naming collision--and I don't think I'm as smart as him and I figured that out: different namespaces with the same name (that's what they're for). His second approach solves the problem of clients inspecting state directly, but inverts the dependency direction--the domain object can see and use the persistence-layer-specific parts of the state object, and if any persistence dependencies have to be added the domain model is now dependent, which is what I wanted to avoid. – S'pht'Kr Aug 26 at 12:47
  • His objection is less about the naming collision and more about his desire to for model elements to reflect the ubiquitous language - I've seen some of his tweets and I get the impression that he doesn't like the I-prefix convention; however, IMO, the argument is somewhat superfluous, and that convention is so well established in the C# community that people may find it confusing if it is not followed. – Filip Milovanović Aug 26 at 18:01
  • You are absolutely right about the dependency structure in the second approach, though, which is why I didn't refer you to it (but it's an option to consider). There are potentially ways around it - say, you could introduce an interface in between - however, it's likely to be more trouble then it's worth (and it would be, in a birds eye view, essentially the same thing as to the first approach, but with an extra level of indirection and added complexity). – Filip Milovanović Aug 26 at 18:01
0

I found an implementation impact reason not to do this for Entity Framework. If you make your EF-coupled entities a subclass of your domain model, you can't have (or I didn't see a way to have) subclass relationships among entities work right for EF.

That is, if I have:

Core.Event <-- Entities.Event

But I want a subclass of Event called OnlineOnlyEvent that expresses a domain concern, the subclass has to inherit from Core.Event...

Core.Event <-- Entities.Event
    ^
    |
Core.OnlineOnlyEvent

So if EF is mapped to Entities.Event as the entity, then a new class Entities.OnlineOnlyEvent can't derive from both, it has to derive from Core.OnlineOnlyEvent, so now EF won't see a subclass relationship among its entities--they are orthoganal.

Core.Event <-- Entities.Event
    ^
    |
Core.OnlineOnlyEvent <-- Entities.OnlineOnlyEvent

"I am your father's brother's nephew's cousin's former roommate." "What's that make us?" "Absolutely nothing!"

So they'll become completely separate tables with no heirarchy relationship--so I would be unable to do TPH for this in EF Core (I suppose I could see it as a way to force TPC in EF Core...sort of).

Since the whole purpose of what I was doing was to isolate the entities for EF, this makes the solution non-viable, though for an implementation reason and not for a more structural/architectural reason.

Notably, the notion that "you shouldn't do this because you should not have such a strong relationship between your domain model and persistence model" does not seem to hold water. Not only is the widespread naive practice to use the domain model as the persistence model directly, but even the oft quoted Vladimir Khorikov states:

For those of you who might wonder why bother with this at all and not just separate the domain model into domain and persistence models and keep the domain model encapsulated this way: it doesn’t work out well. In complex applications, the amount of effort required to build a separate persistence model doesn’t justify the improvements in terms of purity. The effort is too large, the benefits are too small.

The only use case where it’s reasonable is with legacy databases. Trying to bridge the gap between such database’s structure and the domain model is almost impossible, so you are pretty much forced into building a separate persistence model. In all other cases, consider relying on the plain ORM and accepting its shortcomings if any.

The general sentiment of which I have come to very heartily agree with, after this long and mostly futile attempt to have my cake and eat it too.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.