18

私は従来の DDD をあきらめていますが、これは多くの場合時間の無駄であり、無限のマッピングを行う必要があります: data layer <--> domain layer <--> presentation layer.

小さな変更であっても、データ モデル、ドメイン モデル、プレゼンテーション モデル / ビューモデル、リポジトリ、マネージャー / サービス クラス、そしてもちろん AutoMapper マップを変更し、すべてをテストする必要があります。各呼び出しでは、基になるコードを呼び出すレイヤーを呼び出すレイヤーを呼び出す必要があります。そして、「将来必要になるかもしれない」以外の見返りはありません。うーん。

私の現在のアプローチはより実用的です。

  • 「データ層」と「ドメイン層」の違いについてはもはや気にしません。意味がないからです。用語は交換可能です。EF に任せて、必要に応じてインターフェイスとリポジトリを追加します。
  • 「データ」プロジェクトと「ドメイン」プロジェクトを (「コア」という退屈な名前に) マージしましたが、Visual Studio の実行速度が実際に速くなったと断言できます。
  • EF エンティティがスタックを上下することを許可しますが、通常どおり、それらをプレゼンテーション モデル / ビューモデルにマッピングします。
  • 単純な操作の場合はコントローラーから直接リポジトリを呼び出し、複雑な操作の場合は通常どおりドメイン マネージャー/サービスを使用します。リポジトリが IQueryable を公開することはありません。
  • エンティティ/POCO を部分クラスとして定義しているため、対応する部分クラスにドメインの動作を個別に追加できます。

問題:あらゆる場所でエンティティを使用するようになったため、クライアント コードでエンティティのナビゲーション プロパティを確認できます。また、モデルはリポジトリを離れた後に常に具体化されるため、これらのナビゲーション プロパティは多くの場合 null です。

考えられる解決策:
1. それと一緒に暮らす。それは醜いですが、上で説明した問題よりはましです。
2. エンティティごとに、ナビゲーション プロパティを非表示にするインターフェイスを定義します。クライアント コードでインターフェイスを使用するようにします。しかし皮肉なことに、これは別のレイヤーを意味します (薄くて扱いやすいとはいえ)。
3. 他には?

私はこの種の高速で緩いプログラミング スタイルに慣れていないので、いくつかの明らかなトリックを見逃している可能性があります。他に考慮すべきことはありますか?すぐに遭遇する他の問題があると確信しています。

編集: この質問は DDD に関するものではありません。そして、多くの人が従来の DDD アプローチに苦労していることに注意してください -- Seemannは同じ結論に達したようです . Rahienは「抽象化アンチパターンのための役に立たない抽象化」について話し、 Evans 自身は、DDD が真に役立つのは 5% だけであると述べましたケース。このスレも参照. コメント/回答のいくつかは、私がどのように DDD を間違って実行しているか、またはシステムを微調整して正しく実行する方法について予想通りのものです。ただし、私は DDD について尋ねたり、DDD が適切な場合にそれを叩いたりしているわけではありません。むしろ、上記の考え方に沿って他の人が何をしているのかを知りたいのです。DDD がすべての設計上の問題に対する万能薬であるとは限りません。10 年ごとに新しいプロセスが登場します (RUP の誰か? XP、アジャイル、Booch など...)。DDD は、最も輝かしい新しいものであり、最もよく知られ、使用されています。しかし、時間通りに出荷され、保守が容易な販売可能な製品を構築しようとしているため、実用主義が最初に来る必要があります。私が学んだ最も有用なプログラミング公理、YAGNI です。私が望むのは、システムを一種の「DDD-lite」に変更することです。これにより、強力なデザイン/OOP/パターンの哲学が得られますが、脂肪はありません。

4

5 に答える 5

5

DDD を使用した一般的な永続化アプローチは、ドメイン モデルを対応するテーブルに直接マップすることです。技術的には、マッピングはまだ存在します (通常はコードで宣言されます) が、 lazyberezovskyが指摘したように、明示的なデータ モデルはありません。

