コントローラがサービスの代わりにリポジトリを呼び出すのは悪い習慣ですか?


43

コントローラがサービスの代わりにリポジトリを呼び出すのは悪い習慣ですか?

さらに説明する:

優れた設計では、コントローラーがサービスを呼び出し、サービスがリポジトリを使用することがわかります。

ただし、コントローラーにはロジックがない場合や必要ない場合があり、dbからフェッチしてビューに渡すだけでよい場合があります。

そして、私はリポジトリを呼び出すだけでそれを行うことができます-サービスを呼び出す必要はありません-それは悪い習慣ですか?


どのようにサービスを呼び出していますか?RESTインターフェース経由?
ロバートハーベイ

2
私はその設計アプローチをかなり一般的に使用しています。私のコントローラー(または基礎となるコンポーザークラス)は、レポからデータを要求するか、レポにデータを送信し、処理を行う必要があるサービスクラスに渡します。データ処理クラスとデータ取得/管理クラスを組み合わせる理由はありません。それらは異なる関心事ですが、典​​型的なアプローチはそのようにすることです。
ジミー・ホッファ

3
えー それが小さなアプリケーションであり、データベースからデータを取得しようとしている場合、サービス層がRESTインターフェイスなどのパブリックAPIの一部でない限り、サービス層は時間の無駄です。「牛乳はあなたにとって良いですか、それとも悪いですか?」乳糖不耐症かどうかによります。
ロバートハーヴェイ

4
コントローラー->リポジトリーの上にコントローラー->サービス->リポジトリー構造が必要であるという厳格なルールはありません。適切なアプリケーションに適切なパターンを選択してください。私が言うことは、アプリケーションの一貫性を保つ必要があるということです。
ニコライダンテ

たぶん、リクエストをリポジトリに転送してから戻るだけの汎用サービスを思いつくかもしれません。これは、統一されたインターフェイスを維持するのに役立ち、将来、リポジトリを呼び出す前に何かを行うために実際のサービスを追加する必要がある場合に簡単になります。
エンリケバルセロス

回答:


32

いいえ、このように考えてください:リポジトリ(また)サービスです。

リポジトリから取得したエンティティがほとんどのビジネスロジックを処理する場合、他のサービスは必要ありません。リポジトリがあれば十分です。

エンティティを操作するためにパススルーする必要があるサービスがある場合でも。最初にリポジトリからエンティティを取得してから、そのサービスに渡します。試みる前にHTTP 404を投げることができるのは非常に便利です。

また、読み取りシナリオの場合、エンティティをDTO / ViewModelに投影する必要があるのが一般的です。その間にサービスレイヤーを配置すると、多くの場合パススルーメソッドがかなりlotいものになります。


2
よく言った!私の好みはリポジトリを呼び出すことであり、場合にのみ、リポジトリは十分ではありません(つまり、異なるリポジトリを使用して2つのエンティティを変更する必要があります)。この操作を担当するサービスを作成し、コントローラから呼び出します。
ジギマンタス

サービスの使用を正当化するためだけに、かなり複雑なコードに気付きました。不条理、少なくとも...
Gi1ber7

私のリポジトリは、「XMLオブジェクト」に変換する必要がある「ビジネスオブジェクト」のリストを返します。その理由は、サービスレイヤーを持つのに十分ですか?各オブジェクトでメソッドを呼び出して別の型に変換し、新しいリストに追加しています。
bot_bot

直接DAOアクセスはコントローラーでは危険です。SQLインジェクションの影響を受けやすくなり、、、 deleteAllなどの危険なアクションにアクセスできます」
Anirudh

4

コントローラがリポジトリを直接呼び出すことは悪い習慣ではありません。「サービス」は単なるツールであるため、意味のある場所で使用してください。

NikolaiDanteはコメントしました:

...適切なアプリケーションに適切なパターンを選択します。私が言うことは、アプリケーションの一貫性を保つ必要があるということです。

一貫性が最も重要な側面だとは思いません。「サービス」クラスは、コントローラがそれを実装する必要がないように、より高いレベルのロジックをカプセル化することを目的としています。特定の操作に「上位レベルのロジック」が必要ない場合は、リポジトリに直接移動してください。

適切な懸念とテスト容易性を促進するために、リポジトリはコンストラクタを介してサービスに注入する依存関係である必要があります。

IFooRepository repository = new FooRepository();
FooService service = new FooService(repository);

service.DoSomething(...);

データベース内のレコードの検索に何らかのパラメータ化されたクエリが必要な場合、サービスクラスはビューモデルを取得し、リポジトリによって実行されるクエリを構築するのに適した場所になる可能性があります。

同様に、フォームに複雑なビューモデルがある場合、サービスクラスは、ドメインモデル/エンティティのメソッドを呼び出し、リポジトリを使用してそれらを永続化することにより、レコードの作成、更新、削除のロジックをカプセル化できます。

コントローラがそのIDでレコードを取得する必要がある場合、反対方向に進むと、このためにサービスオブジェクトに委任することは、ハンマーで画thumbを打つようなものです-それは必要以上の方法です。

