Active Records vs. Repository - pros and cons?

Matt picture Matt · Jun 29, 2011 · Viewed 10.1k times · Source

Using ActiveRecord you might define a class like this:

class Contact
{
  private String _name;
  public String Name
  {
    get { return _name; }
    set 
    { 
      if (value == String.IsNullOrWhiteSpace())
        throw new ArgumentException(...);
      else
        _name = value;
    }
  }

  public Boolean Validate() { ... /* check Name is unique in DB */  }

  public Boolean Save() { ... }

  public static List<Contact> Load() { ... }
}

Whilst this is nice and simple, I've found my classes become very bloated with a big mix of logic going on!

Using a layered/domain design you might define the same class like:

class Contact
{
    [Required(AllowEmptyStrings=false)]
    public String Name { get; set; }
}

class ContactService : IService
{
    public List<Contact> LoadContacts() { return (new ContactRepository()).GetAll(); }
    public Contact LoadContact(int id) { return (new ContactRepository()).GetById(id); }
    public Boolean SaveContact(Contact contact)
    {
        if (new ContactValidator().Validate(contact))
            new ContactRepository().Save(contact);
    }
}

class ContactRepository : IRepository
{
    public List<Contact> GetAll() { ... }
    public Contact GetById(int Id) { ... }
    public Boolean Save(Contact contact) { ... }
}

class ContactValidator : IValidator
{
    public Boolean Validate(Contact contact) { ... /* check Name is unique in DB */ }
}

class UnitOfWork : IUnitOfWork
{
    IRepository _contacts = null;
    public UnitOfWork(IRepository contacts) { _contacts = contacts; }
    public Commit() { _contacts.Save(); }
}

How was it migrated from Active Record => layered design?

  • Entity level validation in the Name setter => remains (ableit via a DataAnnotation)
  • Business logic/rule validation (unique Name) => moved from entity into a new separate ContactValidator
  • Save logic => moved to a separate Repository pattern class (also with a UnitOfWork)
  • Load logic => moved to the separate Repository
  • Interaction with the Repository is via a new ContactService (which will enforce use of ContactValidator, ContactRepository, UnitOfWork, etc - opposed to letting the caller loose with the ContactRepository!).

I'm looking for peer approval/suggestions for this layered design - I don't usually design outside of Active Record type! Any comment appreciated.

NB - This example is deliberately simple (the UnitOfWork isn't really used and the newing of Repository/Validator would be handled differently).

Answer

g.foley picture g.foley · Aug 25, 2011

It really depends on how complex your domain logic is. For example if I was writing a simple blog then active record will be fine, mostly the application is saving and loading data. Its simple and active record pattern is the right tool for the job.

However if I was writing software for a shipping company in which there are many complex business rules and processes then using the repository pattern, along with other Domain Driven Design patterns will provide at much more maintainable code in the long run.

Using domain driven design you would use the specification pattern to achieve your validation.