Wednesday 23 May 2012

Paul T Davies SOACP

I have just completed Module 2 of Thomas Erl's SOA School course, which apparently makes me an SOA Certified Professional!

Tuesday 22 May 2012

Business Logic Based on Large Data Sets

A collegue recently came to me with help on an architecture desicion: A client wants to add a site to their list of sites, and the system must check that the site does not already exist before adding it, creating a validation error if it does. Now the validation of the new site is clearly something that belongs in the domain, so surely this should be quite straightforward, possibly something like this (this is written off the top of my head, and practically psuedocode):
  
public class Site : Entity
{
    public ValidationMessageCollection ValidateCreateSite(string siteName, string addressLine1, string addressLine2)
    {
        //...

        List<Site> matchingSites = (from matchingSite 
                                    in _sites
                                    where matchingSite.SiteName.Equals(siteName)
                                    && matchingSite.AddressLine1.Equals(addressLine1)
                                    && matchingSite.AddressLine2.Equals(addressLine2)
                                    select matchingSite)
                                    .ToList();

        if (matchingSites.Count() > 0)
            validationMessageCollection.AddError("Similar site already exists.");

        //...
    }
}
OK, ignore the simplistic message, the actual logic behind wheter or not a site is the same as another and whether or not this even compiles, the problem I'm illustrating is based around the fact that a client may have thousands of sites, and to load them all would be wasteful on resourses and slow. Somehow we need the database to do the query, but the domain to do the logic.

Here is my solution - perform the query and then simply pass the result into the domain, and let it make a descision what to do based on the results:
  
public class SiteService
{
    public long AddSite(
        string siteName, 
        string addressLine1, 
        string addressLine2) //Or whatever
    {
        ISiteRepository siteRepository = new SiteRepository();

        bool similarSitesExist = 
            siteRepository.GetWhetherSimilarSitesExist(siteName, addressLine1, addressLine2)

        ValidationMessageCollection validationMessageCollection = 
            Site.ValidateCreateSite(siteName, addressLine1, addressLine2, similarSitesExist)

        if(validationMessageCollection.Count > 0) 
            throw new ValidationFaultException(validationMessageCollection);

        Site site = Site.Create(siteName, addressLine1, addressLine2);
        siteRepository.SaveInTransaction(site);
        return site.Id.Value;
    }
}

public class Site : Entity
{
    public ValidationMessageCollection ValidateCreate(
        string siteName, 
        string addressLine1, 
        string addressLine2, 
        bool similarSitesExist)
    {
        //...

        if (similarSitesExist)
            validationMessageCollection.AddError("Similar site already exists.");

        //...
    }
}
Or, if you want to return more information about the sites that exists, this could be an option:
  
public class SiteService
{
    public long AddSite(string siteName, string addressLine1, string addressLine2) //Or whatever
    {
        ISiteRepository siteRepository = new SiteRepository();

        List<Site> similarSites = 
            siteRepository.GetSimilarSites(siteName, addressLine1, addressLine2)

        ValidationMessageCollection validationMessageCollection = 
            Site.ValidateCreateSite(siteName, addressLine1, addressLine2, similarSiteAddresses)

        if(validationMessageCollection.Count > 0) 
            throw new ValidationFaultException(validationMessageCollection);

        Site site = Site.Create(siteName, addressLine1, addressLine2);
        siteRepository.SaveInTransaction(site);
        return siteAddress.Id.Value;
    }
}

public class Site : Entity
{
    public ValidationMessageCollection ValidateCreateSite(
        string siteName, 
        string addressLine1, 
        string addressLine2, 
        List<SiteAddress>> similarSites)
    {
        //...
              
        if(similarSites.Count() > 0)
        {
            string similarSiteAddressString = "Similar site addresses exist: ";
                     
            foreach(SiteAddress similarSiteAddress in similarSiteAddresses)
            {
                string = string + similarSiteAddress.Name + ", " + similarSiteAddress.AddressLine1;
            }
                     
            validationMessageCollection.AddError(similarSiteAddressString);
       }
              
       //...
    }
}

Monday 14 May 2012

Domain Based Security Part 2

