web-dev-qa-db-ja.com

エンティティオブジェクトをデータ転送オブジェクトとして使用することは良い習慣ですか?

ある場合、なぜエンティティフレームワークは、レイヤー間でデータを転送するために同じプロパティを持つ新しいオブジェクトを作成するロジックを提供しないのでしょうか。

エンティティフレームワークで生成したエンティティオブジェクトを使用しています。

30
user2484998

それはあなた次第です。

ほとんどの人はそれは良い習慣ではないと言いますが、場合によってはそれでうまくいくことがあります。

EFは、複数の理由でDDDをうまく使用できませんでしたが、2つ目立っています。エンティティにパラメーター化されたコンストラクターを持たせたり、コレクションをカプセル化したりすることはできません。ドメインモデルにはデータと動作の両方を含める必要があるため、DDDはそれに依存しています。

ある意味で、EFは貧血ドメインモデルを強制するため、この場合はエンティティをDTOとして使用できます。ナビゲーションプロパティを使用する場合、いくつかの問題が発生する可能性がありますが、それらのエンティティをシリアル化してネットワーク経由で送信できます。しかし、それは実際的ではないかもしれません。送信する必要のないプロパティを持つ各エンティティのシリアル化を制御する必要があります。より簡単な方法は、データ転送用に調整された個別のクラスを単純に設計することです。 AutoMapper のようなライブラリがこの目的で作成されます。

例:次の定義のPersonというクラスがあるとします。

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; get; }

    // plus a bunch of other properties relevant about a person
}

従業員のリストをどこかに表示したい場合、IdFirstNameLastNameのみを送信するのが現実的です。ただし、他のすべての無関係なプロパティを送信する必要があります。応答のサイズを気にしない場合はそれほど大きな問題ではありませんが、関連するデータのみを送信するのが一般的な考え方です。一方、人物のリストを返すAPIを設計する場合は、すべてのプロパティを送信する必要があるため、エンティティをシリアル化して送信するのが合理的です。この場合、DTOクラスの作成は議論の余地があります。エンティティとDTOの混同が好きな人もいれば、そうでない人もいます。

更新された質問に答えるために、EFはORMです。その仕事は、データベースレコードをオブジェクトに、またはその逆にマッピングすることです。 EFを通過する前後にこれらのオブジェクトをどのように処理するかは、EFの問題の一部ではありません。それもそうではありません。

24
devnull

いいえそうではありません。

理想的には、DTOは永続リポジトリ(別名、データベーステーブル)と一致します。

ただし、ビジネスクラスは必ずしも一致しません。データベースにあるものに対して、追加のクラス、または分離されたクラス、または結合されたクラスが必要になる場合があります。アプリケーションが小さい場合、この種の問題は実際には発生しない可能性がありますが、中規模から大規模のアプリケーションでは、これが頻繁に発生します。

もう1つは、ビジネスレイヤーがDTOについて何も知らなくても、DTOは永続性を扱うもののドメインの一部であることです。

いいえ、それは悪い習慣です。

いくつかの理由:

  1. 新しいエンティティフィールドはデフォルトでDtoにあります。エンティティを使用すると、デフォルトですべての情報を利用できるようになります。これにより、機密情報を公開したり、少なくとも、APIを消費するユーザーには使用されない多くの情報を使用してAPIコントラクトを拡張したりすることができます。もちろん、いくつかの注釈(Java世界からの@JsonIgnoreなど)を使用してフィールドを無視できますが、これは次の問題につながります...
  2. エンティティの注釈/条件/コントロールがたくさんあります。 Dtoで送信するものを制御する必要があります。属性名が変更された場合(これにより、契約が破られます)、エンティティー以外の属性、属性の順序などを追加します。まもなく、簡単なたくさんの注釈、余分なフィールドがあり、毎回何が起こっているのか理解するのが難しいエンティティ。
  3. 柔軟性が少ない。 Dtoを使用すると、より多くのクラスで情報を分割したり、属性名を変更したり、新しい属性を追加したりすることができます。Dtoのようなエンティティを使用してこれを簡単に行うことはできません。
  4. 最適化されていません。エンティティは常に単純なDtoよりも大きくなります。したがって、常に無視/シリアル化される情報が多くなり、おそらくより多くの不要な情報が送信されます。
  5. レイジー問題。一部のフレームワーク(Hibernateなど)のエンティティを使用して、以前にデータベーストランザクション内でレイジーロードされなかった情報を取得しようとすると、ORMプロキシがトランザクションにアタッチされず、何らかの「レイジー例外」が発生します。エンティティのgetメソッドを呼び出すだけです。

したがって、エンティティフィールドをDtoにマッピングして、このジョブを支援するために、ある種のマッパーツールを使用する方が簡単で安全です。

6
Dherik

それは実際には非常に悪い考えです。 Martin Fowlerには ローカルDTOに関する記事 があります。

要するに、DTOパターンは、同じプロセス内のレイヤー間ではなく、ネットワーク経由など、プロセス外のデータ転送に使用されました。

5

@Dherikが言ったことを完了するために、エンティティオブジェクトをデータ転送オブジェクトとして使用することの主な問題は次のとおりです。

  1. トランザクションでは、DTOとして使用するため、エンティティに加えられた変更をコミットするリスクがあります(トランザクションでセッションのエンティティをデタッチできる場合でも、ほとんどの場合、前にこの状態を確認する必要がありますエンティティDTOに変更を加え、トランザクション内にないこと、または変更を永続化したくない場合はセッションが閉じられていることを確認します)。

  2. クライアントとサーバー間で共有するデータのサイズ:エンティティのすべてのコンテンツをクライアントに送信して、リクエストの応答のサイズを最小限にしたくない場合があります。エンティティからDTOを分離すると、特定のユースケースで送信するデータを特化できるように、より柔軟になります。

  3. 可視性とメンテナンス:エンティティのフィールドでjpa/hibernateアノテーションを管理し、同じ場所でjsonでシリアル化するためにジャクソンアノテーションを維持する必要があります(エンティティ実装から分離して、インターフェースによって継承されたインターフェースに配置できる場合でも)エンティティ)。次に、新しいフィールドを追加するときにDTOコンテンツを変更すると、別の人はおそらくそれがエンティティのフィールドであると考えることができるため、データベースで関係するテーブルのフィールド(@TransientケースのすべてのDTOフィールドの注釈..!)。

私の意見では、エンティティを読むときにノイズが発生しますが、私の意見は確かに主観的です。

1