Castle’s ActiveRecord: Not for the Domain Purist in you…
I finally “broke down” and began using ActiveRecord just to try it out. Here’s the tutorial that I used. Now, don’t get me wrong – ActiveRecord is built on top of NHibernate and it’s one of the fastest ways to start working with your persistence layer.
So what’s the problem?
Well, it’s this:
using Castle.ActiveRecord;
namespace Foo.Domain
{
[ActiveRecord]
public class Dealer : ActiveRecordBase<Dealer>
{
private int id;
private string contactFirstName;
private string contactLastName;
private string businessName;
private Address address;
private string email;
private PhoneNumber contactPhoneNumber;
private PhoneNumber faxNumber;
public Dealer()
{
address = new Address();
contactPhoneNumber = new PhoneNumber();
faxNumber = new PhoneNumber();
}
[PrimaryKey(PrimaryKeyType.Identity, “DealerId”, Access=PropertyAccess.NosetterCamelcase)]
public int Id
{
get { return id; }
}
[Property]
public virtual string BusinessName
{
get { return businessName; }
set { businessName = value; }
}
[Property]
public virtual string ContactFirstName
{
get { return contactFirstName; }
set { contactFirstName = value; }
}
[Property]
public virtual string ContactLastName
{
get { return contactLastName; }
set { contactLastName = value; }
}
[Property]
public virtual string Email
{
get { return email; }
set { email = value; }
}
[Nested(“ContactPhoneNumber”)]
public virtual PhoneNumber ContactPhoneNumber
{
get { return contactPhoneNumber; }
set { contactPhoneNumber = value; }
}
[Nested(“FaxNumber”)]
public virtual PhoneNumber FaxNumber
{
get { return faxNumber; }
set { contactPhoneNumber = value; }
}
[Nested]
public virtual Address Address
{
get { return address; }
set { address = value; }
}
}
}
Those attributes are tagging persistence concerns all over my domain object!! The class also inherits from ActiveRecordBase
Finally, the class if forced to expose a setter for the value objects: ContactPhoneNumber, FaxNumber, and Address. The class should not have to allow external objects to directly set properties that access value objects. An external object can directly overwrite the value object references managed by my Dealer aggregate. (Please tell me I’m wrong on this.)
Of course, these are known drawbacks to using AR and it’s really only a concern for domain purists out there (who me?). All in all, ActiveRecord is great for getting started with simple domains that map fairly easily to their relevant database tables.
Here’s all I had to do in my test to get AR Unit Testing working.
- I borrowed and slightly modified the unit testing base class, AbstractModelTestCase, from this article. I modified it to support usage of NDbUnit (the original, not my modified version):
[SetUp]
public virtual void Init()
{
sqlDbUnit = new SqlDbUnitTest(GetConnectionString());
PrepareSchema();
CreateScope();
}
2. I wrote my specifications utilizing an NDbUnit schema and data file. It was as easy as:
using Foo.Domain;
using NUnit.Framework;
using NDbUnit.Core;
namespace Specifications.Foo.Domain.ActiveRecord
{
[TestFixture]
public class ADealerListWithOneDealer : AbstractModelTestCase
{
protected override void PrepareSchema()
{
sqlDbUnit.ReadXmlSchema(@”….SchemasDealerSchema.xsd”);
sqlDbUnit.ReadXml(@”….TestDataTestDataForADealerListWithOneDealer.xml”);
sqlDbUnit.PerformDbOperation(DbOperationFlag.CleanInsert);
}
[Test]
public void ShouldHaveASizeOfOne()
{
FlushAndRecreateScope();
Dealer[] dealers = Dealer.FindAll();
Assert.AreEqual(1, dealers.Length, “Should have a length of 1, given the test data.”);
}
}
}
As you can see, almost no persistence code was written to accomplish this trivial bit of work.
Repositories
Repositories are not explicit using the AR method of persisting entities, as shown above. Persistence concerns are directly associated with the entity (via Find(), FindAll(), etc). Ayende has an Active Record repository implementation in his Rhino Commons library. The SVN url is https://svn.sourceforge.net/svnroot/rhino-tools/trunk/ and the code can be found under rhino-commons/Rhino.Commons/Repositories.
Ayende’s ARRepository implementation appears to be a way around inheriting from ActiveRecordBase