私が現在取り組んでいるシステムでは、ドメイン ビジネス ルールと永続性制約の検証を分離することで、SRP (と思います!) に従っています。使いすぎた顧客の例を使用してみましょう。システムのビジネス ルールを満たすには、顧客が有効な郵便番号、住所、名前を持っている必要があるとします。さらに、顧客が選択したユーザー名はすべての顧客で一意である必要があるとしましょう。これは永続性制約として定義します。次の「本番環境の準備ができていない」疑似コードを検討してください。
public interface IPersistenceValidator<T>
{
bool IsValidForPersistence(T domainObj, IList<ValidationError> validationErrors);
}
public interface IValidatable
{
bool IsValid(IList<ValidationError> validationErrors);
}
public class Customer : IValidatable
{
public bool IsValid(IList<ValidationError> validationErrors)
{
//check for business rule compliance
}
}
public class CustomerDao : IPersistenceValidator<Customer>
{
public bool IsValidForPersistence(Customer domainObj, IList<ValidationError> validationErrors)
{
//check for persistence constraint compliance (user name is unique)
}
public bool SaveCustomer(Customer customer)
{
//save customer
}
}
上記で定義されたクラスは、次のようにサービス クラスに接続される場合があります。
public class SaveCustomerService
{
private CustomerDao _customerDao;
public SaveCustomerService(CustomerDao customerDao)
{
_customerDao = customerDao;
}
public bool SaveCustomer(Customer customer)
{
IList<ValidationError> validationErrors = new List<ValidationError>();
if (customer.IsValid(validationErrors))
{
if (_customerDao.IsValidForPersistence(customer, validationErrors))
{
return _customerDao.SaveCustomer(customer);
}
else
{
return false;
}
}
else
{
return false;
}
}
}
このアプローチに関する私の主な懸念は、CustomerDao の将来の消費者が、SaveCustomer() の前に IsValidForPersistence() を呼び出すことを知っている必要があることです。そうしないと、無効なデータが永続化されます。SQL レベルでこれを防ぐために DB 制約を作成することもできますが、それは面倒なように感じます。
IsValidForPersistence() を CustomerDao.SaveCustomer() に移動する必要があるようですが、SaveCustomer() の署名をリファクタリングして、ValidationErrors クラスへの参照を含める必要があります。大規模なリファクタリングに飛び込む前に、これらの問題に対処するための一般的な/推奨されるパターンについて、他のユーザーからフィードバックを得たいと思いました。
ありがとう