ナビゲーション プロパティの問題は、DDD を使用しているかどうかに関係なく、いくつかの方法で解決できます。アプローチ 1 は好きではありません。なぜなら、コードを推論するのが難しくなるためです。どのプロパティが設定され、どのプロパティが設定されないかわからないからです。アプローチ 2 は理論的にははるかに優れています。なぜなら、特定のクエリに必要なものが非常に明確になり、物事を明示的にすることは一般的に良い方法だからです。同様の、しかしより単純で脆弱性の低いアプローチは、read-modelsを使用することです。これは、一連のクエリの特定のクエリの要件を満たすように設計された単なるオブジェクトです。DDD のコンテキスト内では、動作が豊富なエンティティをクエリから切り離すことができますが、これはしばしば対立します。今ではDRYの支持者異端を叫び、たいまつや熊手であなたにやってくるかもしれませんが、実際には、読み取りモデルとエンティティを維持する方がはるかに簡単で、インターフェイスや複雑なマッピング戦略を介してクエリ要件を満たすようにエンティティを強制しようとする方がはるかに簡単です。さらに、読み取りモデルと動作モデルの責任はまったく異なるため、DRY は適用されません。

これは、DDD があなたのシナリオに適用できると言っているわけではありません。特に大部分がCRUDであるシナリオでは、本格的な DDD を避けることが賢明な決定であることがよくあります。KISS と YAGNIの良い例です。DDD は、ドメインがデータだけでなく複雑な動作で構成されている場合にメリットを享受します。いずれにせよ、読み取りモデル パターンが適用されます。

アップデート

読み取りモデルを採用しない実装については、フェッチング戦略の概念によってデータベースから必要なものを正確に指定できるフェッチング戦略の設計を参照してください。これにより、ナビゲーション プロパティの問題が軽減されます。リンクされた投稿で参照されている資料も興味深いものです。全体として、これは他のアプローチに存在する間接的なレイヤーを回避しようとします。ただし、私の意見では、提案されたフェッチ戦略を使用することは、読み取りモデルを使用するよりも複雑ですが、最終的な結果は同じです。

于 2012-10-19T23:48:22.483 に答える
2

この点についてのいくつかの考え:

... リポジトリは決して IQueryable を公開しません ... モデルは、リポジトリを離れた後、常に実体化されます ...

あなたの質問は「asp.net-mvc」でタグ付けされているので、Web アプリケーションを念頭に置いています。すべてのリクエストの 90% 以上は、データベースからデータを取得し、それらのデータを Web ビューに表示することになっている GET リクエストになります。これらの必要なデータが、単なるプロパティのバッグ (エンティティ タイプのプロパティの選択、または複数のエンティティからのプロパティで構成される) ではなく、実際にエンティティである頻度はどれくらいですか?

たとえば、アプリケーションに 100 個のビューがあるとします。これらの少数のみが完全なエンティティを表示します:

  • そのうちの 50 は、選択されたデータを表示するリスト ビューです (顧客の ID と住所はありますが、顧客の担当者、電話番号、売上高はありません)。
  • それらの 20 には、参照を選択するためのオートコンプリート テキスト ボックスが含まれています (注文の顧客ですが、オートコンプリート リストには顧客の名前と市区町村のみが表示され、残りの住所、連絡担当者、電話番号、販売量は表示されず、最初の 5 ヒットが表示されます)
  • 1 は顧客の編集ビューで、すべてを表示しますが、売上高は表示しません
  • 1 は、過去 5 回の注文を含む顧客の詳細ビューです。
  • 1は、各アイテムの製品を含む注文アイテムを含む注文の詳細ビューですが、製品のサプライヤー名はありません
  • 1 は同じビューですが、過去 3 か月間の仕入先の平均リード タイムで、各品目および品目の製品の仕入先を確認したい購買部門に特化しています。
  • 1は、商品カテゴリ「修理サービス」の注文項目のみの注文を示すサービス部門のビューです。
  • 人事部の 1 つのビューには、大きな BLOB として保存された写真を含む従業員が表示されます
  • 人事計画部門の 1 つのビューには、写真なしの従業員の短いバージョンが表示されます
  • などなど

UI プログラマーとして、上記の例でビューをレンダリングするには、あらゆる種類のデータ要件があります。

  • プロパティの選択のみが必要です
  • ビューごとに同じエンティティのプロパティの異なる選択が必要です
  • すべてのアイテムを含む注文が必要ですが、製品への参照はありません
  • すべてのアイテム (ただし、アイテムのすべてのプロパティではない) と、製品およびサプライヤーへの参照 (ただし、すべてのサプライヤーのプロパティではない) を含む注文が必要です
  • 注文アイテムのフィルタリングされたリストのみを含む注文が必要です
  • これまでに受けた 3000 件の注文すべてではなく、最後の 5 件の注文を含む顧客が必要です
  • 従業員が必要ですが、大きな塊の画像なしでお願いします
  • などなど

