TDD with Monorail – Part 6

As promised the mapping and repository layers…

Not much of particular interest here really, but for the sake of completeness I will write about what I have done in these areas.

The Mappers

The mappers are mainly a supporting mechanism to allow me to send messages through the API interface rather than the domain objects themselves. A mapping classes simply takes an object of a specific type and returns an object of another type. The interface implemented by all my mappers also defines a method for doing this for a collection of the same types. Here is how I implemented a mapper for my SaveUserRequest message and User domain classes.

mappers

The test

using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using TimEscott.GrassRoots.Core.Mappers;
using TimEscott.GrassRoots.Specs.Builders.Domain;
using TimEscott.GrassRoots.Specs.Builders.Messages;

namespace TimEscott.GrassRoots.Specs.Unit.Mappers
{
    [TestFixture]
    public class When_mapping_a_save_user_request_to_a_user : UnitTestBase
    {
        private IUserMapper mapper;

        [SetUp]
        public void SetUp()
        {
            userBuilder = new UserBuilder();
            saveUserRequestBuilder = new SaveUserRequestBuilder();
            mapper = new UserMapper();
        }

        [Test]
        public void Should_create_a_user()
        {
            var expectedUser = userBuilder.BuildVaildObject();

            var actualUser = mapper.MapFrom(saveUserRequestBuilder.BuildVaildObject());

            Assert.That(actualUser.Email, Is.EqualTo(expectedUser.Email));
            Assert.That(actualUser.FirstName, Is.EqualTo(expectedUser.FirstName));
            Assert.That(actualUser.Surname, Is.EqualTo(expectedUser.Surname));
            Assert.That(actualUser.Phone, Is.EqualTo(expectedUser.Phone));
        }
    }
}

The implementation – notice that I have not implemented the map collection method yet as I do not have a requirement for it.

using System.Collections.Generic;
using TimEscott.GrassRoots.Core.Domain;
using TimEscott.GrassRoots.Core.Messages.Users;

namespace TimEscott.GrassRoots.Core.Mappers
{
    public class UserMapper : IUserMapper
    {
        public User MapFrom(ISaveUserRequest source)
        {
            return new User()
                       {
                           Email = source.Email,
                           FirstName = source.FirstName,
                           Surname = source.Surname,
                           Phone = source.Phone
                       };
        }

        public IEnumerable<User> MapCollection(IEnumerator<ISaveUserRequest> sources)
        {
            throw new System.NotImplementedException();
        }
    }
}

I am not really sure that there is much more to say about the mappers.

The Repository layer

This is where things are quite exciting for me. Coming from a world without ORM, now that I know about it I don’t know how I ever lived without it. I am using another part of the Castle stack here, ActiveRecord. To use ActiveRecord there is a couple of things I need to do to wire things up. Firstly I need to add the ActiveRecord config to the Windsor config files.

<facility
            id="activerecord"
            type="Castle.Facilities.ActiveRecordIntegration.ActiveRecordFacility, Castle.Facilities.ActiveRecordIntegration"
            isDebug="false"
            isWeb="true">

      <assemblies>
        <item>TimEscott.GrassRoots.Core</item>
      </assemblies>

      <config>
        <add key="connection.driver_class" value="NHibernate.Driver.SqlClientDriver" />
        <add key="dialect" value="NHibernate.Dialect.MsSql2000Dialect" />
        <add key="connection.provider" value="NHibernate.Connection.DriverConnectionProvider" />
        <add key="connection.connection_string" value="Data Source=TIMESCOTT-PC\SQLEXPRESS;Initial Catalog=GrassRoots;Integrated Security=SSPI"/>
      </config>
    </facility>

The other thing I am going to do is to get ActiveRecord to create the database schema for me. To do this I have added this line to Application_Start method in Global.asax. Now each time I start the application the tables are going to get dropped and recreated – so unless I want to re-generate the schema because of a change in the domain, I will comment this line out.

ActiveRecordStarter.CreateSchema();

There are much better ways to manage the database schema, but at this stage of the application I am not too worried about it. Something that I want to take some time to look at is Migrations for .Net as a solution to schema management.

A repository is a class that is only concerned with persisting and retrieving an object of a particular type. I have setup a generic interface IRepository<T> and an ActiveRecordRepository which implements it. Currently the interface only defines a Save method as this that is the only requirement I have for this stripe of development. Using the ActiveRecordMediator means that I only have to create this one class and that is all my repository layer needs to consist of. Here is the code:

using TimEscott.GrassRoots.Core.Domain;

namespace TimEscott.GrassRoots.Core.Repositories
{
    public interface IRepository<T> where T : IEntity
    {
        void Save(T instance);
    }
}
using Castle.ActiveRecord;
using TimEscott.GrassRoots.Core.Domain;

namespace TimEscott.GrassRoots.Core.Repositories
{
    public class ActiveRecordRepository<T> : IRepository<T> where T : class, IEntity
    {
        public void Save(T instance)
        {
            ActiveRecordMediator<T>.Save(instance);
        }
    }
}

I don’t have any unit tests on the repositories themselves as they do not appear to be obviously unit testable. Having said that I don’t think this is a huge problem as it is a very small amount of code and this area will be covered by my integration tests.

Mapping the User class to the database

Now I have got my repository sorted I am now ready to wire up some classes to the database. I am going to do this using attributes in the classes. Here is the User class tagged with the necessary ActiveRecord attributes.

using Castle.ActiveRecord;

namespace TimEscott.GrassRoots.Core.Domain
{
    [ActiveRecord("`User`")]
    public class User : EntityBase
    {
        [Property(NotNull = true)]
        public string Email { get; set; }

        [Property(NotNull = true)]
        public string FirstName { get; set; }

        [Property(NotNull = true)]
        public string Surname { get; set; }

        [Property(NotNull = true)]
        public string Phone { get; set; }
    }
}

You might have noticed that the User class does not have an Id field, that is defined in its base class, EntityBase. The IEntity interface and EntityBase class are used to ensure that any object that is mapped to the database has the required fields. Currently all that consists of is an Id of type Guid. Here is the class diagram.

domain

Okay, so all I need to do now is to create the database and run the application!

Just a note on choice of database engine…
I am currently using SQL Server Express 2005, I wanted to have a play with SQLite as it seems far easier to deploy and much more lightweight. Has anyone had any experience with setting this up with ActiveRecord or found a good post about this? I have had a look around and tried to set it up but seemed to be unable to get hibernate to open a connection to the database. Anyway I will continue to try and get SQLite running with this project and will put up a post when I get it working.

Advertisements

~ by Tim on 6 August 2008.

One Response to “TDD with Monorail – Part 6”

  1. For SQLite you could look at using Ayende’s Rhino Tools libraries. Using a mixture of binsor, the UnitOfWork and the Repository it’s very easy to configure a series of tests using SQLite.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

 
%d bloggers like this: