web-dev-qa-db-ja.com

エンティティの代わりにDTOを使用することは何ですか?

私はRCPアプリケーションに取り組んでいます。このアプリケーションは初めてです。

Spring Beanは、エンティティを保存/フェッチするビジネスロジックを記述するために使用されます。

ただし、エンティティをクライアントに直接送信するのではなくDTOに変換していますでクライアントにデータを入力します。保存中に、DTOをエンティティに変換して保存しています。

これらの変換の利点は何ですか?誰かが説明できますか?

18
Naveen Kocherla

開発者が「これを行う意味は何ですか?」そのために、いくつかの例を示します。


すべての例は、この単純なデータモデルに基づいています。

Personエンティティには5つのプロパティがあります:Id, FirstName, LastName, Age, CityId

また、アプリケーションがこのデータをさまざまな方法(レポート、フォーム、ポップアップなど)で使用すると想定できます。

アプリケーション全体がすでに存在しています。私が言及するすべては、既存のコードベースへの変更です。これは覚えておくことが重要です。


例1-基になるデータ構造の変更-DTOなし

要件が変更されました。人の年齢は政府のデータベースから動的に取得する必要があります(姓名に基づいて仮定しましょう)。

Age値をローカルに保存する必要がないため、Personエンティティから削除する必要があります。ここでは、エンティティがデータベースデータを表していることを理解することが重要です。データベースにない場合、エンティティにはありません。
政府のWebサービスから年齢を取得すると、別のオブジェクト(またはint)に保存されます。

しかし、フロントエンドにはまだ年齢が表示されています。すべてのビューはPerson.Ageプロパティを使用するように設定されましたが、現在は存在しません。問題が発生します:人物のAgeを参照するすべてのビューを修正する必要があります


例2-基になるデータ構造の変更-DTOを使用

古いシステムには、同じ5つのプロパティId, FirstName, LastName, Age, CityIdを持つPersonDTOエンティティもあります。 Personを取得した後、サービスレイヤーはそれをPersonDTOに変換して返します。

しかし今、要件は変更されました。人の年齢は政府のデータベースから動的に取得する必要があります(姓名に基づいて仮定しましょう)。

Age値をローカルに保存する必要がないため、Personエンティティから削除する必要があります。ここでは、エンティティがデータベースデータを表していることを理解することが重要です。データベースにない場合、エンティティにはありません。

ただし、中間にPersonDTOがあるため、このクラスがkeepAgeプロパティを使用できることを確認することが重要です。サービスレイヤーはPersonをフェッチし、PersonDTOに変換します。次に、政府のWebサービスから個人の年齢もフェッチし、その値をPersonDTO.Ageに格納します。そのオブジェクトを渡します。

ここで重要なのはサービスレイヤーを使用するユーザーには、古いシステムと新しいシステムの違いは見られないです。これにはフロントエンドが含まれます。古いシステムでは、完全なPersonDTOオブジェクトを受け取りました。また、新しいシステムでは、完全なPersonDTOオブジェクトを引き続き受け取ります。 ビューを更新する必要はありません

関心の分離というフレーズを使用する場合、これが意味します。2つの異なる関心(データベースへのデータの保存、フロントエンドへのデータの提示)があり、それぞれ異なるデータタイプが必要です。これら2つのデータ型に現在同じデータが含まれている場合でも、将来的には変更される可能性があります。
上記の例では、Ageは2つのデータ型の違いです。Person(データベースエンティティ)はAgeを必要としませんが、PersonDTO(フロントエンドのデータ型)では必要です。
懸念事項を最初から分離する(=個別のデータ型を作成する)ことで、コードベースはデータモデルに加えられた変更に対してはるかに回復力があります。

DTOオブジェクトがあると、新しい列がデータベースに追加されたときに、エンティティとDTOの両方にプロパティを追加するという二重の作業が必要になると主張するかもしれません。それは技術的に正しいです。 1つのクラスではなく2つのクラスを維持するには、少し余分な労力が必要です。

