Jika Pola Repositori berlebihan untuk ORM modern (EF, nHibernate), apa abstraksi yang lebih baik?

12

Saya baru-baru ini membaca banyak argumen yang menentang penggunaan pola repositori dengan ORM seperti Entity Framework yang kuat karena menggabungkan fungsionalitas seperti repositori, bersama dengan fungsionalitas Unit Kerja juga.

Argumen lain yang menentang penggunaan pola untuk situasi seperti pengujian unit adalah bahwa pola repositori adalah abstraksi yang bocor karena implementasi yang lebih umum meningkatkan IQueryable.

Argumen yang menentang penggunaan pola repositori masuk akal bagi saya, tetapi metode alternatif abstraksi yang disarankan seringkali lebih membingungkan dan tampak sama sulitnya dengan masalahnya.

Solusi Jimmy Bogards tampaknya adalah campuran meniup abstraksi, tetapi juga memperkenalkan arsitekturnya sendiri. https://lostechies.com/jimmybogard/2012/10/08/favor-query-objects-over-repository/

Contoh lain dari repositori yang tidak perlu .... tetapi gunakan arsitektur saya! http://blog.gauffin.org/2012/10/22/griffin-decoupled-the-queries/

Lain ... http://www.thereformedprogrammer.net/is-the-repository-pattern-useful-with-entity-framework

Saya belum menemukan pengganti yang jelas atau alternatif dari pendekatan pola repositori "terlalu rumit" yang tidak lebih dirancang sendiri.

Pengembang Lainnya
sumber
4
Apa yang ingin Anda capai secara spesifik? Abstraksi harus memiliki tujuan. Jika Anda menulis aplikasi CRUD, ORM mungkin cukup abstrak.
JacquesB
@ JacquesB Saya mencoba untuk menghindari masalah impedansi relasional objek dengan model domain yang kuat, tetapi juga abstrak yang jauh dari viewmodels saya dalam implementasi mvc.
AnotherDeveloper
Reed Cospey memiliki banyak hal positif untuk dikatakan tentang IQuerable di sini: stackoverflow.com/questions/1578778/using-iqueryable-with-linq Ini menyiratkan lebih baik pada lapisan transport. Sejauh abstraksi, saya menemukan penggunaan untuk pola Repositori Generik bekerja dengan baik ketika saya perlu menyuntikkan EntityType tetapi masih ingin mempertahankan metode umum. Di sisi lain, saya sendiri berpendapat di forum MSDN LINQ bahwa EF adalah pola repositori karena semuanya ada dalam memori. Satu proyek menggunakan banyak klausa Dimana sebagai pemanggilan metode yang bekerja dengan baik.
John Peters
Tapi satu bidang yang saya perhatikan tetapi ditolak adalah bisnis Pohon Ekspresi, beberapa sangat menyukainya, tapi .... Anda harus mempelajarinya dengan keras sebelum Anda merasa itu berguna.
John Peters
2
kita harus kembali ke memanggil SQL dari pengontrol
bobek

Jawaban:

12

Saya pikir Anda menggabungkan repositori dan repositori generik.

Repositori dasar hanya antarmuka penyimpanan data Anda dan menyediakan metode untuk mengembalikan data

IRepository {
   List<Data> GetDataById(string id);
}

Itu tidak membocorkan lapisan data ke dalam kode Anda melalui IQueryable atau cara-cara lain untuk mengirimkan pertanyaan acak dan memberikan permukaan metode yang dapat diuji dan disuntikkan dengan baik.

Repositori Generik memungkinkan Anda untuk mengirimkan pertanyaan seperti ORM

IGenericRepository<T> {
    List<T> Get<T>(IQuery query);
    //or
    IQueryable<T> Get<T>();
}

Saya setuju tidak ada gunanya menggunakan Repositori Generik di atas ORM yang pada dasarnya hanyalah Repositori Generik.

Jawabannya adalah menggunakan pola Repositori dasar untuk menyembunyikan ORM Anda

Ewan
sumber
1
ORM ORM? Saya berusaha untuk menjadi lucu. Mengapa Anda perlu mengabstraksikan ORM?
johnny
1
alasan yang sama Anda abstrak apa pun. untuk menghindari mencemari kode Anda dengan kelas berpemilik
Ewan
6

Sebagian besar argumen yang Anda sebutkan salah atribut ke fitur pola Repositori yang tidak dimiliki.

Secara konseptual, Repositori sebagaimana didefinisikan sebelumnya dalam DDD hanyalah kumpulan objek yang dapat Anda cari atau tambahkan. Mekanisme ketekunan di baliknya disarikan, sehingga sebagai konsumen Anda mendapat ilusi bahwa itu adalah koleksi di dalam memori.

Implementasi Repositori yang memiliki abstraksi yang bocor (mengekspos IQueryablesmisalnya) adalah implementasi Repositori yang buruk.

Implementasi Repositori yang mengekspos lebih dari sekedar operasi pengumpulan (misalnya, fitur Unit Kerja) adalah implementasi Repositori yang buruk.

Apakah ada alternatif untuk Repositori untuk akses data? Ya, tetapi itu tidak terkait dengan masalah yang Anda kemukakan dalam pertanyaan Anda.

guillaume31
sumber
1

