4

コンテキスト: 多数の列が含まれる複雑なデータベース モデルがある .NET プラットフォームでスマート クライアント アプリケーションを構築する。自然なアプリケーション スタイルは、典型的なデータ ドリブン CRUD です。場合によってはかなりの量のサーバー側のロジックと、やや複雑な検証もあります。クライアントとサーバーを完全に制御できるため、相互運用性の必要性は最小限です。


この質問には多くの詳細がありますが、それは申し訳ありませんが、回答に適切なコンテキストを設定したいからです。


いくつかの前提条件
- Microsoft の世界では珍しくありませんが、以前のほとんどのアプリケーションは DataSets を使用して作成されているため、関係する開発者にとって最もよく知られているテクノロジです。しかし、開発者が OO の考え方にも精通しているとしましょう。
- クライアントとサーバーの両方で検証を実行する必要があります。
- ほとんどのデータを表形式で表示していません。
- これはイントラネット アプリケーションではないため、帯域幅についてあまり多くを想定することはできません


最大の問題: データセットですか、それともオブジェクトですか?


データセットを使用する場合、いくつかの長所と短所があります。長所
としては、データベースからデータを取得する、ネットワークを介してデータを取得する、ネットワークを介して変更されたデータを返すという点で、Microsoft のサポートが少し得られます。小さいチャンク – 変更の送信のみを指定できるため。かなりの量のデータが含まれる可能性があるため、送信するデータが少ないのは良いことです。
- マイナス面は次のとおりです。検証、ビジネス ロジックなどに関して、手続き型のコードが得られ、オブジェクト指向コードの利点が得られません。動作とデータが一緒になって、より自然な作業スタイルと思考スタイルが得られます。あなたがしていること、そしておそらく検証ロジックとの密接な関係。また、データセットをグリッドに配置する利点から目を背けることもできます。これは一般的なユース ケースではないためです。

オブジェクトを選択する場合も同じドリルですが、より多くのオプションが含まれます
。 良い点 : 動作とデータが一緒に。検証ロジックを近づけます。オブジェクト間の関係が見やすく、理解しやすくなります。より読みやすいコード。単体テストが容易になります。しかし、かなりの数の選択肢と実行する必要がある作業もあります。


OR/マッピング
- リレーショナル モデルからオブジェクトへのデータの取得。OR マッパーはそれほど複雑ではなく、うまく処理できます。しかし、それは開発時間に追加されます。


コントラクト マッピング
- サーバー側オブジェクトからコントラクト オブジェクト (DTO の可能性が高い) にデータをマップすることは、一般的に良い方法です。これは CRUD スタイルのアーキテクチャに適したアプリケーションであるため、DTO は画像にあまり価値を追加せず、作業をマッピングするだけです。


共有コード
- ドメイン データとロジックを含むアセンブリがクライアント側とサーバー側の両方で利用できる、共有コード シナリオに進むことができます。これは密結合ですが、クライアント サーバー アプリが自然に密結合している場合は、必ずしも悪いことではありません。


コントラクト レイヤーを追加するかどうかに関係なく、ワイヤ経由で送信する必要がある大きなオブジェクト構造があります。クライアントとサーバーの両方を制御しているため、トランスポートとエンコーディングは TCP を介したバイナリ エンコーディングである必要があります。それは役に立ちます。データセットでは、変更のみを送り返すオプションがあります。オブジェクト構造全体を前後に送信すると、パフォーマンスの問題が発生する可能性があります。オブジェクト構造全体を送信するオプションは、関連する変更 (作成、更新、削除) を何らかの方法で識別し、それに関する情報のみを送信することです。理論的には、集約ルート ID と変更内容をサーバーに送信し、サーバーに集約ルートを遅延ロードして変更を実行し、再度保存するように依頼することはそれほど難しくありません。しかし、これに伴う大きな複雑さは、行われた変更を特定することです。このアプローチを採用することはありますか? なんで?どのように正確にそれを行いますか?

プレゼンテーション
正確な UI テクノロジは、質問にとってそれほど重要ではありません。WinForms、Silverlight、または WPF が可能です。新しいスマート クライアントであるため、WPF を使用していると仮定しましょう。つまり、双方向バインディングがあり、MVVM を適切に使用できます。

ユーザー インターフェイスにバインドされたオブジェクトは、INotifyPropertyChanged を実装し、プロパティが更新されるたびにイベントを発生させる必要があります。これをどのように解決しますか?共有コードのシナリオに進む場合は、それをドメイン オブジェクトに追加できますが、サーバー側で使用することを意図していないコードとロジックをサーバー側に追加する必要があります。コントラクト オブジェクトを使用すると、分離はより自然になりますが、マッピングのレイヤーを追加するだけでは、多くの付加価値は得られません。

テクノロジ
一部の問題の解決に役立つテクノロジはいくつかありますが、他の問題を複雑にすることがよくあります。それらを使用しますか、それともゼロから自分で構築しますか?
**
- CSLA は可能ですが、単体テストが難しくなり、データ アクセスへの結合がより緊密になるようです。それは多くの問題に役立ちますが、個人的に私はこの技術に精通していません。
- WCF RIA サービスは、Silverlight ソリューションで可能ですが、明らかに制限があります。データのサイズは 1 です。
- WCF Data Services は、何かをすばやく起動するための別のアプローチですが、REST はあまり役に立ちません。また、RIA Services にある検証サポートも不足しています。

まとめ
ここまで読んでいただければ、私がこれからどこへ向かうのか、お分かりいただけると思います。一度にすべてを話すことを避けるために範囲を絞ろうとしましたが、分散開発は複雑であるため、多くの部分を考慮する必要があります。


アップデート

返信ありがとうございます!私は、さまざまな回答を受け入れるのに十分なオープンな質問をしようとしていましたが、珍しいことではないいくつかの要件に対処するのに十分具体的でした.

さまざまな長所と短所があり、システムごとに異なるさまざまな考慮事項があります。通常、それぞれが解決策を見つけることの複雑さを増します。この質問のポイントの 1 つは、タスク ベースの UI を使用して、今日では正しい答えであることが多い 1 つの答えに必ずしも直接適合しないいくつかの追加の要件を特に指定して、答えを得ることでした。もしそうなら、私は「CRUD-guy」ではありません。しかし、いくつかのシステムは、さまざまな理由 (ほとんどの場合レガシー) から、CRUD に適しています。

多くのビジネス アプリには、さまざまな方向に向かう同様の要求があります。

ビジネス関連
- 表示: ユーザーにデータを表示し、同じデータを更新する (読み取りと CUD - 作成、更新、削除)
- 検証: ビジネス ルール

UI 関連
- 検証: UI ルール
- UI 更新: オブジェクトの変更時に UI を更新するためのコード (INotifyPropertyChanged)

ネットワーク関連
- データ サイズ: ネットワーク経由で送信するデータの量

DB 関連
- 遅延読み込み

SRP/再利用関連
- マッピング: オブジェクトの複数のレイヤーによって引き起こされる/関心の分離

メンテナンス/変更関連
- 変更: 新しい情報 (列/フィールド) の追加
- コードの量
- 再利用と「変更する理由」

技術的な制限
- 変更の追跡

しかし、これらは非常に具体的なものにすぎません。どの「-ilities」が最も重要であるかを常に把握する必要があります。したがって、スケーラビリティ、可用性、拡張性、相互運用性、ユーザビリティ、保守性、およびテスト容易性がどの程度必要かを知る必要があります。

ほとんどの状況で何かを一般化しようとすると、次のようになります。

クライアント
- 分離とテスト容易性のために MVVM を使用します -
DTO の上に VM を作成します - VM に
INotifyPropertyChanged を実装します。
- XamlPowerToys、Postsharp、またはこれを支援するその他の手段を使用する価値があります
- UI で読み取りと CUD を分離し
ます - CUD をタスクベースにし、コマンドなどを使用してそれらの操作をサーバー側に送信します

サーバー
- 画面ごとに dto を調整する -または Ayende がhttp://msdn.microsoft.com/en-us/magazine/ff796225.aspx
で説明しているマルチクエリ アプローチを使用するあなたが解決しようとしている問題とは完全に無関係なステップです。そのマッピングは - ドメインモデルを、読み取りではなく、CUD 関連の操作を含む主にビジネス操作に関係させます - 変更する理由の数を増やす再利用性を避けます -カプセル化の問題を回避する - (それにより、CQRS スタイルのアーキテクチャを有効にし、読み取りと CUD のスケーリングを時間内に分離する可能性があります )





http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/02/15/validation-in-a-ddd-world.aspx )

これは必然的に、この特定の状況で私がとるアプローチですか?

