DTOをドメインオブジェクトにマッピングするためのベストプラクティス?


83

DTOをドメインオブジェクトにマッピングすることに関連する質問をたくさん見ましたが、彼らが私の質問に答えているとは感じませんでした。私はこれまで多くの方法を使って自分の意見を持っていますが、もう少し具体的なものを探しています。

状況:

多くのドメインオブジェクトがあります。CSLAモデルを使用しているため、ドメインオブジェクトは非常に複雑になり、独自のデータアクセスが含まれます。あなたはこれらをワイヤーで回したくありません。さまざまな形式(.Net、JSONなど)でデータを返すいくつかの新しいサービスを作成する予定です。このため(およびその他の理由)、ネットワーク上を通過する無駄のないデータ転送オブジェクトも作成しています。

私の質問は、DTOとドメインオブジェクトをどのように接続する必要があるかということです。

私の最初の反応は、FowlerのDTOパターンタイプのソリューションを使用することです。私はこれが何度も行われているのを見てきましたが、それは私にとって正しいと感じています。ドメインオブジェクトには、DTOへの参照が含まれていません。ドメインオブジェクトからDTOを作成するために、外部エンティティ(「マッパー」または「アセンブラー」)が呼び出されます。通常、ドメインオブジェクト側にORMがあります。これの欠点は、「マッパー」が実際の状況では非常に複雑になる傾向があり、非常に壊れやすいことです。

別のアイデアは、ドメインオブジェクトが単なる無駄のないデータオブジェクトであるため、DTOを「含む」ことです。ドメインオブジェクトプロパティは内部でDTOプロパティを参照し、要求された場合はDTOを返すことができます。これには問題はありませんが、気分が悪いです。NHibernateを使用している人々がこの方法を使用しているように見える記事をいくつか見ました。

他の方法はありますか?上記の方法の1つは使用する価値がありますか?もしそうなら、そうでなければ、なぜですか?


4
オートマッパーは面白そうです。私はそれが置き換えられる前にたくさんのコードを見てきました。私の主な問題は、なんらかの理由で大量のマッピングコードに悩まされる場合は、自分で制御したいということです。
ブライアンエリス

2
DTOからドメインオブジェクトに移行する場合、そのマッピングは100%手動です。ドメインオブジェクトを単なるデータコンテナではなく操作ベースに維持しようとしているため、解決するのははるかに難しい問題です。DTOに行く、それは簡単に解決できる問題です。
ジミーボガード

もう1つのオプションは、前回のプロジェクトで開始したServiceToolkit.NETのベータ版です。多分それはあなたを助けることができます:http

ドメインオブジェクトがdtoオブジェクトの知識を持ってはならないという点で間違っていることに同意します。この場合、それらは関連している可能性がありますが、それらの目的は完全に分離されており(dtoは通常、目的のために作成されます)、不要な依存関係を作成することになります。
Sinaesthetic 2013年

回答:


40

ドメインとDTOの間にマッパーを配置することの利点は、単一のマッピングのみをサポートしている場合ほど顕著ではありませんが、マッピングの数が増えるにつれて、そのコードをドメインから分離することで、ドメインをよりシンプルでスリムに保つことができます。余分な重みでドメインを乱雑にすることはありません。

個人的には、ドメインエンティティからマッピングを排除し、「マネージャー/サービスレイヤー」と呼ばれるものに責任を負わせようとしています。これは、アプリケーションとリポジトリの間に位置し、ワークフロー調整などのビジネスロジックを提供するレイヤーです(Aを変更する場合は、サービスAがサービスBと連携するようにBも変更する必要がある場合があります)。

可能な終了フォーマットがたくさんある場合は、たとえばエンティティを変換するために、Visitorパターンを使用できるプラグ可能なフォーマッタを作成することを検討するかもしれませんが、この複雑なものはまだ必要ありません。


「(Aを変更する場合は、サービスAがサービスBと連携するように、Bも変更する必要がある場合があります)」-これはビジネスロジックに該当しませんか?この部分はサービスではなくコントローラーに行くべきだと思いますか?
アイヤッパ

24

Jimmy Bogardによって作成さような、オブジェクト間の接続がなく、準拠している命名規則に依存するオートマッパーを使用できます。


9
Automapperは、誤ってプロパティを公開する可能性があります->セキュリティホール。DTOとして公開する必要があるものを明示的に言う方がよいでしょう。
デーモン2010年