Following on from my previous post, here is a more complex example of domain based security. Here are the business rules:
  • A user can edit their own tasks.
  • A basic user can edit another basic user's task, but not an admin user's task.
  • An admin user can edit an admin user's task or a basic user's task.
Again, the user entity will have a CanEditTask that takes a task as a parameter and returns a boolean.
There could be a list of permissions such as 'Edit tasks for basic user' or 'Edit tasks for admin user', but as the list of roles increases, this quickly becomes unmaintainable. It is more flexible to have a permission such as 'Edit tasks for users in role' which can be applied to a variety of roles. The way to do this is to break up the many-to-many relationship between the Role and Permission entities by inserting a new 'RolePermission' entity in the relationship. This entity links roles to the permission that they are entitled to and had references to the relevant Role and Permission entities, but has the benefit in that it can be inherited and introduce extra elements to the relationships.

For example a RolePermission object may hold a 'Basic user' role and a 'Edit own tasks' permission. This means that a basic user can edit their own tasks, but a RolePermissionForRole could inherit RolePermission, and this could have a 'Admin user' role, an 'Edit tasks for users in role' permission, and a RoleAppliesTo field of 'Basic user'. This means an admin user can edit tasks for a basic user. Here is how the UML class diagrams of the old and new systems compare.

First the previous system:


And the new one:


The user has a reference to it's own role, and consequently it's own permissions, and thus it knows which roles it is allowed to edit tasks of. The Task object has a reference to the User it is assigned to, and that user has has a reference it's role. So the CanEditTask method has access to all this information, and thus can work out whether the edit can take place. Here are the entites:
  
public class User : Entity
{
    protected string _username;
    protected Role _role;

    public virtual string Username
    {
        get { return _username; }
        set { _username = value; }
    }
               
    public virtual Role Role
    {
        get { return _role; }
        set { _role = value; }
    }

    public virtual bool CanEditTask(Task task)
    {
        List rolesWhosTasksCanBeEditied = _role
            .RolePermissions
            .Where(rolePermission => rolePermission.Permission.Key == PermissionKey.EditTaskForRole)
            .Cast()
            .Select(rolePermissionForRole => rolePermissionForRole.RoleAppliesTo)
            .ToList();

        if(rolesWhosTasksCanBeEditied.Contains(task.AssignedTo.Role))
            return true;
        else
            return false;
    }
}

public class Role : Entity
{
    protected string _rolename;
    private IList<RolePermission> _rolePermissions;

    public virtual string RoleName
    {
        get { return _rolename; }
        set { _rolename = value; }
    }

    public virtual IList<RolePermission> RolePermissions
    {
        get { return _rolePermissions; }
        set { _rolePermissions = value; }
    }
}

public class RolePermission : Entity
{
    protected Permission _permission;

    public virtual Permission Permission
    {
        get { return _permission; }
        set { _permission = value; }
    }
}

public class RolePermissionForRole : RolePermission
{
    private Role _roleAppliesTo;

    public virtual Role RoleAppliesTo
    {
        get { return _roleAppliesTo; }
        set { _roleAppliesTo = value; }
    }
}

public class Permission : Entity
{
    protected string _key;
    protected string _description;

    public virtual string Key
    {
        get { return _key; }
        set { _key = value; }
    }

    public virtual string Description
    {
        get { return _description; }
        set { _description = value; }
    }
}
public class Task : Entity
{
    private string _description;
    private User _assignedTo;

    public virtual string Description
    {
        get { return _description; }
        set { _description = value; }
    }

    public virtual User AssignedTo
    {
        get { return _assignedTo; }
        set { _assignedTo = value; }
    }
}
So now for the test to ensure this works as expected. A permission is created for editing a task for users in a specified role, and roles for basic user and admin user are created. Two RolePermissionForRole entities are created which give the permission to edit tasks, one for basic users' tasks, and one for admin users' tasks. Both of these RolePermissionForRole entities are added to the admin role, but only the RolePermissionForRole entity for editing a basic user's tasks is added to the basic user. Users and tasks are then created and it is asserted that a basic user can edit a basic user's task but not an admin user's task, and an admin user can edit both. Hope that makes sense! Here is the test to hopefully make it clearer:
  
