サービスは常にDTOを返す必要がありますか、それともドメインモデルも返すことができますか


174

大規模なアプリケーションを(再)設計しています。DDDに基づく多層アーキテクチャを使用しています。

データレイヤー(リポジトリの実装)、ドメインレイヤー(ドメインモデルとインターフェイスの定義-リポジトリ、サービス、作業単位)、サービスレイヤー(サービスの実装)を備えたMVCがあります。これまでのところ、すべてのレイヤーでドメインモデル(主にエンティティ)を使用し、ビューモデルとしてのみDTOを使用しています(コントローラーでは、サービスはドメインモデルを返し、コントローラーはビューに渡されるビューモデルを作成します)。

DTOの使用、使用、マッピング、および受け渡しに関する無数の記事を読んだことがあります。明確な答えはないことを理解していますが、ドメインモデルをサービスからコントローラーに返すかどうかはわかりません。ドメインモデルを返しても、コントローラーは常にビュー固有のビューモデルを作成するため、ドメインモデルはビューに渡されません。この場合、正当なようです。一方、ドメインモデルがビジネスレイヤー(サービスレイヤー)を離れると、適切に感じられません。サービスはドメインで定義されていないデータオブジェクトを返す必要がある場合があります。その場合、マップされていないドメインに新しいオブジェクトを追加するか、POCOオブジェクトを作成する必要があります(一部のサービスはドメインモデルを返すため、これは醜いです。効果的にDTOを返します)。

問題は、ビューモデルを厳密に使用する場合、ドメインモデルをコントローラーに返すことは問題ありませんか、それとも、サービスレイヤーとの通信に常にDTOを使用する必要がありますか?その場合、必要なサービスに基づいてドメインモデルを調整しても問題ありませんか?(率直に言って、私はそうは思いません。サービスはドメインが持っているものを消費する必要があるからです。)厳密にDTOに固執する必要がある場合、サービスレイヤーで定義する必要がありますか?(私はそう思います。)DTOを使用する必要があることは明らかです(たとえば、サービスが多くのビジネスロジックを実行して新しいオブジェクトを作成する場合)。ドメインモデルのみを使用する必要がある場合もあります(たとえば、Membershipサービスが貧血のUser( s)-ドメインモデルと同じDTOを作成することはあまり意味がないようです)-しかし、私は一貫性と優れた実践を好みます。

記事ドメインvs DTO vs ViewModel-それらをいつどのように使用するか?(および他のいくつかの記事)は私の問題と非常に似ていますが、この質問には答えません。記事EFのリポジトリパターンでDTOを実装する必要がありますか?も同様ですが、DDDは扱いません。

免責事項:存在していてファンシーであるためにデザインパターンを使用するつもりはありません。一方、アプリケーション全体の設計に役立ち、分離に役立つため、優れたデザインパターンとプラクティスを使用したいと思います。特定のパターンを使用するのは難しいとしても、少なくとも現時点では「必要」ではありません。

いつもありがとうございます。


28
クローズに投票する人たちのために-この質問をオピニオンベースとしてクローズする理由を説明していただけませんか?
Robert Goldwein、2014

20
@Aron「コードレビューは、ピアレビューのために取り組んでいるプロジェクトからのコードを共有するための質問と回答のサイトです。」-私の質問はコードに関するものではないので、そこからは外れています。SO:「あなたが直面した実際の問題についての質問に焦点を当ててください。あなたが試したこと、そしてあなたが何をしようとしているかについての詳細を含めてください。」-特定の専門家の問題があり、解決しようとしました。ここでの質問の多くはアーキテクチャに関するものであり、そのような質問は明らかに問題ないので、この質問のどこが悪いのか具体的に教えていただけますか?それ以上の誤解を避けることができますか?
Robert Goldwein 14

7
その質問をしていただきありがとうございます。あなたは私に好意を示し、私の人生をずっとシンプルで幸せにしてくれました、ありがとう。
Loa

