web-dev-qa-db-ja.com

サービスレイヤーはMVCアプリケーションのビューモデルを返す必要がありますか?

ASP.NET MVCプロジェクトがあり、asp.netサイトのこのコンタクトマネージャーチュートリアルなどのサービスレイヤーを使用しているとします。 http://www.asp.net/mvc/tutorials/iteration- 4-make-the-application-loosely-coupled-cs

ビューのビューモデルがある場合、サービスレイヤーは各ビューモデルを提供する適切な場所ですか?たとえば、サービスレイヤーのコードサンプルにはメソッドがあります

    public IEnumerable<Contact> ListContacts()
    {
        return _repository.ListContacts();
    }

代わりにIEnumerableが必要な場合は、サービスレイヤーに配置する必要がありますか、それとも「正しい」場所が他にありますか?

おそらくより適切なのは、ContactControllerに関連付けられたビューごとに個別のビューモデルがある場合、ContactManagerServiceに各ビューモデルを返すための個別のメソッドが必要ですか?サービスレイヤーが適切な場所でない場合、ビューモデルオブジェクトはコントローラーで使用するためにどこで初期化する必要がありますか?

62
erg39

一般的には違います。

ビューモデルは、ビューとの間で情報を提供することを目的としており、一般的なドメインではなく、アプリケーションに固有である必要があります。コントローラーは、リポジトリ、サービス(ここではサービスの定義をいくつか想定しています)などとの相互作用を調整し、ビューモデルの構築と検証を処理し、レンダリングするビューを決定するロジックも含める必要があります。

ビューモデルを「サービス」レイヤーにリークすることで、レイヤーがぼやけ、ドメインレベルの責任に焦点を当てるべきものと、特定のアプリケーションおよびプレゼンテーションが混在するようになります。

48
captaintom

いいえ、そうは思いません。サービスは、問題のあるドメインのみを考慮し、結果を表示するビューは考慮しません。戻り値は、ビューではなく、ドメインオブジェクトで表現する必要があります。

25
duffymo

従来のアプローチまたは理論的には、ViewModelはユーザーインターフェイスレイヤーの一部である必要があります。少なくとも名前はそう言っています。

しかし、Entity Framework、MVC、Repositoryなどを使用して自分で実装する場合、他のことに気づきます。

誰かがエンティティ/ DBモデルをViewModels(最後に言及されたDTO)にマップする必要があります。これは[A] UIレイヤーで(コントローラーによって)、または[B]サービスレイヤーで行う必要がありますか?

私はオプションBを使用します。オプションAは、いくつかのエンティティモデルが組み合わされてViewModelを形成するという単純な事実があるため、違います。不要なデータをUIレイヤーに渡さない場合がありますが、オプションBでは、サービスはデータを再生し、必要な/最小値のみを(ViewModelへの)マッピング後にUIレイヤーに渡すことができます。

ここでも、オプションAで、ViewModelをUIレイヤー(およびエンティティモデルはサービスレイヤー)に配置します。

サービスレイヤーがViewModelにマップする必要がある場合、サービスレイヤーはUIレイヤーのViewModelにアクセスする必要があります。どのライブラリ/プロジェクトですか? ViewmodelはUIレイヤーの別のプロジェクトにある必要があり、このプロジェクトはサービスレイヤーによって参照される必要があります。 ViewModelが別のプロジェクトにない場合、循環参照が存在するため、実行できません。サービスレイヤーがUIレイヤーにアクセスするのは面倒ですが、それでも対処できます。

しかし、このサービスを使用する別のUIアプリがある場合はどうなりますか?モバイルアプリがある場合はどうなりますか? ViewModelはどのように違うのですか?サービスは同じビューモデルプロジェクトにアクセスする必要がありますか?すべてのUIプロジェクトは同じViewModelプロジェクトにアクセスしますか、それとも独自のプロジェクトを持っていますか?

これらの考慮事項の後、私の答えはViewmodelプロジェクトをサービスレイヤーに配置することです。とにかく、すべてのUIレイヤーはサービスレイヤーにアクセスする必要があります。そして、それらすべてが使用できる同様のViewModelが多数存在する可能性があります(したがって、サービスレイヤーのマッピングが容易になります)。最近、マッピングはlinqを介して行われますが、これはもう1つの利点です。

