web-dev-qa-db-ja.com

データベースのコンテンツに依存するドメインモデルルールをどこで検証しますか?

私は、管理者がフィールドを含むフォームを定義できるシステムに取り組んでいます。定義されたフォームは、システムにデータを入力するために使用されます。フォームは、GUIを介して人間が入力する場合もあれば、別のシステムから報告された値に基づいて入力される場合もあります。

管理者は、フィールドごとに、フィールドに許可される値を制限する検証ルールを定義できます。検証ルールは、「フィールドに入力された値はTrueまたはFalseである必要があります」から「フィールドに入力された値はデータベースのテーブルBの列Aに存在する必要があります」までの任意の値です。管理者はいつでもフィールドの検証ルールを変更できます。

このシナリオでは、各フィールドが正しく入力されていることを検証するのに最も適した場所は何だと思いますか?私は現在、主に2つのアプローチを考えています。

オプション#1:ドメインモデルで検証

各フィールドオブジェクトには、管理者が指定した検証ルールが含まれます。 Fieldオブジェクトには、IValidatorへの参照も含まれます。フィールドの値を設定しようとすると、フィールドは指定された値と検証ルールをIValidatorに渡します。指定された値が有効でない場合、ValidationExceptionがスローされ、他のシステムへのGUI /インターフェイスで適切に処理されます。

長所:

  • 検証ルールに違反するフィールドが誤って割り当てられたフィールドに対する強力な保護

短所:

  • データアクセスレイヤーは、検証をバイパスし、現在の検証ルールに違反するフィールドを構築できる必要があります。管理者がフィールドの検証ルールを変更しても、古いデータに基づいてフィールドオブジェクトを構築できるようにする必要があります。数年前に入力されたフォームをレンダリングするとき。これは、フィールドを保存するたびに現在の検証ルールを保存することで解決できる可能性があります。

  • この設計では、フィールドモデルはIValidatorを介してデータアクセスレイヤー/リポジトリに間接的にリンクしています。ドメインモデルへのサービス/リポジトリの挿入は、一般的に 眉をひそめている のようです。

オプション#2:サービスで検証

フィールドの値を設定するすべての試行が、検証ルールが確実に実行されるサービスを通過するようにしてください。検証ルールに違反している場合は、ValidationExceptionをスローします。

もちろん、データアクセスレイヤーは、以前にDBに永続化されているフィールドオブジェクトを作成するときに、サービスを使用しません。

長所:

  • 「サービス/リポジトリをドメインモデルに挿入しない」という考え方に違反していない。

  • フィールドを永続化するときに、現在の検証ルールを永続化する必要はありません。サービスは、フィールドの現在の検証ルールを単純に検索できます。履歴データを見ると、フィールドの値は変更されません。

短所:

  • サービスを使用してフィールド値を設定する必要のあるすべてのロジックが実際にそうであるとは限りません。私はこれを大きな欠点だと考えています。誰かが "thisField.setValue(thatField.getValue())"を書いているだけで、thisFieldの検証ルールに違反する可能性があります。これは、データアクセスレイヤーがフィールドを永続化しようとしているときに、フィールドの値が入力規則と一致するようにすることで軽減できる可能性があります。

私は現在、オプション#2よりもオプション#1を好みます。これは主にこれをビジネスロジックと見なしており、オプション#2はシステムに不正なデータを導入するリスクが高いと感じているためです。どのオプションを選びますか、またはこのシナリオに適合する、説明されている2つのオプションよりも優れた別の設計はありますか?

編集(検証の複雑さ)

現在のところ、検証ケースは比較的単純です。フィールド値は、たとえば数値、日付、時刻付きの日付、またはデータベース列の既存の値。ただし、時間の経過とともに複雑さが徐々に増加すると思われます。たとえば、検証ソリューションは国際化を念頭に置いて構築する必要があります。日付などはロケール固有の構文で入力できます。

ここでは、オプション#1に進むことを決定しました。ドメインモデルに多くの責任を割り当てないように注意してください。同様の状況に直面している人は、関連する質問 レイヤードアーキテクチャでの検証と承認 および データ入力の検証-どこで?どれくらい? を確認することもできます。

10
Lauri Harpf

検証はどのくらい複雑ですか?多くの場合、検証では、フィールドと正確に評価されるフィールドに依存するビジネスルールの組み合わせが必要です。

検証が複雑になるほど、オプション2は難しくなり、パフォーマンスが低下します。

もちろん、データ層は永続化時に検証サービスを呼び出すことができます。これは、ルールの変更が原因でデータが無効な状態になるという奇妙な状況に役立ちます。

コメントする価値のあるもう1つの項目は、なんらかのQAサイクルなしで検証ルールを変更できることです。しかし、それは別のスレッドのトピックです。

上記の情報を考えると、オプション1は、規律を維持し、検証と永続性を分離していると仮定すると、最も柔軟に思えます。

4
deschaefer

レイヤーが不足している可能性があります。アプリケーションの詳細(要件、アーキテクチャなど)がわからない場合、クライアント(それが誰であれ)のようなものを実行します->アプリケーションサービス->ドメインモデル

アプリケーションサービスレイヤーは、リポジトリおよびビジネスロジックを含むドメインモデルと対話することができます。だからあなたは次のようなものを持つことができます:

FieldUpdateService >> updateField(fieldId, newValue)
  List<FieldRule> rules = this.rulesRepository.findRulesFor(fieldId)
  Field field = this.fieldRepository.find(fieldId)
  try 
    field.updateValue(rules,newValue)
    fieldRepository.save(field)
  catch 
    // manage error

Fieldと彼のFieldRulesの間に関係があり、HibernateのようなORMをJavaで使用する場合は、次のことだけを行います。

FieldUpdateService >> updateField(fieldId, newValue)
  Field field = this.fieldRepository.find(fieldId)
  try 
    field.updateValue(newValue)
    fieldRepository.save(field)
  catch 
    // manage error

Field >> updateValue(newValue)
  for rule in rules
     if !rule.isValid(newValue)
        throw ValueNotAllowedException
  this.value = newvalue

ORMは、要求したオブジェクトのグラフをクエリしてインスタンス化するためです。

誰かができることについての疑問について

thisField.setValue(thatField.getValue()) "およびthisFieldの入力規則は、誰も賢明ではなく違反される可能性があります

誰かが次のように書くこともできます:FieldRule alwaysReturnTrueRule = new FieldRule {isValid(newValue){return true; }} field.updateValue( "uncheckedValue"、alwaysReturnTrueRule)これはあいまいな例ですが、何を言っても、コードの不正使用からユーザーを保護するものは何もありません。おそらく、適切なドキュメンテーションとコミュニケーションは対面するでしょう。コードの悪用からあなたを守るものは何もないと思います。

0
gabrielgiussi