[TestMethod]
public void CheckUserCanEditTasksBelongingToUsersInCertainRoles()
{
    Permission editTaskPermission = new Permission()
    {
        Key = PermissionKey.EditTaskForRole,
        Description = "Edit task for users in role."
    };

    Role basicRole = new Role()
    {
        RoleName = "Basic User",
        RolePermissions = new List<RolePermission>()
    };

    Role adminRole = new Role()
    {
        RoleName = "Admin User",
        RolePermissions = new List<RolePermission>()
    };

    RolePermissionForRole editTaskPermissionForBasicUser = new RolePermissionForRole()
    {
        Permission = editTaskPermission,
        RoleAppliesTo = basicRole
    };

    RolePermissionForRole editTaskPermissionForAdminUser = new RolePermissionForRole()
    {
        Permission = editTaskPermission,
        RoleAppliesTo = adminRole
    };

    basicRole.RolePermissions.Add(editTaskPermissionForBasicUser);
    adminRole.RolePermissions.Add(editTaskPermissionForBasicUser);
    adminRole.RolePermissions.Add(editTaskPermissionForAdminUser);

    User barryBasic = new User()
    {
        Username = "Barry Basic",
        Role = basicRole
    };

    User arnoldAdmin = new User()
    {
        Username = "Arnold Admin",
        Role = adminRole
    };

    Task arnoldsTask = new Task()
    {
        Description = "Arnold's Task",
        AssignedTo = arnoldAdmin
    };

    Task barrysTask = new Task()
    {
        Description = "Barry's Task",
        AssignedTo = barryBasic
    };

    Assert.IsFalse(barryBasic.CanEditTask(arnoldsTask));
    Assert.IsTrue(arnoldAdmin.CanEditTask(barrysTask));
}
And here is the NHibernate mapping files to map all this:
  
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="TaskLogger.Domain.Entities"
                   assembly="TaskLogger.Domain">
  <class name="User" table="`User`">
    <id name="Id" column="Id" type="long">
      <generator class="identity"/>
    </id>
    <property name="Username" type="string" />
    <many-to-one name="Role" class="Role" column="RoleId" cascade="save-update" />
  </class>
</hibernate-mapping>

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="TaskLogger.Domain.Entities"
                   assembly="TaskLogger.Domain">
  <class name="Role" table="`Role`">
    <id name="Id" column="Id" type="long">
      <generator class="identity"/>
    </id>
    <property name="RoleName" type="string" />
    <bag name="RolePermissions"  >
      <key column="RoleId"/>
      <one-to-many class="RolePermission"/>
    </bag>
  </class>
</hibernate-mapping>

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="TaskLogger.Domain.Entities"
                   assembly="TaskLogger.Domain">
  <class name="RolePermission" table="`RolePermission`">
    <id name="Id" column="Id" type="long">
      <generator class="identity"/>
    </id>
    <many-to-one name="Permission" class="Permission" column="PermissionId" cascade="save-update" />

    <joined-subclass name="RolePermissionForRole" table="`RolePermissionForRole`">
      <key column="`Id`"/>
      <many-to-one name="RoleAppliesTo" class="Role" column="RoleAppliesToId" cascade="save-update" />
    </joined-subclass>
    
  </class>
</hibernate-mapping>

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="TaskLogger.Domain.Entities"
                   assembly="TaskLogger.Domain">
  <class name="Permission" table="`Permission`">
    
    <id name="Id" column="Id" type="long">
      <generator class="identity"/>
    </id>
    
    <property name="Key" column="`Key`" type="string"/>
    <property name="Description" type="string" />
    
  </class>
</hibernate-mapping>

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="TaskLogger.Domain.Entities"
                   assembly="TaskLogger.Domain">
  <class name="Task" table="`Task`">
    <id name="Id" column="Id" type="long">
      <generator class="identity"/>
    </id>
    <many-to-one name="AssignedTo" class="User" column="AssignedToId" cascade="all" />
    <property name="Description" type="string" />
  </class>
</hibernate-mapping>

Sunday 13 May 2012

Domain Based Security Part 1

