0

何らかのコンテキスト(NH の ISession、IRepository など) にアクセスする必要がある POCO オブジェクトに対して検証を実行する最善の方法について、何度も何度も考えています。

まだ確認できる唯一のオプションはService Locatorを使用することなので、検証は次のようになります。

public User : ICanValidate {
    public User() {} // We need this constructor (so no context known)

    public virtual string Username { get; set; }

    public IEnumerable<ValidationError> Validate() {
        if (ServiceLocator.GetService<IUserRepository>().FindUserByUsername(Username) != null)
            yield return new ValidationError("Username", "User already exists.")
    }
}

私はすでにInversion Of Control と Dependency Injectionを使用していますが、多くの事実のために ServiceLocator が本当に好きではありません:

  • 暗黙の依存関係を維持するのが難しくなります。
  • コードのテストが難しくなります。
  • 潜在的なスレッドの問題。
  • ServiceLocator のみへの明示的な依存関係。
  • コードがわかりにくくなります。
  • テスト中に ServiceLocator インターフェイスを登録する必要があります。

しかし一方で、単純な POCO オブジェクトでは、ServiceLocator を使用せずに IoC/DI のみを使用して上記のような検証を実行する方法は他にありません。

現在、私はサービス層でそのような種類の検証を行っています。そのため、アクターがユーザー名を変更しようとするたびに (もちろん別のものかもしれません)、サービスはこの検証を実行します。明らかな欠点の 1 つは、User を使用するすべてのサービスがこのチェックを実行する必要があることです (1 回の呼び出しであっても)。

質問は次のとおりです。上記の状況で DI/IoC を使用する方法はありますか?

ありがとう、
ドミトリー。

4

2 に答える 2

1

通常、リポジトリは、フェッチ/保存するドメイン オブジェクトよりも高いレベルの抽象化です。ドメイン オブジェクトがリポジトリに依存している場合は、上流の設計に問題があることを示しています。

あなたが実際に持っているのは循環依存です。はIUserRepositoryに依存し、Userは にUser依存しますIUserRepository。これは、両方のオブジェクトが同じアセンブリにある場合にコンパイルされるという意味で、技術的には機能しますが、一般的な設計として問題が発生します。を処理したいが、それが由来するUserものについて何も知らないオブジェクトがあらゆる種類に存在する可能性があります。IUserRepository

あなたへの私の提案は、これを の「検証」プロパティにしないことですUser。検証はリポジトリ自体で実行する必要があります。または、さらに良いことに、保存しようとしたときにユーザー名が既に存在する場合は、リポジトリに例外を発生させます。

この提案には二次的な理由があります。その理由は並行性です。ユーザー名を検証してまだ存在しないことがわかったとしても、1 秒後にそのユーザーを保存しようとすると、そうではない可能性があります。とにかく、例外的なケース (既に存在するユーザー名を挿入しようとした) を処理する必要があります。これを考えると、事前に保証する方法がないため、可能な限り最後の瞬間まで延期することもできます。

ドメイン オブジェクトには依存関係があってはなりません。それらが自己検証する場合、検証はデータベース内の他のデータではなく、検証される実際のオブジェクトにのみ依存する必要があります。重複ユーザー名の制約は、実際にはデータの制約であり、ドメインの制約ではありません。

概要:この特定の検証をUserクラスの外に移動します。そこには属していません。それが、この特定のアンチパターンを使用していることに気付く理由です。

于 2010-05-10T23:49:04.470 に答える
1

アーロノートが言っていることを追加するだけです。ドメイン モデルの検証では、モデルに固有の属性のみを検証する必要があり、より大きなシステムのコンテキスト内では検証しないため、この設計には大きな問題があります。このような固有のプロパティの例としては、ユーザー名の長さ、許容される文字、姓と名の両方がファイルされていることなどの要件があります。

実行している検証はシステム全体の検証であり、サービス/リポジトリに属しています。ドメイン駆動設計を使用して設計された場合、このシステムは次のようになります。

public class User : ICanValidate {
    public User() {} 

    public virtual string Username { get; set; }

    public IEnumerable<ValidationError> Validate() {
        if (!string.IsNullOrEmpty(this.UserName))
          yield return new ValidationError("Username must not be empty");
    }
}

public class UserRepository : IUserRepository {
}

public static class UserService { 
  readonly IUserRepository Repository;

  static UserService() {
    this.Repository = ServiceLocator.GetService<IUserRepository>();
  }

  public static IEnumerable<ValidationError> Validate(User user) {
      if (Repository.FindUserByUsername(user.Username) != null)
          yield return new ValidationError("Username", "User already exists.")
  }
}
于 2010-05-11T00:18:14.980 に答える