web-dev-qa-db-ja.com

ベストプラクティス-多層アーキテクチャとDTO

ここにstackoverflowに関するQ/Aのいくつかを読んだ後、私はまだ私のWebアプリケーションでのDTOの正しい実装について混乱しています。私の現在の実装は(Java EEベースの)多層アーキテクチャ(永続性、サービス、プレゼンテーション層を含む)ですが、(とりわけ)ドメインオブジェクトを含むすべての層で使用される「共通」パッケージを備えています。この場合、レイヤーは実際には独立していると見なすことはできません。共通のパッケージを段階的に削除することを計画していますが、さまざまな課題/質問に直面しています。

  • 永続層がクラスmyproject.persistence.domain.UserEntity(JPAベースのエンティティ)を使用して、データベースとの間でデータを格納およびロードするとします。ビューにデータを表示するには、別のクラスを提供しますmyproject.service.domain.User。どこで変換しますか?ユーザー向けのサービスは、2つのクラス間で変換する責任がありますか?これは本当にカップリングを改善するのに役立ちますか?
  • serクラスはどのように見えるべきですか?不変であるためにゲッターのみを含める必要がありますか?ビューが既存のユーザーを編集するのは面倒ではないでしょうか(新しいserを作成し、既存のserオブジェクトのゲッターを使用するなど)。
  • 同じDTOクラス(ser)を使用して、既存のユーザーを変更したり、新しいユーザーを作成したりするリクエストをサービスに送信する必要がありますか、それとも他のクラスを実装する必要がありますか?
  • myproject.service.domainのすべてのDTOを使用することにより、プレゼンテーション層はサービス層に大きく依存しませんか?
  • 私自身の例外を処理する方法は?私の現在のアプローチでは、ほとんどの「重大な」例外がプレゼンテーション層によって処理されるまで再スローされます(通常、それらはログに記録され、ユーザーに問題が発生したことが通知されます)。一方で、私は再び共有パッケージを持っているという問題を抱えています。一方で、これが「ベストプラクティス」と見なされるかどうかはまだわかりません。何か案は?

ご回答ありがとうございます。

16
nils

異なるレイヤー間にいくつかのパッケージがあることは珍しいことではありませんが、通常はロギングなどの横断的関心事のためにのみ行われます。モデルを異なるレイヤーで共有しないでください。共有しないと、モデルを変更するには、それらすべてのレイヤーを変更する必要があります。通常、モデルはデータレイヤーに近い下位レイヤーです(アプローチに応じて、上、下、または絡み合っています)。

データ転送オブジェクトは、その名前が示すように、データを転送するために使用される単純なクラスです。そのため、通常、レイヤー間の通信に使用されます。特に、オブジェクトではなくメッセージを介して通信するSOAアーキテクチャーがある場合。DTOは、情報を転送する目的でのみ存在するため、不変である必要があります。 、変更しないでください。

ドメインオブジェクトは1つであり、DTOは別のものであり、プレゼンテーション層で必要なオブジェクトはさらに別のものです。ただし、小規模なプロジェクトでは、これらのさまざまなセットをすべて実装し、それらの間で変換する価値がない場合があります。それはあなたの要件に依存します。

Webアプリケーションを設計していますが、「Webアプリケーションをデスクトップアプリケーションに切り替えることはできますか?サービスレイヤーは本当にプレゼンテーションロジックを認識していませんか?」と自問するのに役立つ場合があります。これらの用語で考えることは、より良いアーキテクチャに向けてあなたを導きます。

あなたの質問に:

永続層がクラスmyproject.persistence.domain.UserEntity(JPAベースのエンティティ)を使用して、データベースとの間でデータを格納およびロードするとします。ビューにデータを表示するには、別のクラスmyproject.service.domain.Userを提供します。どこで変換しますか?ユーザー向けのサービスは、2つのクラス間で変換する責任がありますか?これは本当にカップリングを改善するのに役立ちますか?

サービスレイヤーは、そのクラス(DTO)とその下のレイヤー(永続性など)を認識しています。そうです、サービスは永続性とそれ自体の間の変換を担当します。

Userクラスはどのように見えるべきですか?不変であるためにゲッターのみを含める必要がありますか?ビューが既存のユーザーを編集する(新しいユーザーを作成する、既存のユーザーオブジェクトのゲッターを使用するなど)のは面倒ではないでしょうか?