While I believe some elements of security should be in a centralised Security Service (such as general details about a user, what areas of the enterprise a user is allowed access to), I also believe some elements of security belong in the domain of a specific system, because the rules that surround the security are tied up with the business rules of the application. I'm going to use a task logging system as an example, and focus on a user's ability to edit a task.
So here are the business rules:
  • A basic user can edit their own tasks, but not the tasks of another user.
  • An admin user can edit their own tasks and the tasks of other users.
The user entity should have a method to test it they can edit a task, which takes the task in question as a parameter and returns a boolean that is true if they can. The user has a reference to it's own role, and consequently it's own permissions, and thus knows if it is allowed to edit its own or other user's task. The Task object has a reference to the user it is assigned to. So the CanEditTask method has access to all this information, and thus can work out whether the edit can take place. Here are the entites:
  
public class User : Entity
{
    protected string _username;
    protected Role _role;

    public virtual string Username
    {
        get { return _username; }
        set { _username = value; }
    }
               
    public virtual Role Role
    {
        get { return _role; }
        set { _role = value; }
    }

    public virtual bool CanEditTask(Task task)
    {
        if (task.AssignedTo == this && _role.Permissions.Select(permission => permission.Key).Contains(PermissionKey.EditOwnTask))
            return true;

        if (_role.Permissions.Select(permission => permission.Key).Contains(PermissionKey.EditAnotherUsersTask))
            return true;

        return false;
    }
}

public class Role : Entity
{
    protected string _rolename;
    private IList _permissions;

    public virtual string RoleName
    {
        get { return _rolename; }
        set { _rolename = value; }
    }

    public virtual IList Permissions
    {
        get { return _permissions; }
        set { _permissions = value; }
    }
}

public class Permission : Entity
{
    protected string _key;
    protected string _description;

    public virtual string Key
    {
        get { return _key; }
        set { _key = value; }
    }

    public virtual string Description
    {
        get { return _description; }
        set { _description = value; }
    }
 }

 public class Task : Entity
 {
    private string _description;
    private User _assignedTo;

    public virtual string Description
    {
        get { return _description; }
        set { _description = value; }
    }

    public virtual User AssignedTo
    {
        get { return _assignedTo; }
        set { _assignedTo = value; }
    }
}

public static class PermissionKey 
{
    public const string EditAnotherUsersTask = "EditAnotherUsersTask";
    public const string EditOwnTask = "EditOwnTask";
    public const string EditTaskForRole = "EditTaskForRole";
}
So the section to focus on is the CanEditTask(Task) method of the User entity. Here is the code for the test, which creates the permissions for editing tasks, creates the basic and admin roles with the required permissions, creates the users with their roles and creates tasks assigned to those user. It then asserts that the correct users can edit the required users tasks.
  
[TestClass]
public class UserTests
{
    [TestMethod]
    public void CheckUserCannotEditAnotherUsersTask()
    {
        Permission editAnotherUsersTaskPermission = new Permission()
        {
            Key = PermissionKey.EditAnotherUsersTask,
            Description = "Edit another user's task"
        };

        Permission editOwnTaskPermission = new Permission()
        {
            Key = PermissionKey.EditOwnTask,
            Description = "Edit own task"
        };

        Role basicRole = new Role()
        {
            RoleName = "Basic User",
            Permissions = new List()
            {
                editOwnTaskPermission
            }
        };

        Role adminRole = new Role()
        {
            RoleName = "Admin User",
            Permissions = new List()
            {
                editAnotherUsersTaskPermission
            }
        };

        User barryBasic = new User()
        {
            Username = "Barry Basic",
            Role = basicRole
        };

        User arnoldAdmin = new User()
        {
            Username = "Arnold Admin",
            Role = adminRole
        };

        Task arnoldsTask = new Task()
        {
            Description = "Arnold's Task",
            AssignedTo = arnoldAdmin
        };

        Task barrysTask = new Task()
        {
            Description = "Barry's Task",
            AssignedTo = barryBasic
        };

        Assert.IsTrue(barryBasic.CanEditTask(barrysTask));
        Assert.IsFalse(barryBasic.CanEditTask(arnoldsTask));
        Assert.IsTrue(arnoldAdmin.CanEditTask(barrysTask));
    }
}
And finally, here is the NHibernate mapping files to map all of this:
  


  
    
      
    
    
    
  





  
    
      
    
    
    
      
      
    
  





  
    
    
      
    
    
    
    

    
      
      
    
    
  




  
    
      
    
    
    
  

