12

ドメイン駆動設計の原則を使用して、ASP.NETMVCアプリの書き直しに取り組んでいます。ユーザーエンティティを検証しようとしています。これまでのところ、基本的なルールを検証することができます(ユーザー名とパスワードがnull /空白以外の文字列であるなど)。ただし、ルールの1つとして、ユーザー名が一意であることを確認する必要があります。ただし、これを行うにはデータベースにアクセスする必要があります。つまり、IUserRepositoryをそのようにUserエンティティに挿入する必要があります。

public class User
{
    private readonly IUserRepository _userRepository;
    public User(IUserRepository repo)
    {
        _userRepository = repo;
    }

    public override void Validate()
    {
        //Basic validation code
        if (string.IsNullOrEmpty(Username))
            throw new ValidationException("Username can not be a null or whitespace characters");
        if (string.IsNullOrEmpty(Password))
            throw new ValidationException("Password can not be a null or whitespace characters");

        //Complex validation code
        var user = _userRepository.GetUserByUsername(Username);
        if (user != null && user.id != id)
            throw new ValidationException("Username must be unique")
    }
}

しかし、これは...よく間違っているようです。私のエンティティを私のリポジトリに依存させることは悪い考えのようです(私が間違っている場合は私を訂正してください)。ただし、エンティティに検証コードを含めることは理にかなっています。複雑な検証コードを配置するのに最適な場所はどこですか?

4

4 に答える 4

10

これらのタイプの状況で使用するパターンは、このタイプの検証ロジックをアプリケーションサービスに配置することです。Userエンティティは、ユーザーのセットの有効性ではなく、自身の有効性にのみ責任があるため、ある程度、これは理にかなっています。ユーザーを作成するアプリケーションサービスメソッドは、次のようになります。

public User CreateUser(string userName)
{
  if (this.userRepository.Exists(userName))
    throw new Exception();
  var user = new User(userName);
  this.userRepository.Add(user);
  return user;
}

アプリケーションサービスは、DDDを使用しているかどうかに関係なく存在する抽象化であるため、DDDが摩擦を与えるときにフォールバックするのに適した場所です。

于 2012-08-22T00:11:42.507 に答える
9

しかし、これは...よく間違っているようです。私のエンティティを私のリポジトリに依存させることは悪い考えのようです(私が間違っている場合は私を訂正してください)。

一般に、リポジトリへの依存は「間違っている」わけではなく、避けられない場合もあります。しかし、それは例外であり、可能な限り避けるべきだと思います。あなたのシナリオでは、この依存関係を持つことを再考するかもしれません。あなたがそれについて考えるならば、エンティティは他のエンティティについて知らないので、「一意性」はエンティティ自体の責任ではありません。では、なぜエンティティにこのルールを適用させるのでしょうか。

ただし、エンティティに検証コードを含めることは理にかなっています。複雑な検証コードを配置するのに最適な場所はどこですか?

あなたは「検証」を過度に一般化しているのではないかと思います。'Validate'メソッドを削除し、オブジェクトが最初に'invalid'状態にならないようにします。私は数ヶ月前に同様の質問に答えました。

ここで、一意性のルールに戻ります。これは、このビジネスルールの施行を純粋にドメインコードで表現できないという意味で、DDDが少し「漏れる」例の1つだと思います。私はこのようにアプローチします:

// repository
interface Users {
  // implementation executes SQL COUNT in case of relation DB
  bool IsNameUnique(String name);

  // implementation will call IsNameUnique and throw if it fails
  void Add(User user);
}

クライアントコードは、新しいユーザーを追加する前に、一意性を明示的にチェックする必要があることを認識します。そうしないと、クラッシュします。この組み合わせにより、ドメインコードにビジネスルールが適用されますが、通常は十分ではありません。追加の施行レイヤーとして、データベースにUNIQUE制約を追加するか、明示的なロックを使用することができます。

于 2012-08-14T18:12:15.220 に答える
5

しかし、これは...よく間違っているようです

いいえ、それはまったく間違っていません。ドメインモデルをリポジトリに依存させることはまったく問題ありません。それに加えて、さらに優れたインターフェースの背後にリポジトリを抽象化しました。

または、コンストラクタインジェクションを使用しないでください。リポジトリが必要なのがそれだけの場合は、リポジトリをValidateメソッドに渡します。

public class User
{
    public void Validate(IUserRepository repo)
    {
        //Basic validation code
        if (string.IsNullOrEmpty(Username))
            throw new ValidationException("Username can not be a null or whitespace characters");
        if (string.IsNullOrEmpty(Password))
            throw new ValidationException("Password can not be a null or whitespace characters");

        //Complex validation code
        var user = repo.GetUserByUsername(Username);
        if (user != null && user.id != id)
            throw new ValidationException("Username must be unique")
    }
}
于 2012-08-14T17:11:55.253 に答える
1

@oleksiiに同意します。仕様パターンを使用する方が、より良いアプローチです。検証はコンテキストによって意味が異なるため、この懸念を分割することは私にとって理にかなっています。

于 2012-08-14T17:59:01.947 に答える