Saturday, June 14, 2014

ORM Choice

ORM choice is a touchy subject, you can't just choose one without riling up some developers or DBAs in your team, it's a choice that must be decided carefully. When you've chosen the right ORM for the project, it is a shared joy for the team; picking the wrong one gives everyone a shared pain


As ORM has an influence how we model the domain models (facilitates shared vocabulary between the developers and the users), we should have a strategy when choosing one (or even two) for your project. We should also have a strategy when modeling our domain models. When we choose an ORM, we must embrace its principles, advantages, limitations, warts and all


And also, there are some who hates ORM, I could articulate some of the reasons why some hates ORM, but it's better to leave that to the experts. Here's the takeaway why some hates ORM, and Martin Fowler can't be wrong on positing that:

much of the frustration with ORMs is about inflated expectations


Back to the subject on choosing an ORM


You might think that this ORM drivel is about my repository framework (a repository framework to prevent the high coupling of an application to an specific ORM) I'm trying to endorse. I would not endorse it, it's a half-baked solution for what it's trying to achieve


I made a repository framework that encapsulates both Entity Framework and NHibernate in my earnest attempt to keep a project from being too coupled to an specific ORM framework. I named it ToTheEfNhX, short for: To The Entity Framework and NHibernate Elimination


However there are some principles and approaches that varies between those two ORMs that made me cease the development of that repository component project


Don't abstract out your ORM, don't abstract things out just for the reason it will be easy for you to switch to another ORM if you don't like the performance of your current ORM. Don't add another layer (e.g., repository, or if you really need to, just add very minimal or thin layer) on top of your ORM, just let the power of your ORM flows to your application



I'm not saying that it's a futile effort to abstract out ORM differences, but there are many differences in approaches between ORMs that will make one cease the effort for doing so


when in Rome, do as the Romans do


Case in point, in order to make the domain models work on both NHibernate and Entity Framework, i.e., instead of adding foreign key property to the model, just maintain an object reference, this domain model works on both NHibernate and Entity Framework:

public class Person 
{
     public virtual int PersonId { get; set; }
     public virtual string Firstname { get; set; }
     public virtual string Lastname { get; set; }

     public virtual Country DreamCountryToTravel { get; set; }
}


However, a keen Entity Framework enthusiast shall observe: "Isn't that model expensive to populate? The DreamCountryToTravel property is an independent association, it needed be assigned with an eagerly-loaded object."

person.DreamCountryToTravel = dbContext.Countries.Find(63); // .Find eagerly loads an object from the database


However, an NHibernater would beg to disagree, "that's the right way to model the business domain, it's very OOP, and you don't need to assign an eagerly-loaded object to DreamCountryToTravel. Session.Load is efficient, it lazily loads an object, it doesn't fetch anything from the database"

person.DreamCountryToTravel = session.Load<Country>(63);


An EF enthusiasts moving to NHibernate should not (and could not) force this kind of domain model to NHibernate:
public class Person 
 {
      public virtual int PersonId { get; set; }
      public virtual string Firstname { get; set; }
      public virtual string Lastname { get; set; }
 
      
      public virtual int DreamCountryToTravelId { get; set; }
      public virtual Country DreamCountryToTravel { get; set; }
 }


EF enthusiasts only can use foreign key property in NHibernate though, but it's not the OOP way to map relational to object:
public class Person 
 {
      public virtual int PersonId { get; set; }
      public virtual string Firstname { get; set; }
      public virtual string Lastname { get; set; }
 
      public virtual int DreamCountryToTravelId { get; set; }
 }


Likewise, an NHibernater moving to Entity Framework, should not force this kind of domain model to Entity Framework, as it will only incur performance problems in Entity Framework:
public class Person 
 {
      public virtual int PersonId { get; set; }
      public virtual string Firstname { get; set; }
      public virtual string Lastname { get; set; }
 
      public virtual Country DreamCountryToTravel { get; set; }
 }


The repository pattern I've made (that works on both NHibernate and Entity Framework) have the above type of a recommended domain modeling. I readied a LoadStub method for EF (4.1 then) in my utmost hope that someday Entity Framework will offer the same functionality as NHibernate's .Load method, that EF will facilitate OOP and drop the relational/persistence concern that foreign key property is. Here's the LoadStub method:

person.DreamCountryToTravel = countryRepo.LoadStub(63);


But alas, two versions (EF 4.1 was the latest version when I made ToTheEfnhX) and later, the independent association still can only receive eagerly loaded object and it is still expensive to eagerly load an object, there's still no way to just obtain an object stub/proxy with EF. There's a way though to load stub objects only, but it's for many-to-many relationship only



And the final straw that led me to cease the development of that ORM-agnostic ToTheEfnhX repository, there's a big difference in approach between the two ORMs when fetching multiple collections. It's hard to abstract them out


If you still want to create a repository pattern/component/wrappers around your ORM, just don't pull a strawman argument to justify creating one. Here's Ayende calling out one person's strawman argument against Ayende's advocacy to not use repository:


http://ayende.com/blog/153701/ask-ayende-life-without-repositories-are-they-worth-living


Don't bindly create a pattern just for the sake of creating a pattern, you might just be reinventing the wheel if you don't realized you already have a wheel. There's no shame on not creating repository pattern when your ORM already has a repository pattern bolted-in


Avoid overengineering: http://ayende.com/blog/4784/architecting-in-the-pit-of-doom-the-evils-of-the-repository-abstraction-layer



I wrote this to ease out the pain of one of the teams in our company for having to transition from one ORM to another. I hope this blog reaches the whole team and make them embrace their new ORM

No comments:

Post a Comment