Wednesday 2 May 2012

Changing the Type of an Entity - Persistence with NHibernate

This is a follow up to my previous post which can be found here

So we have an Employee class and a Manger subclass, here are the classes:
  
    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;
        }

        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;
        }

    }

    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; }
        }
    }

    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();
        }
    }
And here are how they are mapped in NHibernate:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="HRSystem.Domain.Entities"
                   assembly="HRSystem.Domain">
  <class name="Employee" table="`Employee`">
    
    <id name="Id" column="Id" type="long">
      <generator class="identity"/>
    </id>
    
    <property name="Name" type="string" />
    <property name="PayrollNumber" type="string" />
    <property name="JoinedOn" type="datetime" />

    <joined-subclass name="Manager" table="`Manager`">
      <key column="`Id`"/>
      <property name="Department" type="string" />
      <property name="BecameManagerOn" type="datetime" />
    </joined-subclass>
    
  </class>
</hibernate-mapping>
A common question on Stack Overflow is about changing from one class to an inherited subclass in NHibernate. Here are some examples:

Convert type of entity to subtype with NHibernate/Hibernate
Can you change from a base class to a joined subclass type in nhibernate?
Is it ever valid to convert an object from a base class to a subclass
NHibernate - Changing sub-types

One common response is that one should use composition over inheritance. In the case of our Employee/manager example, this would mean (for example) that everyone in the company would be represented by an Employee entity. Each Employee entity would have a Role entity which would be subclassed to the respective role (BasicEmployee, Manager, etc), and any varying behaviour is deferred to the Role class. see Design Patterns by the Gang Of Four for more about favouring composition over inheritance.

This is the correct approach when it is the behaviour that varies, but in our case, Manager has some extra fields, and composition over inheritance doesn't account for this. We need somewhere to store the extra fields, and inheritance is a good solution to this.

Some people suggest that the employee object should have a ManagerFields objects, where fields specific to a manager role ('_department', '_becameManagerOn') would be stored if the employee is a manager, and would be null if not. Or even, just have nullable fields '_department' and '_becameManagerOn' on Employee that are only used if the Employee is a manager. You have to ask yourself if you would do this if you were not using NHibernate?

Some people suggest that if you are using inheritance, then to promote an Employee to Manager, you should use native SQL to insert a record into the manager table, so when you next get the Employee it will cast to manager. This doesn't even warrant discussion.

The answer, that some people touch on on in the StackOverflow questions, is that you need to to delete the old entity, and replace it with the new entity. This seems quite straightforward, but there is one aggrevating factor: often we would want it to keep the same ID, as this may be a foreign key in other entities. (This is not a problem if the entity being changed is a child in an aggregate, as the aggregate root would have all the references, and would be doing all the updating so could update these references.) But what if it is an aggregate root? What if other entities in the domain, or even other domains/systems reference it? We need a way to preserve the ID.

Replacing Entities While Maintaining the Identity - GUIDs


One answer is to use GUIDs instead of auto incrementing IDs, this way assigned identites can be used. In this situation, it is not too much of a problem to delete the old Employee entity and save the new Manager entity which has the same ID. This way, when any entity is loaded that references the Employee, it will load the Manager entity. The code for the base entity would now look like this:
    public class Entity
    {
        protected Guid? _id;    //Changed from long?
        protected int? _hashCode;

        public virtual Guid? Id    //Changed from long?
        {
            get { return _id; }
            set { _id = value; }
        }

        //Implementation of Equals() and GetHaskCode() etc...
    }
