Perbedaan antara Repositori dan Lapisan Layanan?

191

Dalam Pola Desain OOP, apa perbedaan antara Pola Repositori dan Lapisan Layanan?

Saya sedang mengerjakan aplikasi ASP.NET MVC 3, dan saya mencoba memahami pola desain ini, tetapi otak saya belum mengerti ... belum !!

Sam
sumber

Jawaban:

330

Lapisan Repositori memberi Anda tingkat abstraksi tambahan atas akses data. Alih-alih menulis

var context = new DatabaseContext();
return CreateObjectQuery<Type>().Where(t => t.ID == param).First();

untuk mendapatkan satu item dari database, Anda menggunakan antarmuka repositori

public interface IRepository<T>
{
    IQueryable<T> List();
    bool Create(T item);
    bool Delete(int id);
    T Get(int id);
    bool SaveChanges();
}

dan telepon Get(id). Lapisan repositori memperlihatkan operasi CRUD dasar .

Lapisan layanan memperlihatkan logika bisnis, yang menggunakan repositori. Contoh layanan dapat terlihat seperti:

public interface IUserService
{
    User GetByUserName(string userName);
    string GetUserNameByEmail(string email);
    bool EditBasicUserData(User user);
    User GetUserByID(int id);
    bool DeleteUser(int id);
    IQueryable<User> ListUsers();
    bool ChangePassword(string userName, string newPassword);
    bool SendPasswordReminder(string userName);
    bool RegisterNewUser(RegisterNewUserModel model);
}

Sementara List()metode repositori mengembalikan semua pengguna, ListUsers()IUserService hanya bisa mengembalikan yang lain, pengguna memiliki akses ke.

Dalam ASP.NET MVC + EF + SQL SERVER, saya memiliki alur komunikasi ini:

Tampilan <- Pengontrol -> Lapisan layanan -> Lapisan repositori -> EF -> SQL Server

Lapisan layanan -> Lapisan repositori -> EF Bagian ini beroperasi pada model.

Tampilan <- Pengontrol -> Lapisan layanan Bagian ini beroperasi pada model tampilan.

EDIT:

Contoh aliran untuk / Pesanan / ByClient / 5 (kami ingin melihat pesanan untuk klien tertentu):

public class OrderController
{
    private IOrderService _orderService;

    public OrderController(IOrderService orderService)
    {
        _orderService = orderService; // injected by IOC container
    }

    public ActionResult ByClient(int id)
    {
        var model = _orderService.GetByClient(id);
        return View(model); 
    }
}

Ini adalah antarmuka untuk layanan pesanan:

public interface IOrderService
{
    OrdersByClientViewModel GetByClient(int id);
}

Antarmuka ini mengembalikan model tampilan:

public class OrdersByClientViewModel
{
     CientViewModel Client { get; set; } //instead of ClientView, in simple project EF Client class could be used
     IEnumerable<OrderViewModel> Orders { get; set; }
}

Ini adalah implementasi antarmuka. Ini menggunakan kelas model dan repositori untuk membuat model tampilan:

public class OrderService : IOrderService
{
     IRepository<Client> _clientRepository;
     public OrderService(IRepository<Client> clientRepository)
     {
         _clientRepository = clientRepository; //injected
     }