ただし、必要な労力を比較する必要があります。 1つ以上の新しい列が追加されると、いくつかのプロパティのコピー/貼り付けにそれほど時間がかかりません。データモデルが構造的に変更されると、おそらくコンパイル時にではなく実行時にのみバグを引き起こすような方法でフロントエンドを変更する必要があり、かなり多くの労力を要し、開発者はバグを探す必要があります。


もっと例を挙げましょうが、原則は常に同じです。

要約するには

  • 個別の責任(懸念)は、互いに個別に機能する必要があります。データクラスなどのリソースは共有しないでください(例:Person
  • エンティティとそのDTOが同じプロパティを持っているからといって、それらを同じエンティティにマージする必要があるという意味ではありません。手を抜かないでください。
    • より露骨な例として、データベースに国、歌、人が含まれているとします。これらのエンティティはすべてNameを持っています。ただし、すべてにNameプロパティがあるからといって、共有のEntityWithName基本クラスから継承する必要があるわけではありません。異なるNameプロパティには意味のある関係はありません。
    • プロパティのいずれかが変更された場合(たとえば、曲のNameTitleに名前が変更されるか、人がFirstNameLastNameを取得する)、それらはあなたです。そもそも不要だった継承を元に戻すために、より多くの労力を費やす必要があります
    • 露骨ではありませんが、エンティティがある場合はDTOは必要ないという主張は同じです。 nowを見ていますが、将来の変更に備えていません。 [〜#〜] if [〜#〜]エンティティとDTOはまったく同じです[〜#〜] if [〜#〜]データモデルが変更されることはありません。その後、DTOを省略できることは間違いありません。ただし、データモデルが変更されないことを保証することはできません。
  • 良い習慣は常にすぐに報われるとは限りません。古いアプリケーションに再度アクセスする必要がある場合、将来的には見返りが見込まれるかもしれません。
  • 既存のコードベースの主なキラーは、コードの品質を低下させ、維持できないスパゲッティコードの役に立たない混乱に発展するまで、コードベースの維持をより困難にし続けます。
  • コードベースをできるだけ長く維持できるようにするために、懸念事項と取得の分離を実装するなどの良い習慣は、悪いメンテナンスの滑りやすい傾斜を回避することを目的としています。

懸念事項を分離することを検討する経験則として、次のように考えてください。

すべての懸念事項(UI、データベース、ロジック)が別の場所にいる別の人によって処理されると仮定します。メールでのみ通信できます。

十分に分離されたコードベースでは、特定の懸念事項への変更は1人の担当者のみが処理する必要があります。

  • ユーザーインターフェイスの変更には、UI開発者のみが関与します。
  • データ保存方法の変更には、データベース開発者のみが関与します。
  • ビジネスロジックの変更には、ビジネス開発者のみが関与します。

これらすべての開発者が同じPersonエンティティを使用していて、エンティティに小さな変更が加えられた場合、プロセスにeveryoneが関与する必要があります。

しかし、レイヤーごとに個別のデータクラスを使用することにより、その問題はそれほど一般的ではありません。

  • データベース開発者が有効なPersonDTOオブジェクトを返すことができる限り、ビジネス開発者とUI開発者は、データの格納/取得方法の変更を気にしません。
  • ビジネス開発者がデータベースにデータを保存し、必要なデータをフロントエンドに提供する限り、データベース開発者とUI開発者は、ビジネスルールを作り直すかどうかは気にしません。
  • UIが `PersonViewModelに基づいて設計できる限り、UI開発者は必要に応じてUIを構築できます。データベースとビジネス開発者は、影響を受けないため、どのように行われるかを気にしません。

ここでのキーフレーズはそれらには影響しないためです。懸念事項を適切に分離することは、他の当事者への影響を最小限に抑えることを目的としています(したがって、関与する必要があります)。

もちろん、いくつかの大きな変更は、複数の人を含めることを避けることができません。まったく新しいエンティティがデータベースに追加されたとき。ただし、アプリケーションの有効期間中に行う必要がある小さな変更の量を過小評価しないでください。大きな変更は少数の少数です。

45
Flater