14

データ中心のアプローチによるSpringMVCでのクリーンなアーキテクチャの維持

作業中の新しいJavaベースのWebアプリ(ポータルタイプのアプリケーション)のフロントエンドのアーキテクチャを計画しようとしています。私はこれを初日から正しく理解したいと思っています。ここで議論を開始して、ボブおじさんのクリーンアーキテクチャを建築設計に実装するのに役立てたいと思います。

これが私たちの技術スタックの簡単な要約です(技術は重要ではなく、構造は重要です):

  • Oracleデータベース
  • WSDLを使用してサービスを公開するOracleServiceBus
  • JAX-WSはWSDLからJavaクラスを生成しました(これを「生成されたサービス層」と呼びましょう)
  • 生成されたデータオブジェクトにマップされたPOJOで構成されるドメインモジュール
  • 「生成されたサービスレイヤー」をフロントエンドアプリケーションに公開するコンシューマーモジュール
  • FreeMarkerを使用してビューをレンダリングするSpringMVCベースのフロントエンドモジュール

キーポイント:

特に、外側の円で宣言されたものの名前は、内側の円のコードで言及してはなりません。これには、関数、クラスが含まれます。変数、またはその他の名前付きソフトウェアエンティティ。

ボブのクリーンアーキテクチャに固執しようとして、アプリケーションロジック、つまり彼のアーキテクチャの「ユースケース」レイヤーをどこに配置するかについて、私は自分自身と少し行き来しました。

これが私が思いついたアプローチです:

レイヤー1-エンティティ

エンティティは、エンタープライズ全体のビジネスルールをカプセル化します。

これは、ドメインオブジェクトを含むドメインモジュールが存在する場所です。これらは、相互依存が最小限の自己完結型オブジェクトです。オブジェクト自体に関連するロジックのみがこれらのドメインオブジェクトに存在でき、ユースケース固有のロジックは存在しません。

データベースへのアクセスは、JPAやHibernateのようなORMとは対照的に、データを変換するサービスバスを使用してWSDLを介して公開されます。このため、従来の意味での「エンティティ」(IDを使用)はありませんが、このレイヤーをデータアクセスレイヤーにするデータ中心のアプローチで、コンシューマーモジュールによってアプリケーションの残りの部分に提示されます。

レイヤー2-ユースケース

この層のソフトウェアには、アプリケーション固有のビジネスルールが含まれています。

これは、アプリケーションのユースケースに固有のロジックが存在する場所です。この層への変更は、データアクセス層(層1)に影響を与えないはずです。GUIまたはフレームワークの実装(Spring MVC)を変更しても、このレイヤーには影響しません。

ここで少し 注意が必要です。データアクセス層(層1)はアプリケーションロジックをクリーンに保つ必要があるため、ユースケースに適した方法でその層の使用を容易にする層が必要です。この問題に対して私が見つけた解決策の1つは、MVC-VMと呼ぶことを選択した「 MVVMパターン」のバリアントを使用することです。説明については、以下を参照してください。この「VM」の一部は、このユースケース固有のロジックをカプセル化するクラスで表されるこのユースケースレイヤーに存在します。*ViewModel

レイヤー3-インターフェースアダプター

このレイヤーのソフトウェアは、データをユースケースやエンティティに最も便利な形式から、データベースやWebなどの外部機関に最も便利な形式に変換する一連のアダプターです。

これは、GUIのMVCアーキテクチャ(「MVC-VM」の「MVC」)が存在する場所です。基本的に、これは、Controller-classesが-classesからデータを取得し、*ViewModelそれをModelMapビューのFreeMarker-templatesによって直接使用されるSpringMVCのオブジェクトに配置する場合です。

私の見方では、サービスバスもこの層に分類されます。

レイヤー4-フレームワークとドライバー

通常、このレイヤーには、内側の次のサークルと通信するグルーコード以外のコードはあまり記述しません。

このレイヤーは、実際にはアプリケーションの構成レイヤー、つまりSpring構成にすぎません。これは、たとえば、FreeMarkerを使用してビューをレンダリングすることを指定する場所です。


モデルビューViewModelパターン

MVVMは、グラフィカルユーザーインターフェイス(マークアップ言語またはGUIコードとして)の開発を、モデル(ビューと区別するためのデータモデルとも呼ばれる)と呼ばれるビジネスロジックまたはバックエンドロジックの開発から明確に分離することを容易にします。モデル)。MVVMのビューモデルは値コンバーターです。つまり、ビューモデルは、モデルからのデータオブジェクトを簡単に管理および使用できるように、それらのオブジェクトを公開する役割を果たします。

ウィキペディアのMVVMパターンの詳細。