Bagi saya, repositori, dikombinasikan dengan ORM atau lapisan ketekunan DB lainnya, memiliki kelemahan ini:

  1. Menutupi Unit Kerja. UoW harus dikodekan oleh programmer dan jarang dapat diimplementasikan sebagai semacam sihir di latar belakang, di mana pengguna hanya membuat kueri dan modifikasi, tanpa mendefinisikan batas-batas UoW dan mungkin titik komit. Kadang-kadang, UoW ditinggalkan dengan menguranginya menjadi micro UoW (misalnya sesi NHibernate) di setiap metode akses Repositori.
  2. Menutupi, atau, dalam kasus terburuk, menghancurkan Ketidaktahuan Kegigihan: Metode seperti "Memuat ()", "Dapatkan ()", "Simpan ()" atau "Perbarui ()" menyarankan operasi langsung, objek tunggal, seolah-olah mengirim individu SQL / DML, atau seolah-olah bekerja dengan file. Bahkan, misalnya, metode NHibernate, dengan nama-nama yang menyesatkan ini, biasanya tidak membuat akses individu, tetapi memerlukan beban malas atau memasukkan / memperbarui batch (Persistence Ignorance). Terkadang, programmer bertanya-tanya mengapa mereka tidak mendapatkan operasi DB langsung dan secara paksa memecah kebodohan, sehingga mematikan kinerja dan menggunakan upaya besar untuk benar-benar membuat sistem (jauh!) Lebih buruk.
  3. Pertumbuhan yang Tidak Terkendali. Repositori sederhana mungkin mengumpulkan lebih banyak metode untuk memenuhi kebutuhan spesifik.

Seperti:

public interface ICarsRepository  /* initial */
{
    ICar CreateNewCar();
    ICar LoadCar(int id); // bad, should be for multiple IDs.
    void SaveCar(ICar carToSave); // bad, no individual saves, use UoW commit!
}

public interface ICarsRepository  /* a few years later */
{
    ICar CreateNewCar();
    ICar LoadCar(int id); 
    IList<ICar> GetBlueCars();
    IList<ICar> GetRedYellowGreenCars();
    IList<ICar> GetCarsByColor(Color colorOfCars); // a bit better
    IList<ICar> GetCarsByColor(IEnumerable<Color> colorsOfCars); // better!
    IList<ICar> GetCarsWithPowerBetween(int hpFrom, int hpTo);
    IList<ICar> GetCarsWithPowerKwBetween(int kwFrom, int kwTo);
    IList<ICar> GetCarsBuiltBetween(int yearFrom, int yearTo);
    IList<ICar> GetCarsBuiltBetween(DateTime from, DateTime to); // some also need month and day
    IList<ICar> GetHybridCarsBuiltBetween(DateTime from, DateTime to); 
    IList<ICar> GetElectricCarsBuiltBetween(DateTime from, DateTime to); 
    IList<ICar> GetCarsFromManufacturer(IManufacturer carManufacturer); 
    bool HasCarMeanwhileBeenChangedBySomebodyElseInDb(ICar car); // persistence ignorance broken
    void SaveCar(ICar carToSave);
}

4. Objek Bahaya Tuhan: Anda mungkin tergoda untuk membuat satu kelas dewa, yang mencakup semua model atau lapisan akses data Anda. Kelas repositori tidak hanya berisi metode Mobil, tetapi metode untuk semua entitas.

Menurut pendapat saya, lebih baik menawarkan setidaknya beberapa peluang permintaan, untuk menghindari kekacauan besar dari banyak metode tujuan tunggal. Tidak masalah apakah itu LINQ, bahasa permintaan sendiri, atau bahkan sesuatu yang diambil langsung dari ORM (OK, semacam masalah kopling ...).

Erik Hart
sumber
4
Di mana semua metode ini pergi jika seseorang tidak menggunakan Pola Repositori?
johnny
1

Jika tujuan dari antarmuka-Repositori adalah untuk mengejek basis data untuk unittest (= test in isolation) abstraksi terbaik adalah sesuatu yang mudah untuk diejek.

Sulit untuk mengejek antarmuka repositori yang didasarkan pada hasil IQueryable.

Dari sudut pandang Unit-testing

IRepository {
   List<Data> GetDataById(string id);
}

dapat diejek dengan mudah

IGenericRepository<T> {
    List<T> Get<T>(IQuery query);
}

dapat diejek dengan mudah hanya jika tiruan mengabaikan konten dari parameter kueri.

IGenericRepository<T> {
    IQueryable<T> Get<T>(some_parameters);
}

tidak bisa dengan mudah diejek

k3b
sumber
0

Saya tidak berpikir pola repositori berlebihan, jika Anda menggunakan fungsi lambda untuk query. Terutama ketika Anda harus mengabstraksi ORM (menurut saya Anda selalu harus) maka saya tidak akan peduli dengan detail implementasi repositori itu sendiri.

Sebagai contoh:

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        UserRepository ur = new UserRepository();
        var userWithA = ur.GetBy(u => u.Name.StartsWith("A"));

        Console.WriteLine(userWithA.Name);


        ur.GetAllBy(u => u.Name.StartsWith("M"))
          .ForEach(u => Console.WriteLine(u.Name));


        ur.GetAllBy(u => u.Age > 13)
          .ForEach(u => Console.WriteLine(u.Name));
    }
}

public class UserRepository 
{
    List<User> users = new List<User> { 
        new User{Name="Joe", Age=10},
            new User{Name="Allen", Age=12},
            new User{Name="Martin", Age=14},
            new User{Name="Mary", Age=15},
            new User{Name="Ashton", Age=29}
    };

    public User GetBy(Predicate<User> userPredicate)
    {
        return users.Find(userPredicate);
    }

    public List<User> GetAllBy(Predicate<User> userPredicate)
    {
        return users.FindAll(userPredicate);
    }
}

public class User
{
    public string Name { get; set; }

    public int Age { get; set; }
}
dhalsim
sumber