どのレイヤーに検証を配置する必要がありますか?


18

Spring Bootを使用してRest APIを作成し、Hibernate Validationを使用してリクエスト入力を検証しています。

しかし、他の種類の検証も必要です。たとえば、更新データを確認する必要がある場合、会社IDが存在しない場合は、カスタム例外をスローする必要があります。

この検証は、サービス層またはコントローラー層に配置する必要がありますか?

サービス層:

 public Company update(Company entity) {
    if (entity.getId() == null || repository.findOne(entity.getId()) == null) {
        throw new ResourceNotFoundException("can not update un existence data with id : " 
            + entity.getId());
    }
    return repository.saveAndFlush(entity);
}

コントローラー層:

public HttpEntity<CompanyResource> update(@Valid @RequestBody Company companyRequest) {
    Company company = companyService.getById(companyRequest.getId());
    Precondition.checkDataFound(company, 
        "Can't not find data with id : " + companyRequest.getId());

    // TODO : extract ignore properties to constant

    BeanUtils.copyProperties(companyRequest, company, "createdBy", "createdDate",
            "updatedBy", "updatedDate", "version", "markForDelete");
    Company updatedCompany = companyService.update(company);
    CompanyResource companyResource = companyAssembler.toResource(updatedCompany);
    return new ResponseEntity<CompanyResource>(companyResource, HttpStatus.OK);
}

回答:


8

コントローラ層とサービス層の両方が特定のインターフェースを公開します。インターフェイスは、インターフェイスの使用方法に関する規約を定義します。通常、コントラクトは、どの引数(およびそのタイプと値)が期待されるか、どの例外がスローされるか、どの副作用が作成されるかなどを意味します。

これで、検証は基本的にコントローラーupdate()メソッドとサービスレイヤーupdate()メソッドのコントラクトの実施です。どちらも非常によく似た契約を持っているので、検証(契約の実施)も一般的である場合は当然です。

そのための1つの方法は、このコントラクトの検証を分離し、両方のレイヤーで呼び出すことです。通常、これは最も明確です-各クラス/メソッドは独自のコントラクトを実施しますが、パフォーマンス(データベースへのアクセス)またはその他の理由のために非実用的であることがよくあります。

他の可能性は、サービス層コントラクトで検証に失敗した場合の動作を明示的に定義しながら、この検証をサービス層に委任することです。通常、サービスレイヤーは一般的な検証エラー(または例外をスロー)を返し、コントローラーレイヤーはエラーに対して特定の方法で対応する必要があります。この場合、着信要求が無効であることを示す400 Bad requestを返します。

この設計では、サービス層のビジネスロジック(非常に汎用的である必要があります)とコントローラー(統合ロジックを処理する)の間のカップリングが過剰になる危険性があります。

とにかく、これは非常に物議を醸す質問であり、100人が100の答えで答えます。これはちょうど私の考えです。


1

入力はサービス層で確認する必要があります。

また、「IDが見つかりません」は論理的なエラー状態です。そのため、コントローラー層からスローする必要があります。

これもレイヤー/デザインに依存します。
サービス層が行うべきこと、およびコントローラー層に期待されること。


答えは、質問から追加の説明を求めることではありません。質問に明確化が必要な場合は、コメントが必要であり、不明な場合は閉鎖のフラグを立てる必要があります。はい、私はあなたがそれらの行動のどちらにも評判がないことを理解しています。

「入力チェック」はあいまいです。たとえば、フィールドにRequired属性を設定して入力する必要があることを示しますが、たとえば、あるフィールド値が別の値よりも大きいことをチェックする複雑なカスタム属性を設定することもできます。私見、比較検証は、コントローラー層よりもはるかに多くのビジネスサービス層を「匂い」させます。
JustAMartin

1

Hibernate検証は、データの整合性に対するチェックです。bbddからのRuntimeExceptionsを回避するため。これらはConstrainsで制御する必要がある検証とほとんど同じです。永続層に供給するのはビジネスレイヤーのみであるため、ビジネスレイヤーから送信されるデータの正当性を信頼する(またはしない)ことができます

DAOに検証を配置しません。上位層からの有効なデータを期待しています。エラーが発生した場合、その内容を認識する責任をbbddに委任します。

次に、ビジネス層で検証が行われます。すべてのビジネス検証は、データの整合性はなく、データの一貫性を保つことに焦点を合わせました。

最後に、コントロールレイヤーで以前の検証を行います。そのようなレイヤーのみに関連するもの。

ビジネス層でどの検証が実装されるかがすぐにわかります。最も一般的なのはidコントロールです。これは両方のレイヤーで簡単に実装できます。多くのコントローラーまたはクライアントがビジネスレイヤーを使用することが予想される場合、どこでも同じ検証を繰り返すのではなく、ビジネスレイヤーに配置される優秀な候補者になります。

コントローラーには、他のファサードで再現されない独自のルールと条件がある場合があります。それからそれはそのようなコントローラーに入れられる候補です。

検証対象を検討し、何に関係なくすべての人に適用したいかを考えてください。または、コンテキスト検証(「特定のコントロール/ビューファサードでのみ発生するものを検証しています」)の場合。


0

Javaショップでは、意図的にWebウィジェットの検証を3つの別個の操作に分割しています。

  1. 基本的なフォーマット-数字は数字でなければなりません。日付は有効な日付などでなければなりません。通常、この検証は無料です-ウィジェットのコンテンツをモデルにバインドするときに、Webフレームワークが自動的に行います。
  2. 単一のウィジェット検証-日付は過去でなければなりません。整数は1〜100でなければなりません。customerIdはデータベースなどに存在する必要があります。これはほとんどの場合、コントローラーレイヤーに属しますが、データリポジトリからのサポートが必要な場合があります。
  3. クロスウィジェットの検証-チェックアウト日はチェックイン日より後でなければなりません。死亡日は生年月日などの前にはできません。これは間違いなくビジネスルール検証です。これもコントローラーレイヤーに配置する傾向がありますが、再利用できるようにビジネスバリデーターに移行することもできます。

レイヤー1が失敗した場合、2または3はチェックされません。同様に、1が成功し、2が失敗した場合、3は実行されません。これにより、誤ったエラーメッセージが生成されなくなります。

ウィジェットのコンテンツではなくREST呼び出しの値について尋ねていますが、同じ原則が適用されます。


-1

テスト駆動のアプローチは、コントローラーがなく、別のオプションを選択する必要があるため、これに光を当てます。明らかに、bussinesルールは1つの場所にあるべきであり、これはあなたの決定における別の制約です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.