S#arp Architecture

So now we have our basic domain model in place to support creating staff members from a domain perspective; the staff member is our “entity” in Domain Driven Design speak. Within the requirements, as was discussed, the employee number is the basis for staff member uniqueness; therefore, we’ll want to be able to compare staff members for equality on this field. The employee number is the “domain signature” of the entity. Here’s where we begin leveraging the S#arp Architecture framework to support consistent comparisons.

  1. Within StaffMemberTests.cs, add a new test called CanCompareStaffMembers().The method should look as follows:
    [Test]
    public void CanCompareStaffMembers() {
        string employeeNumber = "ABC123"; 
        StaffMember staffMember = new StaffMember(employeeNumber); 
        Assert.That(staffMember, Is.EqualTo(new StaffMember(employeeNumber)));
    }
    


  2. Compile the build; it’ll compile because objects are always comparable. So we’ve also taken care of the write-just-enough-code-to-get-it-to-compile step.


  3. Within NUnit, double click the CanCompareStaffMembers test to have it run. It will fail because the comparison is using the native .NET Equals() method.


  4. To enable consistent comparisons of domain objects, alter StaffMember to inherit from Entity, and add a DomainSignature attribute above the property(s) which makes the staff member unique, as follows:

    using SharpArch.Core.DomainModel;
    
    ... 
    
    public class StaffMember : Entity
    {
        ...
    
        [DomainSignature]
        public string EmployeeNumber { get; protected set; } 
    
        ...
    
    }
    

    Inheriting from Entity and adding the attribute has had a few effects on the StaffMember object. Equals() and GetHashCode() have been provided by default for consistent behavior. Contrast to the original inspiration from http://devlicio.us/blogs/billy_mccafferty/archive/2007/04/25/using-equals-gethashcode-effectively.aspx, Equals and GetHashCode now work independently of each other; consequently, in theory, two objects can have the same hashcode without being equal. This is very much by design. Additional differences between the current architecture and what was described in the blog post include: GetHashCode() has been moved up to Entity so only the “domain signature” has to be defined via the inclusion of attributes. Furthermore, as shown in the example above, the comparison behavior has been abstracted into a Entity class so as to serve as an optional base class for non-persistent objects. To read more about the importance of providing Equals and GetHashCode for NHibernate, see http://www.hibernate.org/hib_docs/nhibernate/html/persistent-classes.html#persistent-classes-equalshashcode.

    For calculating the HashCode, the “domain signature” gets automatically created by performing bitwise XORs, and some other fancy math, with all the domain signature properties resulting in an int HashCode defining what makes an object unique. For checking objects for equality, each domain signature property is compared, one by one. Alternatively, you can forego using attributes and define the domain signature comparison functionality yourself by overriding HasSameObjectSignatureAs(BaseObject compareTo) . Take a look at Northwind.Core/Order for an example of doing so.

    To reiterate the easier alternative, you could simply add the DomainSignature attribute above both the respective properties which has the same behavior as the example. To learn more about the difference between an entity and a value object, as well as a more in depth analysis of what defines an entity’s domain signature, I highly recommend reading Domain Driven Design by Eric Evans.

    So what if the domain signature happens to include a complex reference type (a non-primitive) as part of its domain signature? For example, assume that a “HomeAddress” object is part of what makes a StaffMember unique. The property comparison will invoke Equals between the properties, so it’s important to have the composite objects implement Equals as well, either by inheriting from Entity, ValueObject (which uses all of the object’s properties for comparison) or providing your own Equals method.

    The following is an illustrative example - i.e., don’t modify the tutorial code you’re working on to reflect this – that shows creating a multi-part signature using the DomainSignature across multiple properties. It’s alright if the properties which make up an object’s domain signature are value or reference types; the base class will take nulls into consideration when comparing the objects.

    using SharpArch.Core;
    using SharpArch.Core.PersistenceSupport;
    using System; 
    namespace Northwind.Core
    {
        public class StaffMember
        {
            ... 
            [DomainSignature]
            public string FirstName { get; set; } 
    
            [DomainSignature]
            public string LastName { get; set; } 
    
            [DomainSignature]
            public Address HomeAddress { get; set; }
        }
    }
    

  5. Compile and run just the CanCompareStaffMembers() test again to see it go green…gotta love the green! Now run all of the tests and you’ll see a bunch of the data related tests break and go red. Ack!! The Northwind sample project, similar to projects generated by the Visual Studio project template, is setup to use Fluent NHibernate Auto Mapping. The benefit of this is that we often don’t have to write any code to successfully map our new Entity to the database. The drawback is that NHibernate cannot be initialized until a couple more steps are taken to get our StaffMember wired up for data persistence; consequently, all of the data related tests will break until NHibernate can be successfully initialized again. But for now, we want to ignore the data layer implementation details so that we can continue to focus on the domain. So complete the next step to postpone this work a bit longer.


  6. Within NUnit, you’ll see two vertical tabs running down the top left corner of the UI tool: Tests and Categories. Select the Categories tab. You’ll see one category, called “DB Tests.” Go ahead and double-click this category to move it to the bottom. Now check the box to “Exclude these categories.” This will exclude every test that we’ve assigned to the “DB Tests” category when we run our tests. To see this in action, go back to the Tests tab and run all of the unit tests. You’ll see them all turn green while the /Data folder is grayed out; this means that they were not run. We’re doing this now so that I can forego explaining what’s necessary to properly wire up the StaffMember entity for persistence and keep our focus on developing the remainder of the domain related logic. In my normal, day to day development, I would get all of the unit tests passing again before moving on, but this is a nice trick to enable you to keep playing with the domain until you’ve got it solidified. But don’t you dare check in a broken test…you’ll have to put the rubber chicken above your door for the rest of the day if you do that!!

ScrewTurn Wiki version 2.0.36. Some of the icons created by FamFamFam.