MVC-VMの役割は、次のようにアプリケーションで実行されます。

  • モデル-ModelMapビューテンプレートで使用されるSpringMVCのデータ構造で単純に表されます。
  • 表示 -FreeMarkerテンプレート
  • Controller SpringのController-HTTPURLリクエストを特定のハンドラー(およびFrontControllerなどの関数)に送信するクラス。これらのクラスのハンドラーは、データを表示するときにユースケースレイヤーからデータをフェッチしてビューテンプレートにプッシュし(HTTP GET)、保存するためにデータを送信します(HTTP POST)。このように、モデルを使用して、基本的にViewModelとViewの間のバインダーとして機能します。

  • ViewModel-これらのクラスは、1)ビューで使用できる方法でデータアクセス層からのデータを構造化し、2)ビューからのデータ入力を処理する役割を果たします。「処理」とは、データを検証して分解し、データをスタックに送信して保存できるようにすることを意味します。このレイヤーは、<UseCase>VMSpringMVCviewmodelフロントエンドモジュールのパッケージ内のクラスとして形成されます。

ModelMapここでの重要なコンポーネントは、SpringMVCでとFreeMarker-templatesの間で発生する暗黙的なバインディングです。テンプレートModelMapは、コントローラーが使用可能な形式でデータを配置したモデル(s)のみを使用します。そうすれば、次のようなテンプレートを作成できます。

<body>
  <h1>Welcome ${user}!</h1>
  <p>Our latest product:
  <a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>

冗長な説明をお詫びしますが、この(比較的単純な)アーキテクチャをより少ない言葉で説明することはできませんでした。

ここでの私のアプローチに関するいくつかの意見をいただければ幸いです-私は正しい方向に進んでいますか?MVC-VMのことは意味がありますか?クリーンアーキテクチャの原則に違反していますか?

もちろんこれには多くの解決策がありますが、私は1)過剰に設計されておらず、2)ボブのクリーンアーキテクチャの原則に準拠している解決策を見つけようとしています。


アップデート:

ここで私を思いとどまらせる重要な問題は、このアプリケーションで「ユースケース」レイヤーがどのような形をとるかということだと思います。データアクセス層からデータを取得するMVCフロントエンドがあることを忘れないでください。MVCパーツがBobの「インターフェイスアダプター」に適合し、データレイヤーのドメインモデルがBobの「エンティティ」レイヤーに適合する場合、アプリケーションロジックを実装するユースケースクラスを何と呼びますか?私はそれらを<UseCase>Modelsと呼んで、MVCプロジェクトに入れたいと思っていますが、Bobによると

モデルは、コントローラーからユースケースに渡され、ユースケースからプレゼンターとビューに戻される単なるデータ構造である可能性があります。

つまり、モデルオブジェクトは「ダム」(Springの単純なMapModelMapのように)である必要があり、ユースケースクラスのデータをこのMap構造に配置するのはコントローラーの責任です。

繰り返しになりますが、私のユースケースクラスはどのような形式を取りますか?どう<UseCase>Interactorですか?

しかし、結論として、MVC-MVのものは過剰に設計されている(または単に正しくない)ことに気付きます。「mikalai」は、この下に、現在の形式では本質的に2層のアプリケーションを示しています。データアクセス層とフロントエンドMVC層。そのような単純な。

4

3 に答える 3

14

おっと、それはたくさんでした。そして、あなたは主にボブおじさんの専門用語をSpringJavaアプリに翻訳したと思います。

建築は主に意見であり、あなたの質問は一種の質問であるため...

建築には多くの異なるスタイルがあり、...ほとんどが過大評価されています。ほとんどが同じものであるためです。間接参照と抽象化による、より高い凝集性より緩い結合です。

最も重要なのは(IMHO)依存関係です。1つの巨大なモノリシックプロジェクトではなく、多くの小さなプロジェクトを作成することが、「クリーンな」アーキテクチャを実現するための最良の方法です。

クリーンなアーキテクチャのための最も重要なテクノロジーは、「SpringMVC」テクノロジーや「Freemarker」テンプレート言語、またはボックス、六角形、その他のさまざまな抽象的なポリゴンの図を含む別のDr.Dobbの記事ではありません。

ビルドおよび依存関係管理テクノロジーに焦点を合わせます。これは、このテクノロジーがアーキテクチャルールを適用するためです。

また、コードのテストが難しい場合は、アーキテクチャが不良である可能性があります。

コードを簡単にテストし、多くのテストを作成できるようにすることに重点を置きます。

そうすれば、心配することなくコードを簡単に変更できます...アーキテクチャを変更することもできます:)

Bull#%$ @#アーキテクチャルールに焦点を合わせすぎないように注意してください。真剣に:あなたのコードがテストしやすく、変更しやすく、理解しやすく、うまく機能するなら、あなたは良いアーキテクチャを持っています。これを行うための6週間から6パックの腹筋の記事はありません(ボブおじさんごめんなさい)。経験と時間がかかります...魔法の弾丸計画はありません。

だからここに私自身の「クリーンな」アーキテクチャ...私はガイドラインを意味します:

  • 多くの小さなプロジェクトを作る
  • 依存関係管理を使用する(つまり、Maven、Gradle)
  • 常にリファクタリング
  • ある種の依存性注入を理解して使用する(Spring)
  • 単体テストを書く
  • 横断的関心事を理解する(つまり、AspectJ、メタプログラミングなどが必要な場合)
