ActiveRecord を使用すると、次のようなクラスを定義できます。
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() { ... }
}
これは素晴らしくシンプルですが、ロジックが大きく混ざり合っているため、クラスが非常に肥大化していることがわかりました。
階層化/ドメイン設計を使用すると、次のような同じクラスを定義できます。
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(); }
}
Active Record => レイヤード デザインからどのように移行されましたか?
- Name セッターでのエンティティ レベルの検証 => そのまま (DataAnnotation 経由で可能)
- ビジネス ロジック/ルールの検証 (一意の名前) => エンティティから新しい別の ContactValidator に移動
- 保存ロジック => 別のリポジトリ パターン クラスに移動 (UnitOfWork も使用)
- ロード ロジック => 別のリポジトリに移動
- リポジトリとの対話は、新しい ContactService を介して行われます (これにより、ContactValidator、ContactRepository、UnitOfWork などの使用が強制されます。発信者が ContactRepository を解放するのとは対照的です!)。
この階層化されたデザインのピア承認/提案を探しています - 通常、アクティブ レコード タイプ以外のデザインはしません! コメントをいただければ幸いです。
注意 - この例は意図的に単純化されています (UnitOfWork は実際には使用されず、Repository/Validator の新規作成は別の方法で処理されます)。