9
@ RobertGoldwein、SO Close Mafiaを気にしないでください、あなたの質問は正当です。
hyankov 2017年

3
この質問をしていただきありがとう
ござい

回答:


177

ドメインモデルがビジネスレイヤー(サービスレイヤー)を離れると、正しくない

ガッツを正しく引き出しているように感じますか?Martin Fowlerによると、サービス層はアプリケーションの境界を定義し、ドメインをカプセル化します。つまり、ドメインを保護します。

サービスはドメインで定義されていないデータオブジェクトを返す必要がある場合があります

このデータオブジェクトの例を提供できますか?

厳密にDTOに固執する必要がある場合、サービスレイヤーで定義する必要がありますか?

はい、応答はサービス層の一部なので。「別の場所」で定義されている場合、サービスレイヤーはその「別の場所」を参照して、ラザニアに新しいレイヤーを追加する必要があります。

ドメインモデルをコントローラーにすべて返すのは問題ありませんか、それともサービスレイヤーとの通信には常にDTOを使用する必要がありますか?

DTOは応答/要求オブジェクトです。通信に使用すると意味があります。プレゼンテーションレイヤー(MVC-Controllers / View、WebForms、ConsoleApp)でドメインモデルを使用している場合、プレゼンテーションレイヤーはドメインに密結合されているため、ドメインを変更すると、コントローラーを変更する必要があります。

ドメインモデルと同じDTOを作成してもあまり意味がないようです)

これは、新しい目にとってのDTOの欠点の1つです。今、あなたは考えていますコードの重複をていますが、プロジェクトが拡大するにつれて、特に異なるチームが異なるレイヤーに割り当てられているチーム環境では、それはより理にかなっています。

DTOはアプリケーションをさらに複雑にする可能性がありますが、レイヤーも同様です。DTOはシステムの高価な機能であり、無料ではありません。

DTOを使用する理由

この記事では、DTOを使用するメリットとデメリットの両方を提供します。 //guntherpopp.blogspot.com/2010/09/to-dto-or-not-to-dto.html

次のように要約:

いつ使用するか

  • 大規模なプロジェクトの場合。
  • プロジェクトの寿命は10年以上です。
  • 戦略的、ミッションクリティカルなアプリケーション。
  • 大きなチーム(5人以上)
  • 開発者は地理的に分散しています。
  • ドメインと表示が異なります。
  • オーバーヘッドデータ交換の削減(DTOの本来の目的)

使用しない場合

  • 中小規模のプロジェクト(最大5メンバー)
  • プロジェクトの寿命は2年程度です。
  • GUI、バックエンドなどのための個別のチームはありません。

DTOに対する議論

DTOを使用した引数

  • DTOがない場合、プレゼンテーションとドメインは密接に結合されます。(これは小さなプロジェクトでは問題ありません。)
  • インターフェイス/ APIの安定性
  • 絶対に必要な属性のみを含むDTOを返すことにより、プレゼンテーションレイヤーを最適化できます。linq-projectionを使用すると、エンティティ全体をプルする必要はありません。
  • 開発コストを削減するには、コード生成ツールを使用します

3
こんにちは、あなたの答えをありがとう、それは本当に良い要約であり、リンクにも感謝します。私の文「時々サービスはドメインで定義されていないデータオブジェクトを返す必要がある」が間違って選択されました、それはサービスが1つのリポジトリから複数のDO(例えば属性)を組み合わせ、これらの属性の構成として1つのPOCOを生成することを意味します(ベースビジネスロジック上)。繰り返しますが、本当に良い答えをありがとう。
Robert Goldwein、2014

1
パフォーマンスに関する重要な考慮事項は、これらのドメインモデルまたはDTOがサービスから返される方法です。EFでは、ドメインモデルの具体的なコレクションを返すクエリを実現した場合(たとえば、.ToArray()またはToList()を使用)、すべての列を選択して、実現されたオブジェクトを設定します。代わりにクエリでDTOを投影する場合、EFは、DTOの入力に必要な列のみを選択するのに十分スマートです。これにより、転送するデータが大幅に少なくなる場合があります。
2014年

