Bagaimana cara menggunakan pola repositori dengan benar?

86

Saya bertanya-tanya bagaimana cara mengelompokkan repositori saya? Seperti dari contoh yang saya lihat di asp.net MVC dan di buku saya, mereka pada dasarnya menggunakan satu repositori per tabel database. Tapi sepertinya banyak repositori yang membuat Anda harus memanggil banyak repositori nanti untuk mengejek dan semacamnya.

Jadi saya rasa saya harus mengelompokkan mereka. Namun saya tidak yakin bagaimana mengelompokkan mereka.

Sekarang saya membuat Repositori pendaftaran untuk menangani semua hal pendaftaran saya. Namun ada 4 tabel yang perlu saya perbarui dan sebelumnya saya memiliki 3 repositori untuk melakukan ini.

Misalnya salah satu tabel adalah tabel lisensi. Ketika mereka mendaftar, saya melihat kunci mereka dan memeriksanya untuk melihat apakah ada di database. Sekarang apa yang terjadi jika saya perlu memeriksa kunci lisensi ini atau sesuatu yang lain dari tabel itu di tempat lain selain pendaftaran?

Satu tempat bisa login (periksa apakah kuncinya tidak kedaluwarsa).

Jadi apa yang akan saya lakukan dalam situasi ini? Tulis ulang kode itu lagi (pecahkan KERING)? Cobalah untuk menggabungkan 2 repositori ini bersama-sama dan berharap tidak ada metode yang diperlukan di waktu lain (seperti mungkin saya mungkin memiliki metode yang memeriksa apakah userName digunakan - mungkin saya akan membutuhkannya di tempat lain).

Juga jika saya menggabungkan mereka bersama-sama saya akan membutuhkan 2 lapisan layanan pergi ke repositori yang sama karena saya pikir memiliki semua logika untuk 2 bagian situs yang berbeda akan panjang dan saya harus memiliki nama seperti ValidateLogin (), ValdiateRegistrationForm () , ValdiateLoginRetrievePassword (), dll.

Atau panggil Repositori dan hanya memiliki nama yang terdengar aneh?

Sepertinya sulit untuk membuat repositori yang memiliki nama yang cukup umum sehingga Anda dapat menggunakannya untuk banyak tempat di aplikasi Anda dan masih masuk akal dan saya rasa memanggil repositori lain dalam repositori tidak akan menjadi praktik yang baik?

chobo2.dll
sumber
11
+1. Pertanyaan bagus.
Griegs
Terima kasih telah menggangguku selama beberapa waktu sekarang. Karena saya menemukan yang saya lihat di buku terlalu sederhana, mereka tidak menunjukkan kepada Anda apa yang harus dilakukan dalam situasi ini.
chobo2
Saya pada dasarnya melakukan hal yang sama seperti Anda dan ya jika saya memiliki kelas linq2sql yang digunakan di lebih dari 1 repositori dan saya perlu mengubah struktur tabel yang saya rusak KERING. Kurang dari ideal. Saya sekarang merencanakannya sedikit lebih baik sehingga saya tidak perlu menggunakan kelas linq2sql lebih dari sekali yang saya rasa merupakan pemisahan yang baik dari kekhawatiran tetapi saya meramalkan suatu hari ketika ini akan menjadi masalah nyata bagi saya.
Griegs
Saya mengajukan pertanyaan serupa (tetapi tidak identik) di sini: stackoverflow.com/questions/910156/…
Grokys
Ya tapi sepertinya sulit untuk merencanakannya untuk semua situasi. Seperti yang saya katakan, saya mungkin dapat menggabungkan login dan pendaftaran ke dalam Otentikasi dan memiliki 2 lapisan terpisah. Itu mungkin akan menyelesaikan masalah saya. Tetapi apa yang terjadi jika di katakanlah halaman profil situs saya, saya ingin menunjukkan kunci mereka untuk alasan apa pun (mungkin saya akan membiarkan htem mengubahnya atau sesuatu). Sekarang apa yang saya lakukan memecahkan KERING dan menulis hal yang sama? Atau coba buat repositori yang entah bagaimana bisa memuat ketiga tabel ini dengan nama yang bagus.
chobo2

Jawaban:

41