最後に、DTOに関するこの議論があります。また、ViewModelのデータ注釈についても説明します。データアノテーション(Microsoft.Web.Mvc.DataAnnotations.dll)を含むViewModelは、サービスレイヤーに配置できません。UIレイヤーに配置されます(ただし、ComponentModel.DataAnnotations.dllはサービスレイヤーに配置できます)。すべてのプロジェクトが1つのソリューション(.sln)にある場合、どのレイヤーを配置してもかまいません。エンタープライズアプリケーションでは、各レイヤーに独自のソリューションがあります。

したがって、DTOは実際にはViewModelです。これは、ほとんどの場合、2つの間に1対1のマッピングがあるためです(たとえば、AutoMapperを使用)。繰り返しますが、DTOにはUI(または複数のアプリケーション)に必要なロジックがあり、サービスレイヤーに存在します。また、UIレイヤーのViewModel(Microsoft.Web.Mvc.DataAnnotations.dllを使用する場合)は、DTOからデータをコピーするだけで、いくつかの「動作」/属性が追加されています。

[ここで、このディスカッションは興味深い方向に進んでいきます...:I]

また、データアノテーション属性がUI専用であるとは考えないでください。 System.ComponentModel.DataAnnotations.dllを使用して検証を制限すると、同じViewModelをフロントエンドおよびバックエンドの検証にも使用できます(したがって、UI-residing-ViewModel-copy-of-DTOが削除されます)。さらに、属性はエンティティモデルでも使用できます。例:.ttを使用して、Entity Frameworkデータモデルを検証属性で自動生成し、バックエンドに送信する前にmax-lengthなどのいくつかのDB検証を実行できます。別の利点は、バックエンドの検証がDBで変更された場合、.tt(DBの詳細を読み取り、エンティティークラスの属性を作成する)が自動的にそれを取得することです。これにより、UI検証ユニットテストも強制的に失敗する可能性があります。これは大きなプラスです(そのため、誤って忘れたり失敗したりするのではなく、修正してすべてのUI /コンシューマーに通知できます)。はい、議論は良いフレームワーク設計に向かって動いています。ご覧のとおり、すべてが関連しています。層ごとの検証、単体テスト戦略、キャッシング戦略などです。

質問とは直接関係ありませんが。これで言及されている「ViewModelFaçade」は必見です channel 9 link も検討する価値があります。それは正確にビデオの11分49秒で始まります。上記の現在の質問が整理されると、これは次のステップ/考えになるため、「ViewModelを整理する方法は?」

また、例では、「_ repository.ListContacts()」はリポジトリからViewModelを返しています。これは成熟した方法ではありません。リポジトリは、エンティティモデルまたはDBモデルを提供する必要があります。これはビューモデルに変換され、サービスレイヤーによって返されるのはこのビューモデルです。

21
Blue Clouds

それはあなたが「サービス」をどのように考えているかに依存すると思います。単一のクラスのコンテキストでは、serviceという用語を本当に気に入ったことはありません。それは信じられないほどあいまいで、クラスの実際の目的についてはあまり教えてくれません。

「サービス層」がWebサービスなどの物理層である場合、絶対にそうではありません。 SOAコンテキスト内のサービスは、データやプレゼンテーションロジックではなく、ドメイン/ビジネスオペレーションを公開する必要があります。ただし、serviceがさらなるレベルの抽象的な概念として使用されている場合カプセル化の場合、私はあなたが望むようにそれを使用することに問題はないと思います。

概念を混同しないでください。サービスがビューモデルを扱う場合、それはプレゼンテーションサービスであり、actualモデルの上に階層化され、データベースやビジネスロジックに直接触れないでください。

5
Aaronaught

これは、私が作業する場所に応じて、少し「それが依存する」ことになります。通常、コントローラーはいくつかのサービスを消費します。次に、返されたDTOを「ViewModel」に結合し、クライアントに渡されます-JSON結果を介して、またはRazorテンプレートにバインドされています。

ことは、時間の約80%-DTOからViewModelへのマッピングは1-1です。 「必要な場所でDTOを直接使用するだけですが、DTOとクライアント/ビューで必要なものが一致しない場合、ViewModelを作成し、必要に応じてオブジェクト間のマッピングを行います」に向けて動き始めています。

これが最良または適切な解決策であるとはまだ確信していませんが、結局、「ビューのニーズを満たすためにXをDTOに追加するだけですか?」といういくつかの白熱した議論につながるためです。

5
e82