ええと、それが私が議論を始めたかったことです:)しかし、それは私が望んでいたよりも難しかったようです(あなた方2人を除いて)。

4

2 に答える 2

8

私は私たち自身の経験からしか答えることができません。さまざまなフレームワーク(WCF RIA、Ideblade)を試しましたが、フレームワークは事態を悪化させるだけであると結論付けました。さらに下で説明します。

まず第一に、CRUDについて忘れておく必要があります。CRUDを備えているのはデモアプリのみです。実際のアプリには動作があります。

クライアント側でエンティティグラフ全体を模倣することはお勧めしません。これらは2つの関心の分離です。

コンテキストごとにカスタマイズされたDtoを作成する必要があります。たとえば、OrderSearchViewがあるとします。次に、OrderSearchDtoを作成し、必要なフィールドのみをマップします。EditOrderViewでは、代わりにEditOrderDtoを使用します。これには必要なフィールドのみが含まれます。

エンティティとdtoの間で自動マッピングツールを使用することはお勧めしません。多くの場合、dtoとエンティティの間に1対1の関係がないためです。dtoは、多くの場合、さまざまな複数のバックエンドエンティティによって構築されます。とにかくマッピングはとても簡単なので、マッピングフレームワークでは要点がわかりません。そして、仕事はマッピングではありません-それはユニットテストを書いています-とにかく(マッピングフレームワークの有無にかかわらず)あなたがしなければならないでしょう。

Dtosは、クライアント側のテクノロジーにとらわれないようにする必要があります。また、dtoにINotifyPropertyChangedを実装すると、単一責任の原則が破られます。それらはデータ転送オブジェクトと呼ばれる領域があります。代わりに、クライアント側でプレゼンターを作成します。EditOrderDtoのラッパーであるEditOrderPresenterを作成します。したがって、dtoはEditOrderPresenter内のプライベートメンバーフィールドになります。Presenterは、クライアントレイヤーで編集できるように調整されているため、通常はINotifyPropertyChangedを実装します。EditOrderPresenterは通常、dtoと同じプロパティ名を持ちます。

クライアント検証をサーバー側のエンティティ検証から物理的に分離する必要があります。共有に注意してください!クライアントの検証は、GUIのエクスペリエンスを向上させるためのGUIの調整にすぎないと思います。dtoとentityの間で検証コードを共有することを大事にしないでください。有用性よりも頭痛の種になる可能性があります。クライアント側で行われる検証の種類に関係なく、常にサーバー側で検証するようにしてください。検証には、単純なプロパティ検証とエンティティ全体の検証の2種類があります(dtoについても同じです)。エンティティの検証は、状態遷移時にのみ実行する必要があります。背景知識については、JimmyNilssonsドメイン駆動設計を確認してください。検証ルールエンジンの使用はお勧めしません。状態パターンを使用するだけです。

では、更新、挿入、削除についてはどうでしょうか。この実装では、WCFを使用し、WCFAPIには次の1つのメソッドしかありません。IResponse[]Process(params IRequest [] requests); これは本当にどういう意味ですか?これは、クライアントがサーバーにリクエストのバッチを発行していることを意味します。サーバーサイドでは、システムで定義されているすべてのリクエストに対してRequestHandlerを実装します。次に、応答のリストを返します。Process()メソッドが1つの作業単位(〜1つのトランザクション)であることを確認してください。これは、バッチ内の1つのリクエストが失敗した場合(すべてが失敗した場合)、トランザクションのロールバックが発生し、データベースに害が及ばないことを意味します。(responsehandlersでエラーコードを使用しないでください-代わりに例外をキャストしてください。)

Agathaメッセージングサーバーを覗いてみることをお勧めします。Davy Brionには、メッセージングレイヤーに関するすばらしいブログ投稿があります。私たちの会社では、独自のメッセージングサーバーを実装することを選択しました。Agathaが提供するものすべてを必要としなかったため、構文を改善しました。とにかく、メッセージングサーバーの実装はそれほど難しくありません-そしてそれは素晴らしい学習体験です。リンクhttp://davybrion.com/blog/

