Wednesday 2 May 2012

Changing the Type of an Entity - Domain

Occasionally, there are times where it makes sense to transform an entity from one type to another. Take the example of a Manager entity which inherits from an Employee entity. At some point an Employee may be promoted to a manager. Here is the code for the Employee entity, including a factory class for creation and a class to amend the entity:
  
using System;
using HRSystem.Domain.Common;

namespace HRSystem.Domain.Entities
{
    public class Employee : Entity
    {
        private string _name;
        private string _payrollNumber;
        private DateTime _joinedOn;

        public virtual string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public virtual string PayrollNumber
        {
            get { return _payrollNumber; }
            set { _payrollNumber = value; }
        }

        public virtual DateTime JoinedOn
        {
            get { return _joinedOn; }
            set { _joinedOn = value; }
        }

        public static Employee Create(string role, string name, string payrollNumber, string department)
        {
            Employee employee = null;

            if (role == "Manager")
            {
                employee = new Manager();
                Manager manager = employee as Manager;
                manager.Department = department;
                manager.BecameManagerOn = DateTime.Now;
            }
            else
            {
                employee = new Employee();
            }

            employee.Name = name;
            employee.PayrollNumber = payrollNumber;
            employee.JoinedOn = DateTime.Now;
            return employee;
        }

        public void Amend(string name, string payrollNumber, DateTime joinedOn)
        {
            _name = name;
            _payrollNumber = payrollNumber;
            _joinedOn = joinedOn;
        }
    }
}
And here is the code for the Manager entity:
  
using System;

namespace HRSystem.Domain.Entities
{
    public class Manager : Employee
    {
        private string _department;
        private DateTime _becameManagerOn;

        public virtual string Department
        {
            get { return _department; }
            set { _department = value; }
        }

        public DateTime BecameManagerOn
        {
            get { return _becameManagerOn; }
            set { _becameManagerOn = value; }
        }
    }
}
Please note that these both ultimately inherit from the Entity class, which should be familliar to any regular users of NHibernate:
  
using System;
using HRSystem.Domain.Entities;

namespace HRSystem.Domain.Common
{
    public class Entity
    {
        protected long? _id;
        protected int? _hashCode;

        public virtual long? Id
        {
            get { return _id; }
            set { _id = value; }
        }

        public override bool Equals(object obj)
        {
            Entity other = obj as Entity;
            if (other == null) return false;
            if (!other.Id.HasValue && !_id.HasValue) return other == this;
            if (!other.Id.HasValue || !_id.HasValue) return false;
            return other.Id.Value == _id.Value;
        }

        public override int GetHashCode()
        {
            if (_hashCode.HasValue) return _hashCode.Value;

            if (!_id.HasValue)
            {
                _hashCode = base.GetHashCode();
                return _hashCode.Value;
            }

            return _id.GetHashCode();
        }
    }
}
So how do we model the promotion of an Employee to a Manager? Perhaps a method on the Employee called Promote, like the following code. Note that in order to change the Employee to a Manager, the extra parameters that a Manager has need to be supplied:

  
        public void Promote(string department)
        {
            //Make the class I'm in a Manager, not an Employee...
        }
The problem with this is that method would somehow have to change the type of the class it belongs to. This is not possible, not in C#, not in Java, not in any OO language.

In Domain Driven Design, Eric Evans makes the case for factory methods. The arguments is that on a car, the methods represent things like the accelerator or brake, which change the state of the car. However, the car does not carry around the ability to build itself - that is done in a factory. But what if we really wanted to change the state of the car? I'm talking tuned engine, new alloys, new paint job, new ICE etc. Unfortunately my car doesn't have a button for that - I have to take it to the garage. When it comes back, it's hardly the same car. The car entity could have a myRide.Pimp() method, and what comes back from that is a whole new car.

For the Employee, the method would look something like this:
  
        public virtual Manager Promote(string department)
        {
            Manager manager = new Manager();
            manager.Id = _id;
            manager.Name = _name;
            manager.PayrollNumber = _payrollNumber;
            manager.JoinedOn = _joinedOn;
            manager.Department = department;
            manager.BecameManagerOn = DateTime.Now;
            return manager;
        }
The code to promote an employee to the manager of ICT would look like this:
  
            Manager manager = employee.Promote("ICT");
I'm not completely sure that Promote(string department) is the quite the right name for this, it should possibly be something like 'GetPromotedVersion(string department)', but I feel it is acceptable in this instance.

The next stage is how to load the Employee and then persist it as a Manager, which I will cover in the next post.

No comments:

Post a Comment