10
オブジェクトを「手動」でマッピングできます。退屈なことですが、モデルごとに2〜3分かかり、多くの反射(AutoMapperなど)を使用すると、常に多くの問題が発生する可能性があります
Razvan Dumitru

1
そのような内容でとても簡単に答えてくれてありがとう。あなたは私に好意を示し、私の人生をずっとシンプルで幸せにしてくれました、ありがとう。
Loa

1
処理が遅くなるため、1,000万件のプロジェクトがキャンセルされました...なぜ処理が遅くなったのですか?リフレクションを使用してDTOオブジェクトをあちこちに転送します。注意してください。オートマッパーも反射を使用します。
RayLoveless 2016年

11

DDDアプローチを実行することを決定したので、アプリケーションは十分に大きく、複雑であるようです。pocoエンティティまたはいわゆるドメインエンティティとサービスレイヤーの値オブジェクトを返さないでください。これを行う場合は、サービスレイヤーを削除します。もう必要ないためです。ビューモデルまたはデータ転送オブジェクトはドメインモデルメンバーにマップする必要があるため、サービスレイヤーに存在する必要があります。では、なぜDTOが必要なのでしょうか。多くのシナリオがある複雑なアプリケーションでは、ドメインとプレゼンテーションビューの懸念を分離する必要があります。ドメインモデルはいくつかのDTOに分割でき、いくつかのドメインモデルはDTOに縮小できます。したがって、モデルと同じであっても、階層化アーキテクチャでDTOを作成することをお勧めします。

サービスレイヤーとの通信には常にDTOを使用する必要がありますか? はい。ドメインモデルメンバーを使用してサービスレイヤーのリポジトリと通信し、それらをDTOにマップしてMVCコントローラーに戻る、またはその逆を行うときに、サービスレイヤーでDTOを返す必要があります。

必要なサービスに基づいてドメインモデルを調整してもよいですか? サービスはリポジトリとドメインメソッドおよびドメインサービスと通信するだけです。ニーズに基づいてドメイン内のビジネスを解決する必要があります。ドメインに必要なものを伝えるのはサービスタスクではありません。

厳密にDTOに固執する必要がある場合、サービスレイヤーで定義する必要がありますか?はい、サービスレイヤーのドメインメンバーにマップする必要があり、DTOをアプリケーションのコントローラーに配置することはお勧めしません(サービスレイヤーでリクエストレスポンスパターンを使用してみてください)。 !


1
ごめんなさい!あなたはここでそれを見ることができますehsanghanbari.com/blog/Post/7/...
エサン

10

私の経験では、実用的なことをする必要があります。「最高のデザインは、機能する最もシンプルなデザインです」-アインシュタイン。それが心です...

ビューモデルを厳密に使用している場合、ドメインモデルをコントローラーに返すのは問題ありませんか。それとも、サービスレイヤーとの通信に常にDTOを使用する必要がありますか?

絶対大丈夫です!ドメインエンティティ、DTO、ビューモデルがある場合、データベーステーブルを含めると、アプリケーションのすべてのフィールドが4か所で繰り返されます。私は、ドメインエンティティとビューモデルが問題なく機能する大規模なプロジェクトに取り組んできました。これに対する唯一の例外は、アプリケーションが分散され、サービスレイヤーが別のサーバーに存在する場合です。この場合、シリアル化の理由でDTOをネットワーク経由で送信する必要があります。

その場合、必要なサービスに基づいてドメインモデルを調整しても問題ありませんか?(率直に言って、サービスはドメインが持っているものを消費する必要があるので、そうは思いません。)

ドメインモデルは通常、ビジネスロジックを反映しており、通常はそのロジックのコンシューマーによって形作られるわけではないため、一般的に私は同意し、ノーと言います。

厳密にDTOに固執する必要がある場合、サービスレイヤーで定義する必要がありますか?(私はそう思う。)