DTOの背後にある考え方は、転送にのみ使用するため、新しいユーザーの作成などの操作は必要ありません。そのためには、さまざまなオブジェクトが必要です。

同じDTOクラス(ユーザー)を使用して、既存のユーザーを変更したり、新しいユーザーを作成したりするリクエストをサービスに送信する必要がありますか、それとも他のクラスを実装する必要がありますか?

サービスメソッドは操作を表現する場合があり、DTOはデータのみを含むパラメーターです。別のオプションは、操作を表し、DTOも含むコマンドを使用することです。これはSOAアーキテクチャで人気があり、サービスは単なるコマンドプロセッサである可能性があります。たとえば、パラメータとしてExecuteインターフェイスを使用する単一のICommand操作があります(対照的にコマンドごとに1つの操作を行うこと)。

Myproject.service.domain内のすべてのDTOを使用することにより、プレゼンテーション層はサービス層に大きく依存しませんか?

はい、サービスレイヤー上のレイヤーはそれに依存します。それがアイデアです。利点は、そのレイヤーのみがそれに依存し、上位または下位レイヤーがないため、変更はそのレイヤーにのみ影響することです(すべてのレイヤーのドメインクラスを使用する場合とは異なります)。

私自身の例外を処理する方法は?私の現在のアプローチでは、ほとんどの「重大な」例外がプレゼンテーション層によって処理されるまで再スローされます(通常、それらはログに記録され、ユーザーに問題が発生したことが通知されます)。一方で、私は再び共有パッケージを持っているという問題を抱えています。一方で、これが「ベストプラクティス」と見なされるかどうかはまだわかりません。何か案は?

各レイヤーには独自の例外があります。それらは、あるレイヤーから別のレイヤーに流れ、次の種類の例外にカプセル化されます。場合によっては、何かを実行する1つのレイヤー(ロギングなど)によって処理され、上位レイヤーが処理する必要のある別の例外をスローすることがあります。また、それらが処理され、問題が解決される場合もあります。たとえば、データベースへの接続に問題があると考えてください。例外がスローされます。あなたはそれを処理し、1秒後に再試行することを決定することができます、そして多分それから成功があるでしょう、それで例外は上向きに流れません。再試行も失敗した場合、例外は再スローされ、プレゼンテーション層まで流れて、ユーザーに適切に通知し、層を再試行するように依頼する場合があります。

17
jnovo

疎結合は確かに推奨される方法です。つまり、ビジネスロジックでコンバーターを維持するのは、膨大で退屈で、苦労することになります。はい、それらはビジネスロジック、つまりDAOとビューの間のレイヤーに属しています。したがって、ビジネス層はDAODTOとビューDTOの両方に依存することになります。そして、実際のビジネスロジックの見方を薄めるコンバータークラスでいっぱいになります...

不変のビューDTOを持つことで逃げることができれば、それは素晴らしいことです。ただし、シリアル化に使用するライブラリでは、セッターが必要になる場合があります。または、セッターがあれば、作成が簡単になる場合があります。

ビューとDAOの両方に同じDTOクラスを使用することで、うまくいきました。悪いことですが、正直なところ、最も重要な部分であるビジネスロジックはとにかくすべてに依存しなければならないので、システムが他の方法でより分離されているとは感じませんでした。この緊密な結合により、非常に簡潔になり、ビューレイヤーとDAOレイヤーの同期が容易になりました。コンポジションを使用することで、一方のレイヤーだけに固有のものを、もう一方のレイヤーには表示されないようにすることができました。

最後に、例外について。特別なDTOフィールドを使用している場合でも、例外を使用している場合でも、内側のレイヤーから伝播されたエラーをキャッチするのは、最外層であるビューレイヤー(Springを使用している場合はコントローラー)の責任です。次に、この最外層は、クライアントにエラーを通知するかどうか、およびその方法を決定する必要があります。実際には、最内層に至るまで、最外層が処理する必要のあるさまざまなタイプのエラーを区別する必要があります。たとえば、DAOレイヤーで何かが発生し、ビューレイヤーが400または500を返すかどうかを知る必要がある場合、DAOレイヤーは、どちらを使用するかを決定するために必要な情報をビューレイヤーに提供する必要があり、この情報が必要になります。独自のエラーとエラータイプを追加できる必要があるすべての中間レベルを通過します。 IOExceptionまたはSQLExceptionを最外層に伝播するだけでは不十分です。内層は、これが予期されるエラーであるかどうかを外層にも通知する必要があります。悲しいですが本当。

3
yotsov