次に、Dtoをどうしますか。まあ、あなたはそれらを更新することは決してありませんが、GUIへの適切なフィードバックを得るためにクライアント側でそれらを変更します。したがって、プレゼンターにdto(要求)に発生しているすべてのことを正しい順序で追跡させることができます。これがrequestBatchになります。次に、requestbatchをWCFのprocess-commandに送信します。そうすると、要求はサーバー側で「再生」され、requesthandlersによって処理されます。これは、実際には、dtoを更新しないことを意味します。ただし、プレゼンターは、適切なGUIフィードバックを提供するために、クライアント側でdtoを編集する場合があります。プレゼンターの仕事は、行われたすべての編集を追跡して、それらをリクエストバッチとしてサーバーに発行することでもあります(編集されたのと同じ順序でrequetを使用します)。次のシナリオを考えてみてください。既存の注文を取得し、編集し、次に、変更をデータベースにコミットします。これにより、2つのバッチが作成されます。1つは注文を取得するためのもので、もう1つは変更をコミットするためのものです。
RequestBatch 1:GetOrderByIdRequest

(..その後、ユーザーがデータを編集します。.)

ReqeuestBatch 2:
StartEditOrderRequest、modusを編集するための状態変更、緩和された検証
AddConsigneeToOrderRequest
ChangeEarliestETDOnOrderRequest、最新のETDに対する検証の必要はまだありません!
DeleteOrderlineRequest
ChangeNumberOfUnitsOnOrderlineRequest
EndEditOrderRequest、状態-元の状態に変更し、ここでエンティティの検証を実行します!
GetOrderByIdRequest、最新の変更でGUIを更新するため。

サーブサイドではNHibernateを使用します。Nhibernateは第1レベルのキャッシュを使用して、データベースの負荷を回避します。したがって、同じ作業単位(requestbatch)内のすべてのリクエストはキャッシュを使用します。

各リクエストには、ジョブを実行するための最小限のデータのみを含める必要があります。これは、dto全体ではなく、OrderId+他のいくつかのプロパティを使用することを意味します。楽観的な更新に関しては、リクエストと一緒にoldValuesの一部を送信できます。これは同時実行セットと呼ばれます。並行性セットには通常、多くのフィールドが含まれていないことに注意してください。その間に変更された順序を更新することは、必ずしも競合状態になることを意味するわけではないためです。例えば。その間に荷受人が別のユーザーによって編集されている間に注文ラインを追加しても、レイズ状態にあるとは限りません。

まあ、これは非常に多くの作業につながるのではありません。確かにもっと多くのクラスがありますが、各クラスは小さく、単一の責任があります。

ところで、中規模のプロジェクトでWCFRIAサービスを試しました。そして、それはうまくいきませんでした。フレームワークの周りで、やりたいことを実行する方法(ハック)を見つける必要がありました。また、コード生成に基づいています。これは、ビルドサーバーにとっては非常に悪いことです。また、レイヤーを介して可視性を作成しないでください。クライアント層に影響を与えることなく、バックアップされたエンティティを変更できるはずです。RIAではこれは非常に困難です。ODataはWCFRIAと同じカテゴリに分類されると思います。

クライアント側でクエリを作成する必要がある場合は、仕様パターンを使用します(iqueryableは使用しないでください)。そうすると、バックエンドエンティティから独立します。

幸運を。
ツイッター:@lroal

于 2010-09-30T10:55:55.700 に答える
2

興味深い問題:)

いくつかの原則から始める場合:

  • 有線で送信されるデータの量を減らすようにしてください
  • 配管コードの記述に費やす時間を最小限に抑えるようにしてください
  • 妥当性を改善してみてください

それに基づいて私は:

  • POCOオブジェクトを使用してデータを転送します。データセットには、必要のない多くの情報が含まれています
  • データベースアクセスにEntityFrameworkPOCOを使用すると、コントラクトオブジェクトからデータオブジェクトへのマッピングが節約されます
  • 検証をヘルパークラスに配置し、テストが簡単で、共有コードモデルをサポートします

私たちのプロジェクトでは、EnterpriseLibraryやDataSetsと比較してEntityFrameworkを使用して時間を節約しました。

サーバー側とクライアント側のオブジェクトでは、次のことを試すことができます。

  • クライアント側のオブジェクトはサーバー側のオブジェクトを継承し、INotifyPropertyChangedを実装します
  • クライアント側とサーバー側のオブジェクトを別々のdllに配置します。これにより、サーバーに未使用のコードがなくなります。
  • Automapperを使用して、2つのタイプ間をマッピングします。(インターフェースを使用する方が良い方法かもしれません)
于 2010-09-29T16:28:09.720 に答える