Satu hal yang saya lakukan salah saat bermain-main dengan pola repositori - sama seperti Anda, saya pikir tabel itu terkait dengan repositori 1: 1. Saat kami menerapkan beberapa aturan dari Domain Driven Design - masalah pengelompokan repositori sering kali hilang.

Repositori harus per akar Agregat dan bukan tabel. Artinya - jika entitas tidak boleh hidup sendiri (yaitu - jika Anda memiliki Registrantyang berpartisipasi secara khusus Registration) - itu hanya sebuah entitas, itu tidak memerlukan repositori, itu harus diperbarui / dibuat / diambil melalui repositori root agregat itu milik.

Tentu saja - dalam banyak kasus, teknik pengurangan jumlah repositori ini (sebenarnya - ini lebih merupakan teknik untuk menyusun model domain Anda) tidak dapat diterapkan karena setiap entitas seharusnya menjadi akar gabungan (yang sangat bergantung pada domain Anda, Saya hanya bisa memberikan tebakan buta). Dalam contoh Anda - Licensetampaknya menjadi akar agregat karena Anda harus dapat memeriksanya tanpa konteks Registrationentitas.

Tapi itu tidak membatasi kita pada repositori bertingkat ( Registrationrepositori diperbolehkan untuk mereferensikan Licenserepositori jika diperlukan). Itu tidak membatasi kita untuk mereferensikan Licenserepositori (lebih disukai - melalui IoC) langsung dari Registrationobjek.

Cobalah untuk tidak mengarahkan desain Anda melalui komplikasi yang disebabkan oleh teknologi atau kesalahpahaman tentang sesuatu. Mengelompokkan repositori ServiceXhanya karena Anda tidak ingin membangun 2 repositori bukanlah ide yang baik.

Jauh lebih baik untuk memberinya nama yang tepat - RegistrationServiceyaitu

Tetapi layanan harus dihindari secara umum - mereka sering menjadi penyebab yang mengarah pada model domain anemia .

EDIT:
Mulailah menggunakan IoC. Ini benar-benar meringankan rasa sakit ketergantungan suntikan.
Alih-alih menulis:

var registrationService = new RegistrationService(new RegistrationRepository(),  
      new LicenseRepository(), new GodOnlyKnowsWhatElseThatServiceNeeds());

Anda akan bisa menulis:

var registrationService = IoC.Resolve<IRegistrationService>();

Ps Akan lebih baik menggunakan apa yang disebut pencari layanan umum tetapi itu hanya sebuah contoh.

Arnis Lapsa
sumber
17
Hahaha ... Saya memberikan saran untuk menggunakan pencari lokasi. Selalu senang melihat betapa bodohnya aku di masa lalu.
Arnis Lapsa
1
@Arnis Sepertinya pendapat Anda telah berubah - Saya tertarik bagaimana Anda menjawab pertanyaan ini secara berbeda sekarang?
ngm
10
@ngm banyak yang berubah sejak saya menjawab ini. Saya masih setuju bahwa akar agregat harus menggambar batas transaksional (disimpan secara keseluruhan), tetapi saya kurang optimis tentang mengabstraksi persistensi menggunakan pola repositori. Akhir-akhir ini - Saya hanya menggunakan ORM secara langsung, karena hal-hal seperti manajemen eager / lazy loading terlalu canggung. Jauh lebih bermanfaat untuk berfokus pada pengembangan model domain kaya daripada berfokus pada mengabstraksi ketekunan.
Arnis Lapsa
2
@Pengembang tidak, tidak persis. mereka masih harus kegigihan bodoh. Anda mengambil akar agregat dari luar dan memanggil metode di atasnya yang melakukan pekerjaan itu. model domain saya tidak memiliki referensi, hanya standar framework .net. untuk mencapai itu, Anda harus memiliki model domain yang kaya dan alat yang cukup pintar (NHibernate melakukan triknya).
Arnis Lapsa
1
Sebaliknya. Mengambil lebih dari satu agregat seringkali berarti Anda dapat menggunakan PK atau indeks. Jauh lebih cepat daripada memanfaatkan hubungan yang dihasilkan EF. Semakin kecil agregat root, semakin mudah untuk mendapatkan performa dari agregat tersebut.
jgauffin
5

