Apakah itu praktik buruk yang repositori panggilan pengontrol alih-alih layanan?

44

Apakah itu praktik buruk yang repositori panggilan pengontrol alih-alih layanan?

untuk menjelaskan lebih lanjut:

Saya mengetahui bahwa dalam desain yang baik pengendali memanggil layanan dan layanan menggunakan repositori.

tapi kadang-kadang di controller saya tidak punya / butuh logika dan hanya perlu mengambil dari db dan meneruskannya untuk melihatnya.

dan saya bisa melakukannya hanya dengan menelepon repositori - tidak perlu memanggil layanan - apakah itu praktik yang buruk?

mohsenJsh
sumber
Bagaimana Anda memanggil layanan? Melalui antarmuka REST?
Robert Harvey
2
Saya lebih sering menggunakan pendekatan desain itu. Pengontrol saya (atau kelas komposer yang mendasarinya) akan meminta data dari atau mengirim data ke repo, dan kemudian meneruskannya ke kelas layanan apa pun yang perlu melakukan pemrosesan. Tidak ada alasan untuk menggabungkan kelas-kelas pemrosesan data dengan kelas-kelas pengambilan data / manajemen, mereka adalah keprihatinan yang berbeda meskipun saya tahu pendekatan tipikal adalah melakukannya dengan cara itu.
Jimmy Hoffa
3
Meh Jika ini adalah aplikasi kecil dan Anda hanya mencoba untuk mendapatkan data dari database, lapisan layanan adalah buang-buang waktu kecuali lapisan layanan itu merupakan bagian dari beberapa API publik seperti antarmuka REST. "Apakah susu baik untukmu atau buruk untukmu?" Tergantung pada apakah Anda toleran laktosa.
Robert Harvey
4
Tidak ada aturan yang keras dan cepat bahwa Anda harus memiliki Controller -> Service -> Repository structure over Controller -> Repository. Pilih pola yang tepat untuk aplikasi yang tepat. Apa yang akan saya katakan adalah Anda harus membuat aplikasi Anda konsisten.
NikolaiDante
Mungkin Anda bisa membuat layanan generik yang hanya meneruskan permintaan Anda ke repositori dan kemudian kembali. Ini bisa berguna untuk menjaga antarmuka yang seragam dan akan sederhana jika di masa depan Anda perlu menambahkan layanan nyata untuk melakukan sesuatu sebelum memanggil repositori.
Henrique Barcelos

Jawaban:

32

Tidak, pikirkan seperti ini: repositori adalah layanan (juga).

Jika entitas yang Anda ambil melalui repositori menangani sebagian besar logika bisnis, maka tidak perlu untuk layanan lain. Hanya memiliki repositori sudah cukup.

Bahkan jika Anda memiliki beberapa layanan yang harus Anda lewati untuk memanipulasi entitas Anda. Ambil entitas dari repositori terlebih dahulu dan kemudian berikan ke layanan tersebut. Mampu membuang HTTP 404 bahkan sebelum mencoba sangat mudah.

Juga untuk skenario baca, Anda hanya perlu entitas untuk memproyeksikannya ke DTO / ViewModel. Memiliki lapisan layanan di antaranya kemudian sering menghasilkan banyak metode yang agak jelek.

Joppe
sumber
2
Dikatakan dengan baik! Preferensi saya adalah memanggil repositori, dan hanya dalam beberapa kasus, maka repositori tidak cukup (yaitu dua entitas harus dimodifikasi dengan menggunakan repositori yang berbeda), saya membuat layanan yang bertanggung jawab atas operasi ini dan menyebutnya dari controller.
Zygimantas
Saya telah melihat kode yang agak rumit hanya untuk membenarkan penggunaan layanan. Masuk akal, paling tidak ...
Gi1ber7
Jadi repositori saya mengembalikan daftar 'objek bisnis' yang perlu saya konversi menjadi 'objek xml', apakah alasan itu cukup untuk memiliki lapisan layanan? Saya memanggil metode pada setiap objek untuk mengubahnya ke tipe lain dan menambahkan ke daftar baru.
bot_bot
Akses DAO langsung berbahaya di controller, itu bisa membuat Anda rentan terhadap suntikan SQL dan memberikan akses ke tindakan berbahaya seperti ,, deleteAll '' Saya pasti akan menghindarinya.
Anirudh
4

Ini bukan praktik yang buruk bagi pengontrol untuk memanggil repositori secara langsung. "Layanan" hanyalah alat lain, jadi gunakan di tempat yang masuk akal.

NikolaiDante berkomentar:

... Pilih pola yang tepat untuk aplikasi yang tepat. Apa yang akan saya katakan adalah Anda harus membuat aplikasi Anda konsisten.

Saya tidak berpikir konsistensi adalah aspek yang paling penting. Kelas "service" dimaksudkan untuk merangkum beberapa logika level yang lebih tinggi sehingga controller tidak perlu mengimplementasikannya. Jika tidak ada "logika tingkat tinggi" yang diperlukan untuk operasi yang diberikan, langsung saja ke repositori.

Untuk mempromosikan Kepedulian Terpisah yang baik dan kemampuan testabilitas, repositori haruslah merupakan ketergantungan yang Anda berikan ke layanan melalui konstruktor:

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

service.DoSomething(...);