それらを使用することに決めた場合、私は同意し、そうだと思います。結局のところ、サービスレイヤーはDTOを返すのに最適な場所です。

幸運を!


8

私はこのパーティーに遅れましたが、これは非常によくある重要な質問であり、対応を余儀なくされました。

「サービス」とは、ブルーブックでEvanが説明した「アプリケーション層」を意味しますか?私はあなたがそうすることを仮定します、その場合の答えは彼らがDTOを返すべきではないということです。「ドメインの分離」というタイトルの青い本の第4章を読むことをお勧めします。

その章では、エヴァンスはレイヤーについて次のように述べています。

複雑なプログラムをレイヤーに分割します。各レイヤー内に、まとまりがあり、下のレイヤーのみに依存するデザインを開発します。

これには正当な理由があります。ソフトウェアの複雑さの尺度として半順序の概念を使用する場合、層をその上の層に依存させると、複雑さが増し、保守性が低下します。

これを質問に当てはめると、DTOは実際にはユーザーインターフェイス/プレゼンテーションレイヤーの懸念事項であるアダプターです。リモート/プロセス間通信がDTO目的であることに注意してください(その投稿では、FDDが必ずしもDDD言語を話しているわけではありませんが、サービスレイヤーの一部であるDTOについては反対していることに注意してください)。

アプリケーション層がこれらのDTOに依存している場合、それはその上の層に依存しており、複雑さが増します。これによりソフトウェアのメンテナンスが難しくなることを保証できます。

たとえば、システムが他のいくつかのシステムまたはクライアントタイプとインターフェイスし、それぞれが独自のDTOを必要とする場合はどうなりますか?アプリケーションサービスのメソッドが返すDTOをどのようにして知るのですか?選択した言語が戻り値の型に基づいたメソッド(この場合はサービスメソッド)のオーバーロードを許可しない場合、どのようにしてその問題を解決しますか?そして、あなたが方法を見つけたとしても、なぜプレゼンテーション層の懸念をサポートするためにアプリケーション層に違反するのでしょうか?

実際には、これはスパゲッティアーキテクチャで終わる道への一歩です。私はこの種のデボルブとその結果を私自身の経験で見てきました。

私が現在働いている場所では、アプリケーション層のサービスがドメインオブジェクトを返します。インターフェース(UI /プレゼンテーション)レイヤーは以下のドメインレイヤーに依存しているため、これは問題とは見なされませんに。また、次の理由により、この依存関係は「参照のみ」のタイプの依存関係に最小化されます。

a)インターフェイス層は、アプリケーション層への呼び出しによって取得される読み取り専用の戻り値として、これらのドメインオブジェクトにのみアクセスできます。

b)アプリケーション層のサービスのメソッドは、その層で定義された「生の」入力(データ値)またはオブジェクトパラメータ(必要に応じてパラメータ数を減らすため)のみを入力として受け入れます。具体的には、アプリケーションサービスドメインオブジェクトを入力として受け入れ。

インターフェース層は、インターフェース層自体内で定義されたマッピング手法を使用して、ドメインオブジェクトからDTOにマップします。繰り返しますが、これにより、DTOは、インターフェイスレイヤーによって制御されるアダプターであることに焦点を合わせたままになります。


1
簡単な質問。私は現在、アプリケーション層から何を返すかを考えています。アプリケーションレイヤーからドメインエンティティを返すと、問題が発生します。ドメインを「外部」に本当にリークしたいですか?そのため、アプリケーション層からのDTOを検討していました。しかし、それは別のモデルを追加します。あなたの回答では、ドメインモデルを「読み取り専用の戻り値」として返すと述べました。どうやってやるの?つまり、どうすればそれらを読み取り専用にできますか?
マイケルアンドリュース