データアクセス/リポジトリ/サービス開発者としてこれらの要件を満たすにはどうすればよいですか?

  • 私はほんの一握りのメソッドを提供し、エンティティを具現化します: オーダー ヘッダーのロード、アイテムを含むオーダー ヘッダーのロード、アイテムと製品を含むオーダー ヘッダーのロード、アイテムと製品とサプライヤーを含むオーダー ヘッダーのロード、顧客ヘッダーのロード (20 個のプロパティのうち 15 個を破棄) 、親愛なる UI 開発者、5 つのプロパティのみが必要な場合)、顧客ヘッダーを 3000 の注文すべてでロードする (親愛なる UI 開発者、5 つしか必要ない場合は 2995 を捨ててください)、などなど。ロードされたナビゲーション プロパティ。
  • UI が必要とするすべての詳細に気を配っています。 、 などのリポジトリ/サービス メソッドを作成しますGetFiveCustomerPropertiesForAutoCompleteGetCustomerWithLastFiveOrdersロードしていないプロパティ (スカラーも含む) を非表示にするリポジトリからインターフェイスを返します。または、要求されたプロパティを含む「DTO」を返します。UI 開発者が次のビューのデータ要件を呼び出すたびに、リポジトリ/サービスを変更し、新しい DTO を作成します。
  • リポジトリから戻りIQueryable<TEntity>、UI 開発者に「ビューに必要なデータを取得するために自分で LINQ クエリを作成する」ように伝えます。(翌朝、DBA は何百ものパフォーマンスの悪いデータベース クエリについて不満を漏らしています。)
  • たとえば、ユーザーのアクセス権に句を適用したり、検索用語に句を追加したり、クエリにオプションを適用したりするIQueryable<TEntity>などのセキュリティ上の問題をカバーするリポジトリ/サービスから「準備された」 s を返します。私は UI 開発者に次のように伝えています。WhereWhereNoTrackingSelectTakeSkipOrderBy) これら 3 つのクエリ部分を UI の問題と見なしているからです。他のすべてのクエリ要件 (フィルタリング、結合、グループ化など) は、リポジトリ/サービス レイヤーで実装する必要があり、UI レイヤーでは禁止されています。」中間マッピング層がなく、必要な列/プロパティよりも多くをロードするオーバーヘッドがありません。

これらはほんの一部の考えです。どのアプローチにも利点と欠点があります。少なくとも 1 人または数人の開発者がリポジトリ/サービスと UI/「プロジェクション」レイヤーの両方で何が起こっているかの概要を把握している小規模なチームで作業する私の経験では、最後のオプションはうまく機能しますが、常に機能するとは限りません。記述された厳密なルール (たとえば、注文に含まれる注文アイテムの製品カテゴリによるフィルターでWhereは、プロジェクション内、つまり UI レイヤーで句を適用する必要があります)。POST 要求とデータ変更には、ビューから収集されたデータをサービスに送り返してそこで処理する DTO を使用します。

「クエリレイヤー」とUIレイヤーをより厳密に分離するには、おそらく2番目のオプションに近いものを好むでしょう.UI要件ごとにインターフェイス/ DTOを使用するのではなく、最も一般的な要件のDTOのセットに何らかの方法で削減します(時々不必要にロードされるプロパティのわずかなオーバーヘッドの価格)。ただし、必要なリポジトリ/サービス メソッドの量が多くなり、(おそらく多くの) DTO の追加のメンテナンスが必要になり、DTO と ViewModel の間の中間マッピングが必要になるため、最後のオプションよりも多くの作業が必要になると思います。

個人的には、完全なエンティティ、特に複雑なオブジェクト グラフを 90% の確率で必要としないのに具体化することに関心があります。しかし、私の懸念は広範なパフォーマンス測定によって検証されていないため、このアプローチは、特別な高性能を必要としない「通常の」アプリケーションにとって実際には問題であることが証明されています。

于 2012-10-21T22:14:03.223 に答える
1

あなたが何を構築しているのかについての手がかりがない場合、誰があなたに適切なアドバイスを与えることができますか? 大まかに言えば、間違ったソリューションを構築している可能性があります (そうであるとは言いません)。したがって、私たちが関係できるのは、技術的な設計の問題と同様の過去の経験だけであることを認識してください.