4
@deamon:有効な懸念事項ですが、すべての厄介なマッピングコードを記述して作成される可能性のあるバグ(および人間の監視による潜在的なセキュリティホール)も同様です。オートマジックの道を進み、組み込みのカスタムマッピング機能を使用して5%を処理します。
メリット

@ deamon-公開してはいけないプロパティの条件付きマッピングを実行することはできませんか?AutoMapperがそのシナリオを処理すると思いますか?
リチャードB

AutoMapperを使用する場合、マッピングが正しく行われたかどうかをテストするために、すべての単体テストを実施することが非常に重要だと思います。
L-Four

8

マッピングロジックをエンティティ内に保持するということは、ドメインオブジェクトが、知る必要のない「実装の詳細」を認識していることを意味します。一般に、DTOは、(着信要求から、または外部サービス/データベースからの読み取りを介して)外部へのゲートウェイです。エンティティはビジネスロジックの一部であるため、これらの詳細をエンティティの外部に保持するのがおそらく最善です。

マッピングを別の場所に保持することが唯一の代替手段ですが、どこに行くべきでしょうか?私はマッピングオブジェクト/サービスを導入しようとしましたが、結局のところ、それは過剰設計のように見えました(そしておそらくそうでした)。小規模なプロジェクトでAutomapperなどを使用してある程度の成功を収めましたが、Automapperなどのツールには独自の落とし穴があります。Automapperのマッピングは暗黙的であり、コードの他の部分から完全に切り離されているため(「関心の分離」ではなく、「神に見捨てられたマッピングはどこにあるのか」のように)、マッピングに関連する問題を見つけるのはかなり困難でした。追跡が難しい場合があります。Automapperには用途がないのは言うまでもありません。問題を回避するために、マッピングはできるだけ明白で透過的なものでなければならないと思います。

マッピングサービスレイヤーを作成する代わりに、マッピングをDTO内に保持することに多くの成功を収めました。DTOは常にアプリケーションの境界に位置するため、DTOにビジネスオブジェクトを認識させ、DTOとの間でマッピングする方法を理解することができます。マッピングの数が妥当な量にスケーリングされた場合でも、それはきれいに機能します。すべてのマッピングが1つの場所にあり、データレイヤー、破損防止レイヤー、またはプレゼンテーションレイヤー内の一連のマッピングサービスを管理する必要はありません。代わりに、マッピングは、要求/応答に関係するDTOに委任された実装の詳細にすぎません。シリアライザーは通常、ネットワークを介して送信するときにのみプロパティとフィールドをシリアル化するため、問題が発生することはありません。個人的には、これが最もクリーンなオプションであることがわかりました。私の経験では、


7

T4テンプレートを使用してマッピングクラスを作成します。

Pro's-ランタイムマッパーよりも高速な、コンパイル時に利用可能な人間が読めるコード。コードを100%制御(部分的なメソッド/テンプレートパターンを使用して、アドホックベースで機能を拡張できます)

短所-特定のプロパティ、ドメインオブジェクトのコレクションなどを除き、T4構文を学習します。


3

ドメインオブジェクトをパラメーターとして受け取るDTOクラス内にコンストラクターを実装するにはどうすればよいでしょうか。

言う...このような何か

class DTO {

     // attributes 

     public DTO (DomainObject domainObject) {
          this.prop = domainObject.getProp();
     }

     // methods
}

10
絶対にしないでください。DTOレイヤーがドメインレイヤーを認識したり、ドメインレイヤーに依存したりすることは望ましくありません。マッピングの利点は、マッピングを変更することで下位層を簡単に切り替えることができること、またはマッピングを変更することで下位層での変更を制御できることです。今日はdtoAがdomainObjectAにマップされているとしましょうが、明日はdomainObjectBにマップする必要があります。あなたの場合、DTOオブジェクトを変更する必要があります。これは大したことではありません。マッパーの多くの利点を失いました。
Frederik Prijck 2014

2
まず第一に、ありがとう!:D。したがって、@ FrederikPrijckは、DTOとの間にレイヤーを挿入することで、DomainObject基本的にDTOのこの問題をドメインオブジェクトに依存するように努めます。したがって、すべての「構築作業」はmapper、両方のDTOに依存すると呼ばれる中間レイヤー(クラス)で行われます。およびDomainObjects。それで、それはこの問題への最善の、または一般的に推奨されるアプローチですか?私はその点が理解されていることを確認することだけを求めます。
ビクター