私はあなたの立場を採用するつもりだと思います。アプリケーションサービスはドメインモデルを返します。次に、ポートアダプタレイヤ(REST、プレゼンテーションなど)は、それらを独自のモデル(ビューモデルまたは表現)に変換します。アプリケーションとポートアダプターの間にDTOモデルを追加するのは、やり過ぎのようです。ドメインモデルを返すことは依然としてDIPに準拠しており、ドメインロジックは境界付きコンテキスト内にとどまります(必ずしもアプリケーション境界内にあるとは限りません。しかし、それは細かい妥協のようです)。
マイケルアンドリュース

@MichaelAndrews、私の答えが役に立ったと聞いてうれしいです。再:返されたオブジェクトが読み取り専用であるという質問。オブジェクト自体は本当に読み取り専用ではありません(つまり、不変です)。つまり、それは実際には(少なくとも私の経験では)起こらないということです。ドメインオブジェクトを更新するには、インターフェイス層は、a)ドメインオブジェクトのリポジトリを参照するか、b)アプリケーション層にコールバックして、受信したオブジェクトを更新する必要があります。これらはいずれも、DDDの優れた実践に対する明確な違反であり、私はそれらが自己強制されていると思います。よろしければ、お気軽に回答に投票してください。
BitMask777 2018年

この答えは、いくつかの理由で私にとって非常に直感的です。まず、同じアプリケーションレイヤーを複数のUI(API、コントローラー)で再利用でき、それぞれが適切と思われるモデルを変換できます。次に、AppでモデルをDTOに変換する場合。レイヤー、つまり、DTOはアプリで定義されます。レイヤー、つまり、DTOがバインドされたコンテキストの一部になったことを意味します(必ずしもドメインではありません!)-これは間違っているように感じます。
Robotron、

1
私はちょうどあなたにフォローアップの質問をするところで、それからあなたがすでにそれに答えたのを見ました:「アプリケーションサービスは決してドメインオブジェクトを入力として受け入れません」。できればもう一度+1します。
Robotron、

5

パーティーには遅れますが、まったく同じタイプのアーキテクチャに直面しており、「サービスからのDTOのみ」に傾いています。これは主に、オブジェクト内の有効性を維持するためにドメインオブジェクト/集合体のみを使用することを決定したためです。つまり、更新、作成、または削除するときのみです。データを照会するときは、EFをリポジトリーとしてのみ使用し、結果をDTOにマップします。これにより、読み取りクエリを自由に最適化し、ビジネスオブジェクトに適応させないようにすることができます。多くの場合、データベース関数を使用しているため、高速です。

各サービスメソッドは独自のコントラクトを定義するため、長期にわたって維持するのが簡単です。私は願います。


1
数年後、あなたがここで述べた理由のために、私たちは同じ結論に達しました。
Robert Goldwein

@RobertGoldweinいいね!今では自分の決断に自信が持てるようになりました。:-)
Niklas Wulff

@NiklasWulff:したがって、問題のDtoはアプリケーションレイヤーコントラクトの一部になりました。つまり、コア/ドメインの一部になります。Web APIの戻り値の型についてはどうですか?回答で言及されているDtoを公開していますか、それともWeb APIレイヤーで定義された専用のビューモデルがありますか?または別の言い方をすると、Dtosをビューモデルにマッピングしますか?
Robotron

1
@Robotron Web APIはなく、MVCを使用しています。したがって、はい、dto:sを異なるビューモデルにマップします。多くの場合、ビューモデルにはWebページを表示するための他の多くのものが含まれているため、dto:sからのデータはビューモデルの一部のみを構成します
Niklas Wulff

4

これまでのところ、すべてのレイヤーでドメインモデル(主にエンティティ)を使用しており、DTOはビューモデルとしてのみ使用しています(コントローラーでは、サービスはドメインモデルを返し、コントローラーはビューモデルを作成してビューに渡します)。

ドメインモデルは用語を提供するため(ユビキタス言語)アプリケーション全体に)をするため、ドメインモデルを広く使用することをお勧めします。

ViewModels / DTOを使用する唯一の理由は、(任意の種類のプレゼンテーションレイヤー)と(ドメインモデル)を分離するためのアプリケーションでのMVCパターンの実装です。この場合、プレゼンテーションとドメインモデルは疎結合です。ViewModel