Jika mencari catatan dalam database membutuhkan beberapa jenis permintaan parameter, sebuah kelas layanan mungkin merupakan tempat yang baik untuk mengambil dalam model tampilan Anda dan membangun kueri yang kemudian dijalankan oleh repositori.

Demikian pula, jika Anda memiliki model tampilan kompleks untuk formulir, kelas layanan dapat merangkum logika membuat, memperbarui, dan menghapus catatan dengan memanggil metode pada Model Domain / Entitas Anda, kemudian bertahan dengan menggunakan repositori.

Pergi ke arah yang berlawanan, jika controller Anda perlu mendapatkan catatan dengan ID-nya, kemudian mendelegasikan ke objek layanan untuk ini seperti memukul paku payung dengan palu godam - itu cara yang lebih dari yang Anda butuhkan.

Saya telah menemukan bahwa pengontrol berada pada posisi terbaik untuk menangani transaksi, atau objek Unit Kerja . Pengontrol atau objek Unit Kerja kemudian akan mendelegasikan ke objek layanan untuk operasi yang kompleks, atau langsung ke repositori untuk operasi sederhana (seperti menemukan catatan dengan 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);
            }
        }
    }
}

Saya pikir campuran layanan dan bekerja dengan repositori secara langsung dapat diterima. Anda dapat lebih lanjut merangkum transaksi dalam objek Unit Kerja jika Anda merasa perlu.

Rincian tanggung jawab seperti ini:

  • Pengontrol mengontrol aliran aplikasi
    • Mengembalikan "404 Tidak Ditemukan" jika keranjang belanja tidak ada dalam database
    • Merender ulang formulir dengan pesan validasi jika validasi gagal
    • Menghemat keranjang belanja jika semuanya memeriksa
  • Pengontrol mendelegasikan ke kelas layanan untuk mengeksekusi logika bisnis pada Model Domain Anda (atau Entitas). Objek layanan tidak boleh menerapkan logika bisnis! Mereka menjalankan logika bisnis.
  • Pengendali dapat mendelegasikan langsung ke repositori untuk operasi sederhana
  • Objek layanan mengambil data dalam model tampilan, dan mendelegasikan ke Model Domain untuk menjalankan logika bisnis (mis. Objek layanan memanggil metode pada Model Domain sebelum memanggil metode pada repositori)
  • Objek layanan mendelegasikan ke repositori untuk kegigihan data
  • Pengendali harus:
    1. Kelola masa pakai suatu transaksi, atau
    2. Buat objek Unit Kerja untuk mengelola masa transaksi
Greg Burghardt
sumber
1
-1 untuk menempatkan DbContext ke dalam pengontrol daripada repo. Repo dimaksudkan untuk mengelola penyedia data sehingga tidak ada orang lain yang harus dalam hal penyedia data berubah (dari MySQL ke file JSON datar misalnya, berubah di satu tempat)
Jimmy Hoffa
@ JimmyHoffa: Saya benar-benar melihat kembali kode yang saya tulis, dan jujur, saya membuat objek "konteks" untuk repositori saya, tidak perlu database. Saya pikir DbContextini adalah nama yang buruk dalam kasus ini. Saya akan mengubahnya. Saya menggunakan NHibernate, dan repositori (atau konteks jika berguna) mengelola akhir database, jadi mengubah mekanisme kegigihan tidak memerlukan perubahan kode di luar konteks.
Greg Burghardt
Anda tampaknya membingungkan pengontrol dengan repositori oleh penampilan kode Anda ... Maksud saya, "konteks" Anda semuanya salah dan harus sama sekali tidak ada dalam pengontrol.
Jimmy Hoffa
6
Saya tidak harus menjawab dan saya tidak yakin ini adalah pertanyaan yang bagus untuk memulai, tetapi saya memilih karena saya pikir pendekatan Anda adalah desain yang buruk. Tidak ada perasaan keras, saya hanya mencegah konteks yang dimiliki oleh pengontrol. IMO pengontrol tidak boleh memulai dan melakukan transaksi seperti ini. Itu pekerjaan dari sejumlah tempat lain, saya lebih suka controller untuk mendelegasikan semua yang tidak hanya memenuhi permintaan HTTP ke tempat lain.
Jimmy Hoffa
1
Sebuah repositori, biasanya bertanggung jawab atas semua informasi konteks data untuk memastikan bahwa tidak ada hal lain yang perlu diketahui tentang data selain dari apa yang perlu diketahui domain itu sendiri
Jimmy Hoffa
1

Tergantung pada arsitektur Anda. Saya menggunakan Spring, dan transaksionalitas selalu dikelola oleh layanan.

Jika Anda memanggil repositori langsung untuk operasi tulis (atau layanan sederhana tanpa logika yang hanya mendelegasikan ke repositori), mungkin Anda menggunakan beberapa transaksi basis data untuk operasi yang harus dilakukan dalam satu. Itu akan menyebabkan data tidak koheren dalam database Anda. Sebagai aturan umum, operasi basis data harus berhasil, atau gagal, tetapi operasi setengah kerja adalah penyebab sakit kepala.

Untuk alasan itu saya berpikir bahwa memanggil repositori langsung dari pengontrol, atau menggunakan layanan pendelegasian sederhana, adalah praktik yang buruk. Anda mulai melakukannya hanya untuk membaca, dan segera Anda, atau satu atau teman Anda akan mulai melakukannya untuk operasi menulis.

Rober2D2
sumber