web-dev-qa-db-ja.com

マイクロサービスRESTful API-DTOかどうか?

REST API-DTOかどうか

マイクロサービスのコンテキストでこの質問を再確認したいと思います。これは元の質問からの引用です。

私は現在プロジェクトのREST-APIを作成しており、ベストプラクティスに関する記事を読んでいます。多くの人はDTOに反対していて、単にドメインモデルを公開しているようですが、他の人はDTO(またはユーザーモデルなど、呼び出したいもの)は悪い習慣だと考えているようです。個人的には、この記事は非常に理にかなっていると思いました。

ただし、すべての追加のマッピングコード、DTOの対応物と100%同一である可能性があるドメインモデルなど、DTOの欠点も理解しています。

さて、私の質問

アプリケーションのすべてのレイヤーで1つのオブジェクトを使用するように調整しています(つまり、DTOを作成して各フィールドを手動でコピーするのではなく、ドメインオブジェクトを公開するだけです)。そして、私の契約とコードの違いは、_@JsonIgnore_または@JsonProperty(access = Access.WRITE_ONLY)または_@JsonView_などのようなJacksonアノテーションを使用して対処できます。または、Jackson Annotationを使用して実行できない変換が必要なフィールドが1つまたは2つある場合は、それを処理するカスタムロジックを記述します(信じてください、このシナリオに遭遇したのは5年以上も一度もありません)休息サービスでの長い旅)

ドメインをDTOにコピーしないことによる実際の悪影響がないかどうかを知りたい

18
so-random-dude

ドメインオブジェクトを公開するだけの長所

  1. 記述するコードが少ないほど、発生するバグも少なくなります。
    • コードベースに広範な(議論可能な)テストケースがあるにもかかわらず、ドメインからDTOまたはその逆へのフィールドのコピーの欠落/誤ったコピーによるバグに遭遇しました。
  2. 保守性-ボイラープレートコードの削減。
    • もちろん、新しい属性を追加する必要がある場合は、ドメイン、DTO、マッパー、テストケースを追加する必要はありません。これはリフレクションbeanCopy utilsを使用して実現できるとは言わないでください。それは目的全体を無効にします。
    • Lombok、Groovy、Kotlinは知っていますが、ゲッターセッターの頭痛だけが軽減されます。
  3. DRY
  4. パフォーマンス
    • これは、「時期尚早なパフォーマンスの最適化がすべての悪の根源である」というカテゴリーに分類されることを知っています。ただし、これにより、リクエストごとに(少なくとも)オブジェクトを1つ以上(少なくともガベージコレクションで)作成する必要がないため、CPUサイクルがいくらか節約されます。

短所

  1. DTOを使用すると、長期的に柔軟性が向上します
    • もし私がその柔軟性を必要とするなら。少なくとも、これまでに出くわしたものは、httpを介したCRUD操作であり、@ JsonIgnoresをいくつか使用して管理できます。または、Jackson Annotationを使用して実行できない変換が必要なフィールドが1つまたは2つある場合は、前述のとおり、それを処理するカスタムロジックを記述できます。
  2. アノテーションでドメインオブジェクトが肥大化する。
    • これは正当な懸念事項です。永続的なフレームワークとしてJPAまたはMyBatisを使用すると、ドメインオブジェクトにこれらのアノテーションが含まれる可能性があります。その場合、Jacksonアノテーションも存在します。私の場合、これはあまり当てはまりませんが、私はSpringブートを使用しており、mybatis.configuration.map-underscore-to-camel-case: truespring.jackson.property-naming-strategy: SNAKE_CASEなどのアプリケーション全体のプロパティを使用することで回避できます

短編、少なくとも私の場合、短所は長所を上回らないので、新しいPOJOをDTOとして持つことによって自分自身を繰り返すことは意味がありません。コードが少ないほど、バグの可能性が低くなります。したがって、ドメインオブジェクトを公開し、個別の「ビュー」オブジェクトを持たないことを進めます。

免責事項:これは、ユースケースに適用できる場合と適用できない場合があります。この観察は私のユースケースごとです(基本的には15個のエンドポイントを持つCRUD API)

6
so-random-dude

私はDTOの使用に投票し、ここに理由があります。

  • 異なるリクエスト(イベント)とDBエンティティ。多くの場合、要求/応答がドメインモデルにあるものとは異なることがあります。特に、他のマイクロサービスからの多くのイベントが発生するマイクロサービスアーキテクチャでは理にかなっています。たとえば、Orderエンティティがありますが、別のマイクロサービスから取得するイベントはOrderItemAddedです。イベント(または要求)の半分がエンティティーと同じであっても、混乱を避けるために、それらすべてにDTOを設定することは理にかなっています。
  • DBスキーマと公開するAPIの間の結合。エンティティを使用する場合、基本的に特定のマイクロサービスでDBをモデル化する方法を公開します。 MySQLでは、おそらくエンティティに関係を持たせたいと思うでしょうが、それらは構成の点でかなり巨大になります。他のタイプのDBでは、多くの内部オブジェクトのないフラットエンティティが存在します。つまり、エンティティを使用してAPIを公開し、DBをMySQLからCassandra-に変更したい場合、APIも変更する必要があります。これは明らかに悪いことです。持ってる。
  • 消費者主導の契約 。おそらくこれは前の箇条書きに関連していますが、DTOを使用すると、マイクロサービスの進化中にマイクロサービス間の通信が中断されないようにすることが容易になります。コントラクトとDBは結合されていないため、これはテストが簡単です。
  • アグリゲーション。場合によっては、1つのDBエンティティの場合よりも多くを返す必要があります。この場合、DTOは単なるアグリゲーターになります。
  • パフォーマンス。マイクロサービスは、ネットワークを介して大量のデータを転送することを意味し、パフォーマンスの問題が発生する可能性があります。マイクロサービスのクライアントが必要とするデータがDBに保存するよりも少ない場合は、提供するデータを少なくする必要があります。繰り返しますが、DTOを作成するだけで、ネットワークの負荷が軽減されます。
  • LazyInitializationExceptionを忘れてください。DTOには、ORMによって管理されるドメインエンティティとは異なり、遅延読み込みとプロキシがありません。
  • DTOレイヤーは、適切なツールでサポートするのはそれほど難しくありません。通常、エンティティをDTOにマッピングするとき、および逆方向にマッピングするときに問題が発生します-正しいフィールドを設定する必要があります変換するたびに手動で。エンティティとDTOに新しいフィールドを追加するときにマッピングを設定することを忘れるのは簡単ですが、幸いにも、このタスクを実行できる多くのツールがあります。たとえば、私たちはプロジェクトにMapStructを使用していました-自動的に、コンパイル時に変換を生成できます
21
Danylo Zatorsky

次の理由により、CQRSを使用する場合の決定ははるかに簡単です。

  • 書き込み側では、すでにDTOであるCommandsを使用します。 Aggregates-ドメインレイヤーの豊富な動作オブジェクト-公開/照会されないため、問題はありません。
  • 読み取り側では、薄いレイヤーを使用しているため、永続性からフェッチされたオブジェクトはすでにDTOである必要があります。すべてのユースケースでreadmodelを使用できるため、マッピングの問題はありません。最悪の場合、GraphQLなどを使用して、必要なフィールドのみを選択できます。

読み取りと書き込みを分割しない場合、どちらのソリューションにもトレードオフがあるため、決定は難しくなります。

2