S#arp Architecture

It’s just not cool to have ignored unit tests that we know are breaking. So at this point, now that our domain and coordination logic is solidified, let’s get the “DB Tests” category of tests passing again and complete the remainder of the data layer tasks.

  1. To start, bring up NUnit, click the Categories tab in the top left, and double click “DB Tests,” which is currently being excluded, so that it pops back up to the top pane. This category is no longer excluded and will now be included the next time we run the tests. So go back to the Tests tab, run all of the tests, and get ready to see some red! Running any of the data tests will complain about property accessors on the StaffMember object not being virtual while NHibernate is initialized.


  2. Alter the domain object to have virtual properties and a no-argument constructor. When NHibernate loads objects, it does so “lazily” by default. This means that if a request is made to load an object, NHibernate actually returns a proxy to the object. Only when the object is used in some way is an actual trip to the database made. This behavior can be modified on a case by case basis and will improve performance in some cases. To facilitate StaffMember objects being lazily loaded by NHibernate, its class properties and methods need to be defined as virtual. So under Northwind.Core, modify StaffMember.cs as follows:

    [DomainSignature]
    public virtual string EmployeeNumber { get; protected set;}
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
    

    Since you hadn’t done this before, NHibernate was complaining that the properties need to be defined as virtual to work with the default, lazy behavior. (To learn more about the proxy design pattern in general, see http://www.dofactory.com/Patterns/PatternProxy.aspx.)

    In addition to needing virtual properties to support the lazy loading of an object, NHibernate also requires that the domain object have a no-argument constructor in order to create and hydrate the object with data from the database. As we do not want anyone creating a StaffMember object without supplying an employee number, we can simply add a protected no-argument constructor, which NHibernate can access, as shown in the first line below:

    protected StaffMember() { } 
    
    public StaffMember(string employeeNumber) {
        Check.Require(!string.IsNullOrEmpty(employeeNumber) &&
            employeeNumber.Trim() != string.Empty,
            "employeeNumber must be provided"); 
    
        EmployeeNumber = employeeNumber;
    }
    

    In this way, our domain logic is enforced while allowing NHibernate to load the object without difficulty.



  3. Now let’s head back to NUnit and run the unit tests again. Progress! We now have all the tests passing except for one: CanConfirmDatabaseMatchesMappings. This test is complaining that the StaffMembers table doesn’t exist. Let’s tackle the missing table so that we can have a clean slate before diving back into the details of our custom Repository method.


  4. Create the table to support the Entity. Many developers start with a database model and allow it to be the driving factor in designing the application. This is an aspect of “model driven development.” The ADO.NET Entity Framework is a perfect example of a solid model driven design framework. Alternatively, within Domain-Driven Design, the approach which S#arp Architecture espouses, the database becomes an afterthought to support the domain model and is only put in place when absolutely necessary. To add the table run the following within the Northwind database:

    USE Northwind
    GO 
    CREATE TABLE dbo.StaffMembers
    (
          Id int NOT NULL IDENTITY PRIMARY KEY CLUSTERED,
          EmployeeNumber varchar(50) NOT NULL,
          FirstName varchar(50) NOT NULL,
          LastName varchar(50) NOT NULL,
    )
    
    GO
    



  5. Go back to NUnit one more time and run all the unit tests. Go green!


To summarize the few steps we had to take to provide persistence support for our new Entity:

  • Mark the properties and methods of the Entity to as virtual.
  • Add a protected (or public) no-parameter constructor to the Entity.
  • Add a table having column names the same as the Entity’s property names, having a table name being the plural equivalent of the Entity name, and having an identity column. (While the Northwind project requires that the Id column name be the Entity name + “Id,” the VS generated project defaults to simply requiring an identity column named “Id.” These and other settings may be adjusted on a per project basis via Fluent NHibernate Auto Mapping Conventions.)

And that’s it! If for some reason, you don’t like the mapping magic that the Fluent NHibernate Auto Mapping introduces, or need a little more control (and you will in some cases), you can provide Auto Mapping Overrides, you can manage your mappings with Fluent NHibernate ClassMaps or you can even use NHibernate HBM files…or any combination that you see fit (not that combining them would make your application very easy to read, mind you).


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