8

この質問は言語に依存しませんが、私は C# 派なので、POCO という用語を使用して、通常はゲッター フィールドとセッター フィールドを使用して、データ ストレージのみを実行するオブジェクトを意味します。

ドメイン モデルを作り直して超強力な POCO にしましたが、プロパティ値がドメイン内で意味をなすようにする方法に関していくつかの懸念が残っています。

たとえば、サービスの EndDate は、サービスが下にある契約の EndDate を超えてはなりません。ただし、チェックを Service.EndDate セッターに入れるのは SOLID 違反のように思えます。実行する必要がある検証の数が増えると、私の POCO クラスが雑然とすることは言うまでもありません。

私にはいくつかの解決策があります(回答に投稿します)が、それらには欠点があり、このジレンマを解決するためのお気に入りのアプローチは何ですか?

4

8 に答える 8

7

つまり、データを保存するだけのオブジェクトが必要であり、メソッドはなくアクセサーしか持たないという、まずい思い込みから始めていると思います。オブジェクトを持つことの要点は、データと動作をカプセル化することです。基本的に構造体に過ぎないものがある場合、どのような動作をカプセル化していますか?

于 2009-01-03T18:24:27.747 に答える
3

「Validate」または「IsValid」メソッドに対する人々の議論を常に耳にします。

個人的にはこれでうまくいくと思いますが、ほとんどの DDD プロジェクトでは、通常、オブジェクトの特定の状態に応じて許容される複数の検証が行われます。

したがって、ほとんどのプロジェクトはクラスごとに複数のバリデーター/状態になると考えているため、「IsValidForNewContract」、「IsValidForTermination」などを好みます。これはまた、インターフェースが得られないことを意味しますが、私が主張しているビジネス条件を非常によく反映する、集約されたバリデーターを書くことができます

この場合の一般的な解決策は、技術的なエレガンス (インターフェイス、デリゲートなど) をわずかに向上させるために、重要なこと (コードが何をしているのか) から焦点をそらすことがよくあると私は本当に信じています。ただ私に投票してください;)

于 2009-01-03T18:12:51.267 に答える
3

私の同僚が、かなりうまくいったアイデアを思いつきました。素晴らしい名前は思いつきませんでしたが、インスペクター/ジャッジと呼びました。

インスペクターはオブジェクトを見て、違反しているすべてのルールを教えてくれます。裁判官は、それについて何をすべきかを決定します。この分離により、いくつかのことが可能になります。すべてのルールを 1 か所 (インスペクター) に置くことができますが、複数のジャッジを持ち、コンテキストによってジャッジを選択することもできます。

複数のジャッジを使用する例の 1 つは、顧客には住所が必要であるというルールを中心に展開しています。これは、標準的な 3 層アプリでした。UI 層では、ジャッジは、入力する必要のあるフィールドを示すために UI が使用できるものを生成します。UI ジャッジは例外をスローしませんでした。サービス層には別のジャッジがいました。保存中に住所のない顧客が見つかった場合、例外がスローされます。その時点で、物事の進行を本当に止めなければなりません。

オブジェクトの状態が変化するにつれて、より厳しいジャッジもありました。これは保険の申し込みであり、見積プロセス中に保険契約が不完全な状態で保存されることが許可されました。しかし、そのポリシーをアクティブにする準備ができたら、多くのことを設定する必要がありました。なのでサービス側のQuoting JudgeはActivation Judgeほど厳しくはありませんでした。それでも、インスペクターで使用されるルールは同じだったので、何もしないことにした場合でも、何が完了していないかがわかります。

于 2009-10-14T19:22:05.313 に答える
2

1 つの解決策は、各オブジェクトの DataAccessObject に Validators のリストを持たせることです。Save が呼び出されると、各バリデータに対してチェックが実行されます。

public class ServiceEndDateValidator : IValidator<Service> {
  public void Check(Service s) {
    if(s.EndDate > s.Contract.EndDate)
      throw new InvalidOperationException();
  }
}

public class ServiceDao : IDao<Service> {
  IValidator<Service> _validators;
  public ServiceDao(IEnumerable<IValidator<Service>> validators) {_validators = validators;}
  public void Save(Service s) {
    foreach(var v in _validators)
      v.Check(service);
    // Go on to save
  }
}

利点は、非常に明確な SoC です。欠点は、Save() が呼び出されるまでチェックを受けないことです。

于 2009-01-03T18:13:34.680 に答える
2

以前は、通常、ValidationService などのサービスに検証を委任していました。原則として、これは依然として DDD の哲学に耳を傾けています。

内部的には、これには Validators のコレクションと、エラー オブジェクトのコレクションを返すことができる Validate() などの非常に単純なパブリック メソッドのセットが含まれます。

非常に簡単に言えば、C# では次のようになります。

public class ValidationService<T>
{
  private IList<IValidator> _validators;

  public IList<Error> Validate(T objectToValidate)
  {
    foreach(IValidator validator in _validators)
    {
      yield return validator.Validate(objectToValidate);
    }
  }
}

バリデーターは、デフォルトのコンストラクター内に追加するか、ValidationServiceFactory などの他のクラスを介して注入することができます。

于 2009-01-03T20:19:31.867 に答える
0

ここに別の可能性があります。検証は、ドメイン オブジェクトのプロキシまたはデコレータを介して行われます。

public class ServiceValidationProxy : Service {
  public override DateTime EndDate {
    get {return EndDate;}
    set {
      if(value > Contract.EndDate)
        throw new InvalidOperationexception();
      base.EndDate = value;
    }
  }
}

利点: 即時検証。IoC 経由で簡単に構成できます。

欠点: プロキシの場合、検証済みのプロパティは仮想である必要があり、デコレーターの場合、すべてのドメイン モデルはインターフェイス ベースである必要があります。検証クラスは少し重くなります。プロキシはクラスを継承する必要があり、デコレータはすべてのメソッドを実装する必要があります。命名と編成は混乱を招く可能性があります。

于 2009-01-04T01:46:12.443 に答える
0

実際には、おそらくそれがロジックに最適な場所だと思いますが、それは私だけです。すべての条件をチェックして true/false を返すある種の IsValid メソッド、おそらくある種の ErrorMessages コレクションを使用することもできますが、エラー メッセージは実際にはドメイン モデルの一部ではないため、それはあいまいなトピックです。私はRoRでいくつかの作業を行ったので、少し偏っています.RoRのモデルは本質的にそれを行っています.

于 2009-01-03T18:11:25.620 に答える
0

別の可能性は、各クラスを実装することです

public interface Validatable<T> {
  public event Action<T> RequiresValidation;
}

そして、設定する前に各クラスの各セッターにイベントを発生させます(属性を介してこれを達成できるかもしれません)。

利点は、リアルタイムの検証チェックです。しかし、コードが乱雑で、誰がアタッチを行うべきかが不明です。

于 2009-01-03T18:18:21.863 に答える