Satu hal yang saya mulai lakukan untuk mengatasi ini adalah benar-benar mengembangkan layanan yang membungkus repositori N. Semoga kerangka kerja DI atau IoC Anda dapat membantu mempermudahnya.

public class ServiceImpl {
    public ServiceImpl(IRepo1 repo1, IRepo2 repo2...) { }
}

Apakah itu masuk akal? Juga, saya memahami bahwa berbicara tentang layanan di rumah ini mungkin atau mungkin tidak benar-benar sesuai dengan prinsip DDD, saya hanya melakukannya karena tampaknya berhasil.

neouser99
sumber
1
Tidak, itu tidak masuk akal. Saya tidak menggunakan kerangka kerja DI atau IoC saat ini karena saya sudah cukup siap.
chobo2
1
Jika Anda bisa membuat instance repo Anda dengan new (), Anda bisa mencoba ini ... public ServiceImpl (): this (new Repo1, new Repo2 ...) {} sebagai konstruktor tambahan dalam layanan.
neouser99
Injeksi ketergantungan? Saya sudah melakukannya tetapi masih tidak yakin apa kode Anda dong dan apa yang dipecahkannya.
chobo2
Saya secara khusus mengejar penggabungan repositori. Kode mana yang tidak masuk akal? Jika Anda menggunakan DI, maka kode dalam jawabannya akan berfungsi dalam arti bahwa kerangka kerja DI Anda akan menyuntikkan layanan IRepo tersebut, dalam kode komentar pada dasarnya hanya pekerjaan kecil untuk melakukan DI (pada dasarnya tidak ada konstruktor parameter Anda 'menyuntikkan' dependensi tersebut ke ServiceImpl Anda).
neouser99
Saya sudah menggunakan DI sehingga saya dapat melakukan pengujian unit dengan lebih baik. Masalah saya adalah jika Anda membuat lapisan layanan dan repo dengan nama yang rinci. Kemudian jika Anda perlu menggunakannya di tempat lain, itu akan terlihat aneh memanggil seperti RegistrationService Layer yang Memanggil RegRepo di beberapa kelas lain mengatakan seperti ProfileClass. Jadi saya tidak melihat dari Anda contoh apa yang Anda lakukan sepenuhnya. Seperti Jika Anda mulai memiliki terlalu banyak Repos di lapisan layanan yang sama, Anda akan memiliki logika bisnis dan logika validasi yang sangat berbeda. Karena di lapisan layanan Anda biasanya memasukkan logika validasi. Begitu banyak yang saya butuhkan lebih ...
chobo2
2

Apa yang saya lakukan adalah saya memiliki kelas dasar abstrak yang didefinisikan sebagai berikut:

public abstract class ReadOnlyRepository<T,V>
{
     V Find(T lookupKey);
}

public abstract class InsertRepository<T>
{
     void Add(T entityToSave);
}

public abstract class UpdateRepository<T,V>
{
     V Update(T entityToUpdate);
}

public abstract class DeleteRepository<T>
{
     void Delete(T entityToDelete);
}

Anda kemudian dapat menurunkan repositori Anda dari kelas dasar abstrak dan memperluas repositori tunggal Anda selama argumen umum berbeda misalnya;

public class RegistrationRepository: ReadOnlyRepository<int, IRegistrationItem>,
                                     ReadOnlyRepository<string, IRegistrationItem> 

dll ....

Saya memerlukan repositori terpisah karena kami memiliki batasan pada beberapa repositori kami dan ini memberi kami fleksibilitas maksimum. Semoga ini membantu.

Michael Mann
sumber
Jadi Anda mencoba membuat repositori umum untuk menangani semua ini?
chobo2
Jadi apa yang sebenarnya dikatakan metode Pembaruan. Seperti Anda memiliki pembaruan V seperti ini dan itu meneruskan entitas T ke Pembaruan tetapi tidak ada kode yang benar-benar memperbaruinya atau ada?
chobo2
Ya .. Akan ada kode dalam metode pembaruan karena Anda akan menulis kelas yang diturunkan dari repositori generik. Kode implementasi dapat berupa Linq2SQL atau ADO.NET atau apa pun yang Anda pilih sebagai teknologi implementasi akses data Anda
Michael Mann
2