サービスはドメインで定義されていないデータオブジェクトを返す必要がある場合があります。その場合、マップされていないドメインに新しいオブジェクトを追加するか、POCOオブジェクトを作成する必要があります(一部のサービスはドメインモデルを返すため、これは醜いです。効果的にDTOを返します)。

私はあなたがアプリケーション/ビジネス/ドメインロジックサービスについて話していると思います。

可能な場合はドメインエンティティを返すことをお勧めします。追加の情報を返す必要がある場合は、複数のドメインエンティティを保持するDTOを返すこともできます。

ドメインエンティティを介してプロキシを生成する第3部のフレームワークを使用する人々は、サービスからドメインエンティティを公開するのに困難を感じることがありますが、それは誤った使い方の問題にすぎません。

問題は、ビューモデルを厳密に使用する場合、ドメインモデルをコントローラーに返すことは問題ありませんか。それとも、サービスレイヤーとの通信に常にDTOを使用する必要がありますか?

99,9%のケースでドメインエンティティを返すのに十分だと思います。

DTOの作成を簡素化するために、それらにあなたのドメインエンティティをマッピングすることは、あなたが使用できるためにはAutoMapperを


4

ドメインモデルの一部を返品すると、契約の一部になります。コンテキスト外のものがそれに依存するため、コントラクトを変更するのは困難です。そのため、ドメインモデルの一部を変更しにくくすることになります。

ドメインモデルの非常に重要な側面は、変更が簡単なことです。これにより、ドメインの変化する要件に柔軟に対応できます。


2

次の2つの質問を分析することをお勧めします。

  1. 上位レイヤー(ビューとビューモデル/コントローラー)は、ドメインレイヤーが公開しているものとは異なる方法でデータを消費していますか?多くのマッピングが行われている場合、またはロジックさえ含まれている場合は、設計を再検討することをお勧めします。おそらく、データが実際に使用される方法に近いはずです。

  2. 上層を深く変える可能性はどのくらいありますか?(ASP.NETをWPFに交換するなど)。これが非常に異なり、アーキテクチャがそれほど複雑でない場合は、できるだけ多くのドメインエンティティを公開することをお勧めします。

私はそれが非常に広いトピックであると思います、そしてそれは本当にあなたのシステムがいかに複雑であるかとその要件に行き着きます。


私たちの場合、上層は確かに変化しません。場合によっては、サービスは非常に一意のPOCOオブジェクト(たとえば、より多くのドメインから構築された-ユーザーとユーザーが所有するファイルなど)を返します。場合によっては、サービスは単にドメインモデルを返します-たとえば、 "FindUserByEmail()の結果はユーザードメインモデルを返す必要があります-および私の懸念は、私たちのサービスがドメインモデルを返すこともあれば、新しいDTOを返すこともあります。この矛盾が気に入らないので、できる限り多くの記事を読みましたが、ドメインモデルのマッピング<-> DTOは1:1であり、ドメインモデルはサービスレイヤーから離れるべきではない-だから私は引き裂かれる
Robert Goldwein

このようなシナリオで、追加の開発作業を負担できる場合は、マッピングも使用して、レイヤーの一貫性を高めます。
jnovo 2014

1

私の経験では、オブジェクトオブジェクトのUIパターン(ネイキッドオブジェクトなど)を使用していない限り、ドメインオブジェクトをUIに公開することはお勧めできません。これは、アプリケーションが成長するにつれて、UIからのニーズが変化し、オブジェクトがそれらの変更に対応するように強制するためです。最終的に2つのマスター(UIとドメイン)を提供することになりますが、これは非常に辛い経験です。私を信じて、あなたはそこにいたくありません。UIモデルにはユーザーとの通信機能があり、ビジネスルールを保持するDOMAINモデルと永続モデルはデータの効果的な格納を扱います。これらはすべて、アプリケーションのさまざまなニーズに対応します。私はこれについてブログの投稿を書いている最中です。完了したら追加します。

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