エンティティオブジェクトをデータ転送オブジェクトとして使用することをお勧めしますか?


29

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

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


3
これは良い質問だと思いますが、読むのがとても難しいので、どうやって修正すればい​​いかよくわかりません。質問を編集してください。
-candied_orange

1
@CandiedOrange +1。これにより、これが非常に多くの賛成票を得たことがさらに怖くなっています。
guillaume31

回答:


23

それはあなた次第です。

ほとんどの人は、それは良い習慣ではないと言うでしょうが、場合によっては逃げることができます。

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を通過する前後にこれらのオブジェクトをどうするかは、その懸念の一部ではありません。また、そうすべきではありません。


1
簡単に要約します。DTOとPOCOの違いは何ですか?データを保持しているだけではありませんか?
ライヴ

1
@ guillaume31永続性を考慮せずに真のドメインモデルを作成することはできません。有効な状態でアグリゲートをロードするためにリフレクション(別の種類のハックは言うまでもなく、私のパブリックプロパティが別の名前のプライベートフィールドによってサポートされていることを条件とする)を使用する場合、パブリックインターフェイスは何が良いでしょうか?EFがパブリックインターフェイスを使用して集約を非表示にするためのインフラストラクチャを提供していない場合、データベースからロードするための追加のボイラープレートを記述する代わりに、ビジネスロジックを他の場所に保持し、ドメインを貧弱にした方が良いでしょう。
devnull

1
それで、パブリックコレクションゲッターにプライベートフィールドと同じ方法で名前を付けるよりも、ドメインモデル全体を貧弱にしたいですか?(コンストラクターの問題はさておき、オブジェクトのすべてのフィールドを取得するコンストラクターを公開することは、最初にドメインに対して最も頻繁に行われるため...)
guillaume31

1
ここから@Konrad :As of EF Core 2.1, only services known by EF Core can be injected. Support for injecting application services is being considered for a future release.アプリケーションサービスをエンティティに注入するのが非常に好きです。
devnull

1
@KonradドメインエンティティのDTOとしての使用を停止します(この方法でドメインエンティティを使用する場合)。DTOは、サービスインジェクションに対するEFのサポートに関係なく便利です。
devnull

8

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

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

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

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


コマンド・オブジェクトは、ビジネス層が約何かを知っておくべきことDTOです。
-devnull

私は、ビジネス層がおそらくインターフェースを介して間接的にコマンドオブジェクトを呼び出すと考えています。そして、機能をまったく持たない、データを運ぶ単純なオブジェクトのようなDTOを意味します。コマンドオブジェクトがDTO(martinfowler.com/eaaCatalog/dataTransferObject.html)かどうかはわかりません。
フアンカルロスエドゥアルドロマイナAc

1
通常、コマンドは実際のコマンドとそのハンドラーに分けられます。コマンドにはデータが含まれ、ハンドラーには動作が含まれます。このように使用すると、コマンド部分にはクライアントから受信したデータのみが含まれ、クライアントとビジネスレイヤー間のDTOとして機能します。
-devnull

DTOは永続性からドメインに流れませんが、データの着信時のように外部から来ることもあります(REST APIで考えてください)。devnullで指摘されているように、コマンドとイベントはややDTOであり、ビジネスレイヤーに転送されます。ハンドラーがビジネス層であるかどうかは、設計に依存します。
ライヴ

4

実際には非常に悪い考えです。Martin FowlerにはLocal DTOに関する記事があります

要するに、DTOPatternは、同じプロセス内のレイヤー間ではなく、ワイヤー経由などでプロセス外にデータを転送するために使用されました。


すべてのプログラミング手法と同様に、すべてに適合するサイズはありません。さもなければ、構築するアプリケーションごとに異なるパターンや手法について議論したり議論したりせず、常に同じものを使用します。DTOは、本質的にはPOPOであり、意図を示すために名前が付けられています。DTOを使用して、たとえばサービス、コントローラー間、およびビュー内への「データ転送」には何もありません。そこの転送され、名称または構造を示しているクラスのまたは制限するからで、データには何も
ジェームズ

4

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

いくつかの理由:

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

このため、エンティティフィールドをDtoにマッピングするために、この作業を支援するために、何らかのマッパーツールを使用する方が簡単で安全です。


1

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

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

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

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

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

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