実際、多くの人があなたの問題に直面しています。マッピングは、静的型付けの土地における疎結合税です。たぶん、より動的な言語があなたの痛みの一部を解決するかもしれません. あるいは、より多くの自動化 (DSL、MDA) に美徳を見出すかもしれません。代わりにクライアント サーバーに切り替えることもできます。

インターフェイスはレイヤーではなく、抽象化です。それらを賢く使用してください。

個人的に、私はこれらの近道をとることはありません。ステップをスキップしようとして何度も噛まれました。奇妙な場所でロジックがポップアップし始めます。単純なデータセットを開発するためのデータ駆動型アプリがある場合、EF も思い浮かびます。しかし、私はオブジェクトを DDD の意味で集合体またはエンティティーとは呼びません。単に ERD の意味でエンティティーと呼びます。部分的なメソッドの散布よりも、Transactionscript の方が適している可能性があります。読み取りモデル オブジェクトに関しては、これらは間接的なレイヤーではありません。

全体として、必要な形状 (null であるナビゲーション プロパティ) を明らかにしないオブジェクトへの依存関係を利用してマッピングの摩擦と戦うため、物事を混乱させているだけです。別の分野で問題を引き起こしています。

于 2012-10-21T21:23:40.120 に答える
0

問題: あらゆる場所でエンティティを使用するようになったため、クライアント コードでエンティティのナビゲーション プロパティを確認できます。

なぜこれが問題なのか、特に EF エンティティとどのように関連しているのかはよくわかりません。クライアント コードとは、プレゼンテーション層コードまたはエンティティを消費するコードを意味しますか?

UI コードの簡単な解決策は、これらのナビゲーション プロパティを公開しない (または、GUI が必要とするオブジェクト グラフの深さに応じて、それらのいくつかのみを公開する) ViewModel を定義することです。

他のコードでは、エンティティのナビゲーション プロパティを表示できるのは通常のことです。公開されているのには理由があります。乱用するとデメテルの法則を破ってしまう可能性がありますが、その罠に陥らないようにすることは開発者の規律の問題です。

エンティティには独自のコントラクトが含まれます。エンティティにアクセスできるすべてのコードは、このコントラクトの任意の部分を使用できるはずです。エンティティが露出しすぎていて、特定の部分へのアクセスを制限するためにそれらの上にインターフェイスを配置する必要があると感じた場合、それは単に別のエンティティである可能性があります。

  • 「データ層」と「ドメイン層」の違いについてはもはや気にしません。意味がないからです。用語は
    交換可能です。EF に任せて、必要に応じてインターフェイスと
    リポジトリを追加します。

  • 「データ」プロジェクトと「ドメイン」プロジェクトを (「コア」という退屈な名前に) マージしたところ、Visual Studio の実行速度が実際に速くなったと断言できます。
  • EF エンティティがスタックを上下できるようにしますが、通常どおりプレゼンテーション モデル / ビューモデルにマッピングします。
  • 単純な操作の場合はコントローラーから直接リポジトリを呼び出し、複雑な操作の場合は通常どおりドメイン マネージャー/サービスを使用します。リポジトリが IQueryable を公開することはありません。
  • エンティティ/POCO を部分クラスとして定義しているため、対応する部分クラスにドメインの動作を個別に追加できます。

データ/ドメインの分離を除いて、これらのどれも根本的にアンチ DDD ではないようです。

特に、データベース ファーストの EF -DDD を実行する場合は、明らかにドメイン中心のアプローチであり、エンティティを定義する前にテーブルを定義するべきではありません。また、一部のドメイン エンティティがデータベースまたは EF と直接通信するか (DDD ではなく、より一般的にはレイヤード アーキテクチャに準拠)、または間にデータ アクセス オブジェクトが体系的に存在するか (DDD 準拠) も明確ではありません。

于 2012-10-22T12:17:36.050 に答える
0

簡単に言うと、方法 2 を使用しました。つまり、クライアントで使用するインターフェイスのレイヤーを追加します。.tt テンプレートを少し調整するだけで、EF にそれらを生成させることができます。

はい、それは(まだ)別のレイヤーを作成しますが、ロジックがなく、複雑さを追加しません. もちろん、クライアントがエンティティを逆シリアル化する必要がある場合は、(まだ) 別のレイヤーを追加して、逆シリアル化を処理し、クライアントに返すエンティティ定義とインターフェイスの両方を参照する必要があります。しかし、それはまた薄いので、私たちはそれと一緒に暮らすことを学びました.それはうまく機能し、クライアントは本当にきれいなままです...

于 2012-10-20T21:31:21.917 に答える