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).