于 2012-10-15T04:17:22.380 に答える
9

私の解決策

したがって、Java / Spring MVCでのBobの「クリーンアーキテクチャ」の実装は境界線上重要であり、私が最初に含めたよりも多くのアーキテクチャファセットを必要とすることがわかりました。
そして、私は実際にオンラインで実装の例を見つけることができませんでした。

明らかに、このロジックはSpring MVC Webモジュールに存在してはならない(「*ViewModel」とは呼ばれない)ため、私のアーキテクチャには「ユースケース」レイヤー用の個別のモジュールがありませんでした。Web / MVCモジュールは単にアプリケーションの詳細であり、アプリケーションロジックはそれから完全に分離され、個別にテスト可能である必要があります。

この新しい「ユースケース」モジュールには*Interactor、ドメインモジュール(エンティティ)からデータを取得するクラスが含まれるようになりました。さらに、MVC / Webモジュールとユースケースモジュール間の通信を容易にするために、「要求/応答オブジェクト」が必要です。

私の依存関係チェーンは次のようになります。

SpringMVCモジュール->ユースケースモジュール->ドメインモジュール

ここで、すべての矢印(依存関係)は境界として形成されます。つまり、インターフェースは、必要に応じて実装され、必要に応じて注入される矢印の右側のモジュールで定義されます(制御の反転)。

これが私が(ユースケースごとに)最終的に作成したインターフェースです:

I<UseCase>Request-MVCモジュールに実装され、コントローラーにインスタンス化されます I<UseCase>Response-ユースケースモジュールに I<UseCase>Interactor実装され、Interactorにインスタンス化されます-UseCaseモジュールに実装され、コントローラーに注入されます I<UseCase>Consumer-ドメインモジュールに実装され、Interactorに注入されます

使い方?
ControllerHTTPリクエストからパラメータを取得し、それをにパックして、RequestModelに渡しInteractorます。Interactorドメインモジュールから必要なデータをフェッチし、アプリケーション固有の*Consumerロジックを適用してから、に入れて、ResponseModelに送り返しますController。次にController、最後に、この(現在はGUIに適した)単純なデータをMapオブジェクトに配置し、FreeMarkerテンプレートに転送します。このテンプレートは、このデータを直接使用してHTMLをレンダリングします。

APresenterはそこで最後の部分に関与し、これをModel-View-Presenterパターンの実装にすることができますが、今はそれを残しておきます。

私の結論

厳密に言えば、開発の初期段階で必要なファイルよりも多くのファイルが作成されました。ただし、アプリケーションの複雑さとサイズが大きくなるにつれて、この構造により、低結合度と高凝集度を簡単に維持できると確信しています。また、Webモジュールは簡単に交換できるようになりました。ユースケースモジュールにリクエストを配信し、応答オブジェクトを受信するだけです。さらに、アプリケーションの各レイヤー(ドメインロジック、アプリケーションロジック、およびGUIロジック)は個別にテスト可能であり、テストするためにWebサーバーを必要とするのはビュー部分のみです。

ここで受け取ったすべてのアドバイスとポインタに感謝します。そして、私の解決策についてコメントしてください-私はそれが完璧であるとは主張していません。

于 2012-10-17T18:51:10.267 に答える
6

このため、従来の意味での「エンティティ」(IDを使用)はありませんが、このレイヤーをデータアクセスレイヤーにするデータ中心のアプローチで、コンシューマーモジュールによってアプリケーションの残りの部分に提示されます。

その部分で何かが奇妙に思えます。WebサービスからIDを取得しても、エンティティにIDを設定できないのはなぜですか?

クリーンアーキテクチャアプローチでは、エンティティレイヤーは正確にはデータアクセスレイヤーではありません。データアクセスは、中心的な関心事ではなく、アーキテクチャの詳細である必要があります。あなた自身が言ったように、エンティティにはドメイン固有のビジネスルールが含まれています。ビジネスルール、つまり動作は、データを取得する方法とは大きく異なります。

エンティティは、すべてのドメインロジックが発生する場所であり、からデータを取得する場所ではありません。Clean Architectureによると、ゲートウェイから永続化されたデータまたは外部データを取得します。

この問題に対して私が見つけた解決策の1つは、MVC-VMと呼ぶことを選択した「MVVMパターン」のバリアントを使用することです。説明については、以下を参照してください。「VM」-この一部は、このユースケース固有のロジックをカプセル化する*ViewModelクラスで表されるこのユースケースレイヤーに存在します。

ViewModelプレゼンテーションアーティファクトであるビューを明確に参照します-別の詳細。ユースケース/インタラクターには、そのような詳細が欠けている必要があります。代わりに、Interactorsは、境界を介して配信メカニズムに依存しないデータ構造(RequestModelsおよびResponseModels)を送受信する必要があります。

これはあなたのカスタムパターンであり、プレゼンテーションフレームワークへの参照を含まないことを理解していますが、「表示」という言葉は誤解を招くだけです。

于 2012-10-15T16:05:02.013 に答える