The mapping files now look like this:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="HRSystem.Domain.Entities"
                   assembly="HRSystem.Domain">
  <class name="Employee" table="`Employee`">
    
    <id name="Id" column="Id" type="guid">
      <generator class="assigned"/>
    </id>
    
    <property name="Name" type="string" />
    <property name="PayrollNumber" type="string" />
    <property name="JoinedOn" type="datetime" />

    <joined-subclass name="Manager" table="`Manager`">
      <key column="`Id`"/>
      <property name="Department" type="string" />
      <property name="BecameManagerOn" type="datetime" />
    </joined-subclass>
    
  </class>
</hibernate-mapping>
Remember what the Promote code lookes like?
        public virtual Manager Promote(string department)
        {
            Manager manager = new Manager();
            manager.Id = _id;       //Especially pay attention to this line!!!
            manager.Name = _name;
            manager.PayrollNumber = _payrollNumber;
            manager.JoinedOn = _joinedOn;
            manager.Department = department;
            manager.BecameManagerOn = DateTime.Now;
            return manager;
        }
Now the code for a service to promote an Employee to a Manager looks like this:
using System;
using NHibernate;
using NHibernate.Cfg;
using HRSystem.Domain.Entities;

namespace HRSystem.Application.Implementations
{
    public class EmployeeService
    {
        public void Promote(Guid employeeId, string department)
        {
            Configuration configuration = new Configuration();
            configuration.Configure();
            configuration.AddAssembly("HRSystem.Data");
            ISessionFactory sessionFactory = configuration.BuildSessionFactory();
            ISession session = sessionFactory.OpenSession();
            Employee employee = session.Get<Employee>(employeeId);
            Manager manager = employee.Promote(department);

            using (ITransaction transaction = session.BeginTransaction())
            {
                session.Delete(employee);
                session.Save(manager);
                transaction.Commit();
            }
        }
    }
}
This is all quite straigntforward. This application service loads the Employee, gets a Manager version of it, deletes the Employee and then Saves the Manager in its place, with the same Id. Now when you go to load an Employee with the same Id, it will return an entity of type Manager. No need to update any other entities that have references to the entity because they will load the new Manager entity, because the ID has not changed.

Replacing Entities While Maintaining the Identity - Auto Incrementing IDs


The problem comes when you are trying to do this in system that uses auto-incrementing ints or bigints as IDs. This is when things start to get a bit messy. You need to do two things: first you need to dynamically manipulate the NHibernate configuration so the Id of the Employee class can be assigned. Secondly, we need to allow the identity to be inserted in the database, and the only way to do this (that I know of) is to use native SQL. Assume the Entity class and mapping file have reverted back to using auto-increment IDs, the way they were at the beginning of this post. The resulting code for the application service now looks like this:
using System;
using NHibernate;
using NHibernate.Cfg;
using HRSystem.Domain.Entities;

namespace HRSystem.Application.Implementations
{
    public class EmployeeService
    {
        public void Promote(Guid employeeId, string department)
        {
            Configuration configuration = new Configuration();
            configuration.Configure();
            configuration.AddAssembly("HRSystem.Data");

            //Get the class mapping for the Employee entity.
            var employeeClassMapping = (from classMapping 
                     in configuration.ClassMappings 
                     where classMapping.MappedClass == typeof(Employee)
                     select classMapping).FirstOrDefault();

            //Change the identifier strategy to assigned dynamically.
            (employeeClassMapping.Identifier as SimpleValue).IdentifierGeneratorStrategy = "assigned";

            ISessionFactory sessionFactory = configuration.BuildSessionFactory();
            ISession session = sessionFactory.OpenSession();
            Employee employee = session.Get<Employee>(employeeId);
            Manager manager = employee.Promote(department);

            using (ITransaction transaction = session.BeginTransaction())
            {
                //First delete the Employee entity.
                session.Delete(employee);

                //Native SQL to allow identity insert.
                session.CreateSQLQuery("SET IDENTITY_INSERT Employee ON").ExecuteUpdate(); 

                //Then save the Manager entity.
                session.Save(manager);

                //This is needed to ensure manager is saved while identity insert is on.
                session.Flush(); 

                //Native SQL to disallow identity insert.
                session.CreateSQLQuery("SET IDENTITY_INSERT Employee OFF").ExecuteUpdate(); 

                transaction.Commit();
            }
        }
    }
}
I never said it would be pretty but it works.

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.