Domain modeling with Entity Framework scorecard

A long, long time ago I had a series on strengthening your domain, highlighting how simple refactoring tools and code smells can help guide your models to push behavior down into your core domain model/business objects/whatever. All the techniques highlighted are things I did on every project, persisting using NHibernate. But what about Entity Framework? How can it handle a fully encapsulated domain model?

In some cases well, in others not. Let’s look at some of the techniques used and see how EF can handle them, and how it differs from NHibernate.

Constructors requiring invariants

Verdict: Pass

EF can handle constructors just like NHibernate handles constructors. If you define a constructor that includes the “invariants” of an entity, you are still required to create a no-arg constructor so that the ORM tool can still instantiate your object (and support lazy loading):

public class Offer : Entity
{
	public Offer(Member memberAssigned, OfferType type, DateTime dateExpiring, int value)
	{
		MemberAssigned = memberAssigned;
		Type = type;
		DateExpiring = dateExpiring;
		Value = value;
	}

    protected Offer() { }

Remember – persistence ignorance is a valid approach up until you run into fundamental limitations of the underlying platform. Both NH and EF require “virtual” for lazy loading, but that’s just a side effect of Java being virtual-by-default, and C#, well, not.

Private setters

Verdict: Pass

EF can handle private setters without any issues:

public Offer(Member memberAssigned, OfferType type, DateTime dateExpiring, int value)
{
	MemberAssigned = memberAssigned;
	Type = type;
	DateExpiring = dateExpiring;
	Value = value;
}

public virtual Member MemberAssigned { get; private set; }
public virtual OfferType Type { get; private set; }
public virtual DateTime DateExpiring { get; private set; }
public virtual int Value { get; private set; 

If you want lazy loading, you might have to make some navigation properties protected. One issue I ran into with EF is that if lazy loading isn’t possible because something isn’t virtual or visibility is incorrect, it will silently fail.

Private fields, no setters

Verdict: Fail

If you fully encapsulate the member, marking the field readonly, Entity Framework cannot map your field. EF still maps to properties, so this doesn’t work:

public class Offer {
    public Offer(Member member) {
        _member = member;
    }
    
    private readonly Member  _member;
    public Member Member {
        get { return _member; }
    }
}

This isn’t truly a show-stopper, however, as you can always convert back to a property with a private setter (and live with having a field that can be modified after construction).

Encapsulated collections

Verdict: Fail

Since EF can’t map to fields, you can’t build a fully encapsulated collection:

private readonly ICollection<Offer> _assignedOffers;
public virtual IEnumerable<Offer> AssignedOffers
{
    get { return _assignedOffers; }
}

public virtual Offer AssignOffer(OfferType offerType, IOfferValueCalculator valueCalculator)
{
    DateTime dateExpiring = offerType.CalculateExpirationDate();
    int value = valueCalculator.CalculateValue(this, offerType);

    var offer = new Offer(this, offerType, dateExpiring, value);

    _assignedOffers.Add(offer);

I’m trying to create a collection where I don’t publicly expose Add, Remove etc methods. These only exist on the private field. Julie Lerman details some workarounds, but those are a lot of work for not much gain.

Another option is to create a custom ICollection implementation where your mutating methods are marked with an Obsolete attribute, also ugly.

Unless you want to support these wonky solutions going forward, it’s a lot easier just to expose your collections as…collections and provide explicit methods for operations that mutate the collection. Nothing else really possible here. Is this a showstopper? Not really, but it is annoying:

public virtual ICollection<Offer> AssignedOffers { get; private set; }

public virtual Offer AssignOffer(OfferType offerType, IOfferValueCalculator valueCalculator)
{
    DateTime dateExpiring = offerType.CalculateExpirationDate();
    int value = valueCalculator.CalculateValue(this, offerType);

    var offer = new Offer(this, offerType, dateExpiring, value);

    AssignedOffers.Add(offer);

    NumberOfActiveOffers++;

    return offer;
}

Anyone can modify that attached collection without following the “rules”.

Encapsulated primitives (aka the NodaTime/Enumeration class problem)

Verdict: Fail, Pass with workaround

Let’s say that instead of a string to represent an Email, you wanted to create an Email class that wraps a primitive with more behavior. This is possible in NHibernate because we can add custom persistable primitives, but not so much in EF. Instead, I need to create a buddy property to represent the mapped primitive:

[NotMapped]
public virtual ExpirationType ExpirationType
{
	get
	{
	    return Enumeration.FromValue<ExpirationType>(ExpirationTypeValue);
	}
	set { ExpirationTypeValue = value.Value; }
}

public virtual int ExpirationTypeValue { get; private set; }

For querying/persisting purposes, I use that “Value” property. If I need the version with the behavior, I use the property without the “Value” suffix.

Until custom primitive type persisters are supported in EF, the workarounds will have to do. In the meantime, I’m starting to use regular enum’s instead of enumeration classes until it’s proven that the enum needs associated behavior, like you can do with this persistable strategy pattern:

public abstract class ExpirationType : Enumeration
{
    public static readonly ExpirationType Assignment = new AssignmentExpiration(1, "Assignment");
    public static readonly ExpirationType Fixed = new FixedExpiration(2, "Fixed");

    protected ExpirationType(int value, string displayName)
        : base(value, displayName)
    {
    }

    public abstract DateTime CalculateExpirationDate(OfferType offerType);

    private class AssignmentExpiration : ExpirationType
    {
        public AssignmentExpiration(int value, string displayName)
            : base(value, displayName)
        {
        }

        public override DateTime CalculateExpirationDate(OfferType offerType)
        {
            return DateTime.Now.AddDays(offerType.DaysValid);
        }
    }


Value objects

Verdict: Don’t use them anyway

Value objects are possible using Complex Types in EF and Composite objects in NH, but don’t use them. There’s all sorts of weird behavior around null values, and ultimately you’re going to run into friction that your domain model is at fundamental odds with your persistence model. Other databases can handle this sort of thing better (document, other NoSQL databases), so you’re better off not attempting it and running into limitations down the road.

If you need a true Value Object, and it’s not just an encapsulated primitive, you’re much, much better off treating it as an entity in EF and managing the visibility/mutability/navigation yourselves.

Final Verdict

The only real annoyance here is the collection issue, all others aren’t going to prevent us from building reasonably encapsulated domain models. But NH didn’t let us do that either, it still required us to add code in some scenarios, breaking that “POCO” illusion.

Since we’re not building an object database, some compromise is needed, and so far, I haven’t seen anything that forces an anemic domain model upon us.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in Domain-Driven Design, Entity Framework, NHibernate. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://tech.trailmax.info/ trailmax

    I feel your pain… Recently I’ve seen quite an interesting approach to ORMs (NH or EF) – use Momento pattern. Something like this: http://stackoverflow.com/a/8560328/809357 You persist only state object, but your actual domain model is based on the state and not persisted. Have not tried that myself yet though, but looks tasty! A lot of work though…

  • Steven Pena

    You can get some isolation around those collections by marking them internal. As long as the dbcontext has visibility to it (if in a separate assembly), you are good to go. This works well for me as I tend to put the domain models in a separate assembly from the business services that orchestrate them.

  • Owen Craig

    The other option for the ICollections is have them as a private property, with a static public static Expression<Func<ICollection> ICollectionAccessor { get; set; }

    Then in your DbContext use that static accessor in your mapping.

    It’s still a kludgy workaround, but it does stop people from directly accessing your ICollections. They are still able access them, but they are required to jump through a lot of hoops to do it which should ring warning bells in anyone’s head.

  • http://lavinski.me Lavinski

    How about using Domain Events + CQRS and avoiding the ORM on the domain model side?

    • http://blog.goranobradovic.com/ Goran Obradović

      I agree. ORM always produces some kind of cludge that is beyond compromise, either on design or performance side.
      But great article nevertheless, thanks for sharing!

  • Pingback: Articles for 2014-apr-30 | Readings for a day

  • http://blog.scooletz.com/ Scooletz

    Jimmy, I don’t get. You point out plenty of cant_do_it in EF and summing it up that all these fails do not matter or you can live with it. I’d finish it with totally different conclusion. I know EF is supported by MS but it’s still years behind NH and I truely hope that one day they will catch up.

    • James Nail

      I agree, I’m a bit disappointed with this post. The gist is, “sure, EF supports strong domain models — but only if you completely redefine the term ‘strong domain models’ to mean weak, unencapsulated domain models.”

      • jbogard

        The only one I thought was truly a showstopper was the encapsulated collections. But with the quite-a-bit-weaker LINQ support, it’s basically a wash.

        That’s not to say there aren’t a ton of other things missing – that’s for the next post.

  • Unai Zorrilla Castro

    Good post!
    Jimmy, for encapsulate a collection you can use a private property, mapped with a simple custom convention.

  • Bogdan Marian

    I think that one way of achieving “persistence-ignorance” is to model persistent entities as interfaces and let the ORM generate the appropriate implementation (i.e. dynamic class) at runtime. This implementation can be as tightly-coupled to the ORM as possible, have “virtual” added to all kind of properties, whatever…
    Currently, I do not know whether EF, NH or any other ORM support such mechanism.

  • Jamie Ide

    I’m surprised you didn’t mention my biggest gripe with EF which is that there’s no Load method so you generally have to include the foreign key and the referenced entity to avoid a database round trip.

    Value objects are relatively easy to implement in NH using a custom IUserType implementation.

    • jbogard

      Oh, yeah, there’s that. I’m on the fence on that one – about 1 out of every 10 NH users can tell you the difference between Get and Load, and WHY you should use one or the other.

      • Jamie Ide

        The bigger problem for me is that it’s nearly impossible to keep the foreign key and the referenced entity consistent. That’s a big problem for stateful applications.

    • Chris B

      I don’t know how good of an idea this really is, but my recent line of thinking around that is that you should only walk the object graph within an aggregate. Out-of-aggregate references are id only. This means that when you are making a connection between an entity and a non-owner aggregate, all you need is the id. If you also have all interactions with an entity go through its aggregate, I don’t see much need for the Load method. I’m hoping I’ll get a chance to play around with that idea soon to more fully evaluate it.

      From your “stateful applications” comment, it sounds like you may be doing winforms development, which may put you in a different situation from a web developer such as myself. It also doesn’t help if you are in brown-field situations.

  • MikeSw

    So, long story short the lesson here is “ORM and proper encapsulation don’t work together” ? I think it’s the same for NoSQL also, restoring to private fields mean special settings and probably you don’t want the whole object serialized with privates. The only solution I see for such scenarios is to use a memento (or Event Sourcing but I know your opinion about it), but this could mean a lot of boring code (copy/paste) . It’s pretty much a decision of what evil you want to live with

  • Mike Cole

    “One issue I ran into with EF is that if lazy loading isn’t possible because something isn’t virtual or visibility is incorrect, it will silently fail.” => Awesome, thanks for the tip. I couldn’t figure out why it wasn’t working in some cases for me.

  • Pingback: Some DDD+EF Resources | The Data Farm