Saya memiliki ini sebagai kelas repositori saya dan ya saya memperluas di repositori tabel / area tetapi terkadang saya masih harus istirahat KERING.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MvcRepository
{
    public class Repository<T> : IRepository<T> where T : class
    {
        protected System.Data.Linq.DataContext _dataContextFactory;

        public IQueryable<T> All()
        {
            return GetTable.AsQueryable();
        }

        public IQueryable<T> FindAll(Func<T, bool> exp)
        {
            return GetTable.Where<T>(exp).AsQueryable();
        }

        public T Single(Func<T, bool> exp)
        {
            return GetTable.Single(exp);
        }

        public virtual void MarkForDeletion(T entity)
        {
            _dataContextFactory.GetTable<T>().DeleteOnSubmit(entity);
        }

        public virtual T CreateInstance()
        {
            T entity = Activator.CreateInstance<T>();
            GetTable.InsertOnSubmit(entity);
            return entity;
        }

        public void SaveAll()
        {
            _dataContextFactory.SubmitChanges();
        }

        public Repository(System.Data.Linq.DataContext dataContextFactory)
        {
            _dataContextFactory = dataContextFactory;
        }

        public System.Data.Linq.Table<T> GetTable
        {
            get { return _dataContextFactory.GetTable<T>(); }
        }

    }
}

EDIT

public class AdminRepository<T> : Repository<T> where T: class
{
    static AdminDataContext dc = new AdminDataContext(System.Configuration.ConfigurationManager.ConnectionStrings["MY_ConnectionString"].ConnectionString);

    public AdminRepository()
        : base( dc )
    {
    }

Saya juga memiliki datacontext yang dibuat menggunakan kelas Linq2SQL.dbml.

Jadi sekarang saya memiliki repositori standar yang mengimplementasikan panggilan standar seperti All and Find dan di AdminRepository saya, saya memiliki panggilan khusus.

Tidak menjawab pertanyaan KERING meskipun saya tidak berpikir.

griegs
sumber
Untuk apa "Repositori"? dan suka CreateInstance? Tidak yakin apa yang Anda lakukan.
chobo2
Ini generik seperti apa pun. Pada dasarnya Anda perlu memiliki repositori khusus untuk (area) Anda. Lihat edit di atas untuk AdminRepository saya.
Griegs
1

Berikut adalah contoh implementasi Repositori generik menggunakan FluentNHibernate. Ia mampu mempertahankan semua kelas yang telah Anda tulis mapper untuk. Ia bahkan mampu menghasilkan database Anda berdasarkan kelas mapper.

James Jones
sumber
1

Pola Repositori adalah pola desain yang buruk. Saya bekerja dengan banyak proyek .Net lama dan pola ini biasanya menyebabkan kesalahan "Transaksi Terdistribusi", "Kembalikan Sebagian", dan "Kumpulan Koneksi Habis" yang dapat dihindari. Masalahnya adalah bahwa pola mencoba menangani koneksi dan transaksi secara internal tetapi itu harus ditangani di lapisan Pengontrol. Juga EntityFramework sudah mengabstraksi banyak logika. Saya akan menyarankan menggunakan pola Layanan alih-alih menggunakan kembali kode bersama.

ColacX
sumber
0

Saya menyarankan Anda untuk melihat Arsitektur Sharp . Mereka menyarankan menggunakan satu repositori per entitas. Saya menggunakannya saat ini dalam proyek saya dan sangat senang dengan hasilnya.

Licik
sumber
Saya akan memberikan +1 di sini, tetapi dia telah menunjukkan bahwa wadah DI atau IoC bukanlah pilihan yang tepat (mengingat itu bukan satu-satunya manfaat dari Sharp Arch). Saya menduga ada beberapa dari banyak kode yang ada yang dia kerjakan.
neouser99
Apa yang dianggap entitas? Apakah itu seluruh database? atau apakah itu tabel database? Kontainer DI atau IoC bukanlah pilihan saat ini karena saya hanya tidak ingin mempelajarinya di atas 10 hal lain yang saya pelajari pada saat yang sama. mereka adalah sesuatu yang akan saya lihat ke revisi berikutnya dari situs saya atau proyek saya berikutnya. Meskipun saya tidak yakin apakah ini akan menjadi yang ini sekilas di situs dan sepertinya ingin Anda menggunakan nhirbrate dan saya menggunakan untuk LINQ ke Sql saat ini.
chobo2