     public OrdersByClientViewModel GetByClient(int id)
     {
         return _clientRepository.Get(id).Select(c => 
             new OrdersByClientViewModel 
             {
                 Cient = new ClientViewModel { ...init with values from c...}
                 Orders = c.Orders.Select(o => new OrderViewModel { ...init with values from o...}     
             }
         );
     }
}
LukLed
sumber
2
@ Sam Striano: Seperti yang Anda lihat di atas, IRepository saya mengembalikan IQueryable. Ini memungkinkan penambahan kondisi tambahan dan penundaan eksekusi di lapisan layanan, tidak lebih baru. Ya, saya menggunakan satu perakitan, tetapi semua kelas ini ditempatkan di ruang nama yang berbeda. Tidak ada alasan untuk membuat banyak majelis dalam proyek-proyek kecil. Pemisahan ruang nama dan folder berfungsi dengan baik.
LukLed
81
Mengapa mengembalikan model tampilan dalam layanan? bukankah layanan harus ditiru jika Anda memiliki banyak klien (seluler / web)? Jika itu yang terjadi maka viewmodel mungkin berbeda dari platform yang berbeda
Ryan
12
Setuju dengan @Ryan, lapisan layanan harus mengembalikan objek entitas atau koleksi objek entitas (bukan IQueryable). Kemudian pada entitas entitas ui ke SomeViewModel oleh Automapper misalnya.
Eldar
5
@Duffp: Anda tidak perlu membuat repositori untuk setiap entitas. Anda dapat menggunakan implementasi generik dan mengikat IRepository<>untuk GenericRepository<>di perpustakaan IOC Anda. Jawaban ini sudah sangat tua. Saya pikir solusi terbaik adalah menggabungkan semua repositori dalam satu kelas yang disebut UnitOfWork. Itu harus berisi repositori dari setiap jenis dan satu metode yang disebut SaveChanges. Semua repositori harus berbagi satu konteks EF.
LukLed
2
alih-alih mengembalikan viewmodel dari lapisan layanan, Anda harus mengembalikan DTO dan mengubahnya menjadi viewModels menggunakan automapper .. kadang-kadang mereka sama, ketika mereka tidak, Anda akan bersyukur telah menerapkan YGTNI "You're Going To Need It "
hanzolo
41

Seperti yang dikatakan Carnotaurus, repositori bertanggung jawab untuk memetakan data Anda dari format penyimpanan ke objek bisnis Anda. Seharusnya menangani kedua cara membaca dan menulis data (menghapus, memperbarui juga) dari dan ke penyimpanan.

Tujuan lapisan layanan di sisi lain adalah untuk merangkum logika bisnis ke satu tempat untuk mempromosikan penggunaan kembali kode dan pemisahan kekhawatiran. Apa artinya ini bagi saya dalam praktik ketika membangun situs MVC Asp.net adalah bahwa saya memiliki struktur ini

[Kontroler] memanggil [Layanan] yang memanggil [repositori]

Satu prinsip yang menurut saya berguna adalah menjaga logika seminimal mungkin dalam pengontrol dan repositori.

Di controller itu karena itu membantu membuat saya KERING. Ini sangat umum bahwa saya perlu menggunakan filter atau logika yang sama di tempat lain dan jika saya meletakkannya di controller saya tidak bisa menggunakannya kembali.

Dalam repositori itu karena saya ingin dapat mengganti penyimpanan saya (atau ORM) ketika ada sesuatu yang lebih baik datang. Dan jika saya memiliki logika di repositori saya harus menulis ulang logika ini ketika saya mengubah repositori. Jika repositori saya hanya mengembalikan IQueryable dan layanan melakukan penyaringan di sisi lain, saya hanya perlu mengganti pemetaan.

Sebagai contoh saya baru-baru ini mengganti beberapa repositori Linq-To-Sql saya dengan EF4 dan yang saya tetap teguh pada prinsip ini bisa diganti dalam hitungan menit. Di mana saya memiliki beberapa logika, itu hanya masalah beberapa jam saja.

Mikael Eliasson
sumber
Saya setuju dengan Anda, Mikael. Bahkan, saya telah menerapkan skenario yang sama di blog teknologi saya freecodebase.com dan saya menggunakan pendekatan kode pertama dalam implementasi ini. Kode sumber dapat diunduh di sini juga.
Toffee
Saya telah meneliti subjek umum menerapkan pola repositori di aplikasi MVC yang ada. Ini adalah kerangka kerja dipesan lebih dahulu dengan ORM seperti Rekaman Aktif dan konvensi Rails / Laravel lainnya, dan memiliki beberapa masalah arsitektur untuk pekerjaan yang saya lakukan sekarang. Satu hal yang saya temui adalah bahwa repositori "tidak boleh mengembalikan ViewModels, DTO, atau objek permintaan", tetapi harus mengembalikan objek repositori. Saya sedang memikirkan di mana layanan berinteraksi dengan objek repositori melalui metode seperti onBeforeBuildBrowseQuerydan dapat menggunakan pembuat kueri untuk mengubah kueri.
kaligrafi-io
@Toffee, tautan Anda rusak, tolong perbarui, saya perlu kode sumber untuk implementasi ini.
Hamza Khanzada
24

Jawaban yang diterima (dan dibalas ratusan kali) memiliki kelemahan besar. Saya ingin menunjukkan ini dalam komentar tetapi itu hanya akan terkubur di sana dalam 30 komentar sesuatu yang menunjukkan di sini.

Saya mengambil alih aplikasi perusahaan yang dibangun seperti itu dan reaksi awal saya adalah WTH ? ViewModels di lapisan layanan? Saya tidak ingin mengubah konvensi karena bertahun-tahun pengembangan telah dilakukan sehingga saya melanjutkan dengan mengembalikan ViewModels. Bocah itu berubah menjadi mimpi buruk ketika kami mulai menggunakan WPF. Kami (tim devs) selalu berkata: ViewModel yang mana? Yang asli (yang kami tulis untuk WPF) atau layanan? Mereka ditulis untuk aplikasi web dan bahkan memiliki bendera IsReadOnly untuk menonaktifkan edit di UI. Mayor, cacat utama dan semuanya karena satu kata: ViewModel !!

Sebelum Anda melakukan kesalahan yang sama, berikut adalah beberapa alasan selain cerita saya di atas:

Mengembalikan ViewModel dari lapisan layanan adalah sangat tidak, tidak. Itu seperti mengatakan:

  1. Jika Anda ingin menggunakan layanan ini, Anda sebaiknya menggunakan MVVM dan di sini adalah ViewModel yang perlu Anda gunakan. Aduh!

  2. Layanan membuat asumsi mereka akan ditampilkan di UI di suatu tempat. Bagaimana jika digunakan oleh aplikasi non UI seperti layanan web atau layanan windows?

  3. Itu bahkan bukan ViewModel nyata. ViewModel nyata memiliki kemampuan untuk diamati, perintah dll. Itu hanya POCO dengan nama yang buruk. (Lihat kisah saya di atas untuk mengapa nama penting.)

  4. Aplikasi yang mengkonsumsi lebih baik menjadi lapisan presentasi (ViewModels digunakan oleh lapisan ini) dan lebih memahami C #. Aduh lagi!

Tolong, jangan lakukan itu!

CodingYoshi
sumber
3
Saya hanya perlu mengomentari ini meskipun saya tahu itu tidak menambah diskusi: "Itu hanya POCO dengan nama yang buruk." << - Itu akan terlihat bagus di T-Shirt! :) :)
Mephisztoe
8

Biasanya repositori digunakan sebagai perancah untuk mengisi entitas Anda - lapisan layanan akan keluar dan sumber permintaan. Kemungkinan Anda akan meletakkan repositori di bawah lapisan layanan Anda.

CarneyCode
sumber
Jadi dalam aplikasi ASP.NET MVC menggunakan EF4, mungkin sesuatu seperti ini: SQL Server -> EF4 -> Repository -> Service Layer -> Model -> Controller dan sebaliknya?
Sam
1
Ya, repositori Anda dapat digunakan untuk mendapatkan entitas yang ringan dari EF4; dan lapisan layanan Anda dapat digunakan untuk mengirimkannya kembali ke manajer model khusus (Model dalam skenario Anda). Kontroler akan memanggil manajer model khusus Anda untuk melakukan ini ... Lihatlah blog saya untuk Mvc 2 / 3. Saya punya diagram.
CarneyCode
Hanya untuk klarifikasi: EF4 dalam skenario Anda adalah tempat Model berada di diagram saya dan Model dalam skenario Anda adalah manajer model khusus dalam diagram saya
CarneyCode
5

Lapisan repositori diimplementasikan untuk mengakses basis data dan membantu memperluas operasi CRUD pada basis data. Sedangkan lapisan layanan terdiri dari logika bisnis aplikasi dan dapat menggunakan lapisan repositori untuk mengimplementasikan logika tertentu yang melibatkan database. Dalam suatu aplikasi, lebih baik memiliki lapisan repositori dan lapisan layanan yang terpisah. Memiliki repositori dan lapisan layanan yang terpisah membuat kode lebih modular dan memisahkan basis data dari logika bisnis.

Akshay
sumber