コントローラがトランザクションまたは作業単位オブジェクトを処理するのに最適な位置にあることがわかりました。コントローラーまたは作業単位オブジェクトは、複雑な操作の場合はサービスオブジェクトに委任するか、単純な操作の場合はリポジトリに直接移動します(IDによるレコードの検索など)。

public class ShoppingCartsController : Controller
{
    [HttpPost]
    public ActionResult Edit(int id, ShoppingCartForm model)
    {
        // Controller initiates a database session and transaction
        using (IStoreContext store = new StoreContext())
        {
            // Controller goes directly to a repository to find a record by Id
            ShoppingCart cart = store.ShoppingCarts.Find(id);

            // Controller creates the service, and passes the repository and/or
            // the current transaction
            ShoppingCartService service = new ShoppingCartService(store.ShoppingCarts);

            if (cart == null)
                return HttpNotFound();

            if (ModelState.IsValid)
            {
                // Controller delegates to a service object to manipulate the
                // Domain Model (ShoppingCart)
                service.UpdateShoppingCart(model, cart);

                // Controller decides to commit changes
                store.SaveChanges();

                return RedirectToAction("Index", "Home");
            }
            else
            {
                return View(model);
            }
        }
    }
}

サービスを組み合わせてリポジトリを直接操作することは完全に受け入れられると思います。必要に応じて、トランザクションを作業単位オブジェクトにさらにカプセル化できます。

責任の内訳は次のようになります。

  • コントローラーはアプリケーションのフローを制御します
    • ショッピングカートがデータベースにない場合、「404 Not Found」を返します
    • 検証が失敗した場合、検証メッセージでフォームを再レンダリングします
    • すべてがチェックアウトした場合、ショッピングカートを保存します
  • コントローラはサービスクラスに委任して、ドメインモデル(またはエンティティ)でビジネスロジックを実行します。サービスオブジェクトはビジネスロジックを実装すべきではありませんビジネスロジックを実行します。
  • コントローラは、簡単な操作のためにリポジトリに直接委任することができます
  • サービスオブジェクトは、ビューモデルのデータを取得し、ドメインモデルに委任してビジネスロジックを実行します(たとえば、サービスオブジェクトは、リポジトリのメソッドを呼び出す前にドメインモデルのメソッドを呼び出します)
  • サービスオブジェクトは、データの永続性のためにリポジトリに委任します
  • コントローラーは次のいずれかを行う必要があります。
    1. トランザクションの存続期間を管理する、または
    2. トランザクションの存続期間を管理する作業単位オブジェクトを作成する

1
DbContextをレポではなくコントローラーに配置する場合は-1。レポは、誰もがイベントデータプロバイダ(のMySQLからインスタンス用のフラットJSONファイルに、一箇所の変化)の変化にしていないので、データプロバイダを管理するためのものです
ジミー・ホッファ

@JimmyHoffa:実際に書いたコードを振り返ってみると、正直なところ、データベースではなく、リポジトリ用の「コンテキスト」オブジェクトを作成しています。DbContextこの場合、悪い名前だと思います。それを変更します。私はNHibernateを使用し、リポジトリ(または便利な場合はコンテキスト)がデータベースの終わりを管理するため、永続化メカニズムを変更しても、コンテキスト外でコードを変更する必要はありません。
グレッグブルクハート

あなたはあなたのコードの見た目でコントローラをリポジトリと混同しているように見えます...私が意味するのは、あなたの「コンテキスト」はすべて間違っており、絶対にコントローラに存在すべきではありません。
ジミー・ホッファ

6
答える必要はありませんし、これが最初から良い質問かどうかはわかりませんが、あなたのアプローチは悪い設計だと思うので、私は反対票を投じます。難しい感情はありません。コントローラーが所有するコンテキストを落胆させるだけです。IMOコントローラーは、このようなトランザクションを開始およびコミットしないでください。それは他の多くの場所の仕事です、私はコントローラーが単にHTTPリクエストを他のどこかに満たしていないすべてを委任することを好みます。
ジミー・ホッファ

1
、それは通常、ドメイン自体が知っているに必要なもの以外のデータの問題について何を知っている何もないニーズを確実にするために、すべてのデータのコンテキスト情報を担当するリポジトリだ
ジミー・ホッファ

1

それはあなたのアーキテクチャに依存します。私はSpringを使用しており、トランザクション性は常にサービスによって管理されています。

書き込み操作(または単にリポジトリに委任するロジックのない単純なサービス)のためにリポジトリを直接呼び出す場合、1つの操作で実行する必要がある操作に複数のデータベーストランザクションを使用している可能性があります。それはあなたのデータベースに一貫性のないデータをもたらすでしょう。一般的なルールとして、データベース操作は機能するか、または失敗するはずですが、半分動作する操作が頭痛の原因です。

そのため、コントローラーからリポジトリーを直接呼び出したり、単純な委任サービスを使用したりするのは悪い習慣だと思います。あなたはただ読むためにそれを始め、すぐにあなた、またはあなたの仲間が書き込み操作のためにそれを始めます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.