4
はい、レイヤーは「アセンブラー」と呼ばれます。3番目のレイヤーを使用してマッピングを定義することにより、アセンブラーレイヤーを別の実装に簡単に置き換えることができます(例:オートマッパーを削除して手動マッピングを使用する)。これは常により良い選択です。それを理解する最良の方法は、私があなたにオブジェクトAを与え、他の誰かがあなたにオブジェクトBを与える場所を考えることです。あなたはそれらの各オブジェクト(dllのみ)にアクセスできないので、マッピングは3番目を作成することによってのみ行うことができます層。ただし、どのオブジェクトにもアクセスできる場合でも、マッピングは関連していないため、常に外部で行う必要があります。
Frederik Prijck 2014

1
しかし、この答えは実際にはコメントと修正で「有用以上」であり、読者に問題についての認識とヒントをもたらします。それは本当に学ぶことに貢献します。なぜ投票するのかわかりません。それは私を助けます。 ..しかし、私はそれについての議論を始めたくありません..あなた次第です。とにかく答えてくれてありがとう。
ビクター

3
実際、私はこのアプローチが好きです。現在、コンストラクターを使用してエンティティをDTOにマップし、マッパークラスを使用して入力dtoをエンティティにマップしています。
dream83619 2017

1

別の可能な解決策:http//glue.codeplex.com

特徴:

  • 双方向マッピング
  • 自動マッピング
  • 異なるタイプ間のマッピング
  • ネストされたマッピングとフラット化
  • リストと配列
  • 関係の検証
  • マッピングのテスト
  • プロパティ、フィールド、メソッド


0

私が作成した、CodePlexでホストされているオープンソースのツールを提案できます:EntitiesToDTOs

DTOからエンティティへのマッピングおよびその逆のマッピングは拡張メソッドによって実装されます。これらは各エンドのアセンブラー側を構成します。

次のようなコードで終わります:

Foo entity = new Foo();
FooDTO dto = entity.ToDTO();
entity = dto.ToEntity();

List<Foo> entityList = new List<Foo>();
List<FooDTO> dtoList = entityList.ToDTOs();
entityList = dtoList.ToEntities();

DTOとドメインエンティティを相互に認識させるため、これはアーキテクチャ的に間違っています。
Raffaeu 2012

5
@Raffaeu ToDTO / ToDTOs / ToEntity / ToEntitiesメソッドは、アセンブラーを表す拡張メソッドとして定義されているため、そうは思いません。エンティティをDTOに、またはその逆に変換するロジックは、実際にはエンティティ/ DTOではなく、拡張メソッド(アセンブラー)にあります。
kzfabi 2012

2
「アセンブラ」について話す場合は、正しい方法で実装してください。それらをモジュール化し、容易に交換可能にし、依存性注入を使用します。ドメインモデル自体がDTOへの変換を認識する必要はありません。1つのドメインオブジェクトがあり、同じドメインを使用する50の異なるアプリケーションがあり、それぞれに独自のDTOがあるとします。50個の拡張機能を作成するつもりはありません。代わりに、アプリケーションごとに1つのアプリケーションサービスを作成し、必要なアセンブラーを依存関係としてサービスに挿入します。
Frederik Prijck 2014

0

どうしてこんなことができるの?

class UserDTO {
}

class AdminDTO {
}

class DomainObject {

 // attributes
 public DomainObject(DTO dto) {
      this.dto = dto;
 }     

 // methods
 public function isActive() {
      return (this.dto.getStatus() == 'ACTIVE')
 }

 public function isModeratorAdmin() {
      return (this.dto.getAdminRole() == 'moderator')
 }

}


userdto = new UserDTO();
userdto.setStatus('ACTIVE');

obj = new DomainObject(userdto)
if(obj.isActive()) {
   //print active
}

admindto = new AdminDTO();
admindto.setAdminRole('moderator');

obj = new DomainObject(admindto)
if(obj.isModeratorAdmin()) {
   //print some thing
}

@FrederikPrijck(または)誰か:提案してください。上記の例では、DomainObjectはDTOに依存しています。このようにして、dto <-> domainobjectのマッピングを行うコードを回避できます。

またはDomainObjectクラスはDTOクラスを拡張できますか?


0

もう1つのオプションは、ModelProjectorを使用することです。考えられるすべてのシナリオをサポートし、最小限のフットプリントで非常に使いやすいです。


0

そのために、Factory、Memento、およびBuilderパターンを使用できます。DTOからドメインモデルのインスタンスを作成する方法の詳細を工場で非表示にします。Mementoは、DTOとの間のドメインモデルのシリアル化/逆シリアル化を処理し、プライベートメンバーにアクセスすることもできます。Builderは、流暢なインターフェースでDTOからドメインへのマッピングを可能にします。

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