ORM – How to Map Shared and Specific Properties in ORM Inheritance

object-orientedormpolymorphism

When I was starting in OO programming, mostly Symfony and Doctrine ORM, I have been advised at least one time to apply inheritance mapping for a case as follows:

For instance, a HR system which deals with job candidates, employees, former employees, part time employees etc..

There would be an entity class User, and since there are other kinds of users, like above mentioned, there would be a few more child classes e.g. Employee, etc..

The difference between users is that each type has a set of specific properties. And they all share a set of properties.

So, the shared properties (e.g. ID, Name, Surname) would be in User parent class and the rest, specific ones would be in particular child class.

Looking at Doctrine docs, a neat way to map this, I guess, would be the Single Table Inheritance

I am not giving example of any behaviours for the classes, at this point, and that is on purpose. The case perhaps implies inevitable different implementations of a certain behaviour, which would justify the polymorphism.

I'd like to be sure about how to deal with specific properties first.

Would you agree that Inheritance mapping is justified in this case?

Does the presence of shared and specific properties for a group of real world objects inevitably implies polymorphism?

Best Answer

I would recommend avoiding that inheritance here especially when you are talking about letting that inheritance bleed down into your database - unless its an Object-Oriented database.

But a typical RDBMS is not object-oriented, and anyone who has to write queries against a table structure that's designed in favour of supporting inheritance for your ORM is not going to thank you for it :)

I'd recommend Composition over Inheritance in virtually every case like this, and here is why.

The key part here is this statement

And they all share a set of properties.

That tends to imply that these set of properties should be implemented using Inheritance from some kind of common Base Class i.e. an Employee is-a Person, a Candidate is-a Person and so on.

e.g.

public class Person
{
   public string Name {get;set;}
}

public class Employee:Person
{
    public string EmployeeId {get;set;
}

public class Candidate:Person
{
    public string CandidateId {get;set;
}

But you're better off tackling this from the perspective of the Domain itself.

Model the Domain Objects to be extremely specific

Although at face value it might seem that this is the best way, and that these properties really are common to all kinds of people, are you really sure?

Certainly all Employees have things like "Name" as do Candidates, but who needs to know that information really depends upon who is asking when we're talking about modeling a Domain with Object-Oriented Programming.

For example, the Payroll system will need to know my name in order to print it on my Payslip; so it makes sense for the SalariedEmployee to have a "Name" property. However the ClockInOut module really couldn't care less; its job is just to record the in and out times for an EmployeeId so to it I am just a TimeClockedEmployee with a unique Id.

If we've inherited from some common Person class, you have that information carried around the place whether you want or need it.

Now Name is not that big a deal but what about things like Religion, etc. That kind of data should only be accessible to modules that really need it for several reasons

  • Ability to keep a sharp and small focus on the Domain being modeled; having common properties - or worse, behaviour - that are never going to be used by a specific Domain model can makes development and maintenance harder.
  • Ability for the specific Domain area being modeled to change without having to affect any other model in the system;
  • Data Security, by avoiding exposing data to parts of the system that do not need it

Handling future changes - example

New and as yet undiscovered business processes may require more information that isn't a fit for a base class at all yet is required by more than one Employee-type object. e.g. Let's say new legislation says you need the Social Security number for both Hourly and Salaried employees, but you do not need it for ContractEmployees (who despite what some might say, are still People :) )

So now you have to break your nice inheritance to put duplicate properties on both HourlyEmployee and SalariedEmployee anyway or else deal with the possibility for some of your code to deal with objects that can have a null SSN. Now things are getting messy - does a null SSN represent a Contractor, or a mapping error? Who knows...

Better approach

In the long run, it's more flexible to model this using Composition whereby we say an HourlyEmployee has-a Person, a Candidate has-a Person and so on.

So an HourlyEmployee, SalariedEmployee,ContractEmployee, PotentialEmployee, VisitorNonEmployee,PizzaDeliveryPerson etc, can have (or not) a PersonalInformation structure if needed, that is indeed common to all those that have it, but which is not part of any hierarchy.

In terms of how this maps to the Database via the ORM, the individual backing tables will have columns for the PersonalInformation object, and most ORM systems will support Value Object Mapping to allow you to map that subset of properties into the PersonalInformation object as a Value object on the Entity in question. YMMV depending on the ORM in question.

e.g. This might store personal information about a person.

   public class PersonalInformation
   {
      public string Name {get;set;}
   }

If you really need to pass around Polymorphic objects, then Interfaces are just as useful here as base classes; in fact, because you can implement multiple interfaces, I would say they are more useful.

   public interface IEmployee
   {
      string EmployeeId {get;}
   }

This might indicate that an entity holds some personal information:

   public interface IPersonalInformationHolder
   {
      PersonalInformation PersonalInformation {get;}
   }

as seen in

   public class Employee : IEmployee,IPersonalInformationHolder
   {
        public string EmployeeId {get;}
        public PersonalInformation PersonalInformation {get;}
    }

Note how the ClockTimedEmployee doesn't have any personal information; because we'll never ask it for your name. That will be done by some other part of the system e.g. the Reporting Module. And it can still be passed around as an IEmployee to any part of the system that cares about EmployeeId

        public class ClockTimedEmployee: IEmployee
        {
            public string EmployeeId {get;set;}
        }

Another distinct advantage of this approach is that it guarantees that you can never accidentally pass a ClockTimedEmployee into any method that expects an IPersonalInformationHolder; the code simply will not compile. This helps to cut out a whole class of runtime bugs that are hard to avoid with inheritance.

Related Topic