Ke Repositori Atau Tidak Ke Repositori

10

Ketika saya pertama kali belajar tentang Domain Driven Design, saya juga diperkenalkan dengan repositori dan unit pola kerja yang dulunya nampak terkemuka untuk anak-anak keren yang melempar pertanyaan SQL seperti kubah ke database. Semakin dalam saya membahas topik itu, semakin saya belajar bahwa mereka tampaknya tidak diperlukan lagi karena ORM seperti EF dan NHibernate yang mengimplementasikan unit kerja dan repositori ke dalam satu API, yang disebut sesi atau konteks.

Sekarang saya tidak yakin apa yang harus saya lakukan. Ke repositori atau tidak ke repositori. Saya benar-benar memahami argumen bahwa abstraksi yang bocor seperti itu hanya menyulitkan hal-hal sambil menambahkan apa-apa yang dapat menyederhanakan akses data, namun, rasanya tidak benar untuk memasangkan setiap aspek yang mungkin dari aplikasi saya ke misalnya Entity Framework . Biasanya, saya mengikuti beberapa panduan sederhana:

  1. Lapisan domain adalah jantung dari sistem, yang mengandung entitas, layanan, repositori ...
  2. Lapisan infrastruktur menyediakan implementasi antarmuka domain yang menjadi perhatian infrastruktur, misalnya file, basis data, protokol.
  3. Lapisan aplikasi meng-host root komposisi yang menghubungkan semuanya dan mengatur semuanya.

Solusi saya biasanya terlihat seperti ini:

Domain.Module1
Domain.Module2
    IModule2Repo
    IModule2Service
    Module2
Infrastructure.Persistence
    Repositories
        EntityFrameworkRepositoryBase
MyApp
    Boostrapper
        -> inject EntityFrameworkRepositoryBase into IRepository etc.

Saya menjaga lapisan domain saya bersih dengan menggunakan IRepository<'T>yang juga merupakan masalah domain tidak tergantung pada hal lain yang memberitahu saya cara mengakses data. Ketika saya sekarang akan membuat implementasi konkret IModule2Serviceyang memerlukan akses data, saya harus menyuntikkan DbContextdan dengan ini, menyambungkannya langsung ke lapisan infrastruktur. ( Datang ke proyek Visual Studio, ini bisa sangat rumit karena dependensi melingkar! )

Selain itu Apa yang bisa menjadi alternatif untuk penyimpanan dan pekerjaan sampingan ? CQRS? Bagaimana cara abstrak kerangka infrastruktur murni?

Acrotygma
sumber
1
Sebagai tambahan, jika Anda berbicara tentang 'Repositori' seperti yang dijelaskan dalam DDD, maka EF dan nHibernate bukan repositori. Tentu, mereka sebagian mengabstraksi mekanisme akses data, tetapi ada lebih banyak repositori dari itu .
Eric King

Jawaban:

4

Rekayasa adalah semua tentang kompromi. Demikian juga pengembangan perangkat lunak. Saat ini, saya percaya hanya pilihan lain, yang akan lebih sederhana, adalah bekerja secara langsung dengan ORM. Tetapi seperti yang Anda katakan, itu mungkin mengunci Anda ke dalam kerangka kegigihan tertentu.

Jadi, Anda harus bertanya pada diri sendiri, "Apakah kerumitan tambahan sepadan dengan pemisahan kode Anda dari kegigihan". Setiap kali saya mendengar orang mengatakan mereka ingin memisahkan kode mereka dari kegigihan, saya bertanya, "Berapa kali dalam karier Anda, Anda mengubah kerangka kegigihan Anda?"

Masalah yang saya lihat dengan repositori adalah mereka membuat perubahan umum lebih sulit. Misalnya. menambahkan cara baru untuk meminta agregat. Dan mereka membuat perubahan yang tidak biasa (seharusnya) lebih mudah. Misalnya. mengubah implementasi repositori.

Lalu ada juga argumen unit-testing, yang saya katakan "Jika kerangka kerja kegigihan tidak memungkinkan Anda untuk mengejek database, baik di memori atau secara lokal, maka sama sekali tidak layak menggunakan kerangka kerja."

Euforia
sumber
1
Tujuan repositori adalah memisahkan aplikasi dari kegigihan dan tidak ada kerumitan yang terlibat. Dan saya mengubah rincian kegigihan setidaknya sekali, karena akses db adalah hal terakhir yang saya kode, sampai saat itu saya menggunakan dalam repo memori. Selain itu, ada jebakan yang sangat halus ketika Anda menggunakan detail kegigihan secara langsung. Objek bisnis Anda akan cenderung dirancang agar kompatibel dengan itu, bukan agnostik. Dan itu karena entitas kaya, yang dienkapsulasi dengan baik tidak dapat secara langsung dipulihkan dari penyimpanan apa pun (kecuali memori) tanpa solusi (jelek)
MikeSW
Tentang menambahkan cara baru ke kueri akar agregat atau entitas apa pun, itu sebabnya CQRS muncul. Secara pribadi, saya menyimpan Repositori untuk keperluan domain dan menggunakan penangan permintaan untuk permintaan sebenarnya. Dan penangan-penangan itu sangat erat digabungkan ke db dan ofc sangat efisien dalam apa yang mereka lakukan.
MikeSW
3

Saya pro-repositori, meskipun saya telah pindah dari pola repositori generik. Sebaliknya saya menyelaraskan repositori saya dengan fungsi bisnis yang mereka layani. Repositori tidak ditujukan untuk mengabstraksi ORM, karena ini bukan sesuatu yang saya harapkan akan berubah, dan pada saat yang sama saya menghindari membuat repositori terlalu terperinci. (Yaitu CRUD) Alih-alih repositori saya melayani dua hingga tiga tujuan utama:

  • Penerimaan data
  • Pembuatan data
  • Penghapusan sulit

Untuk pengambilan data, repositori selalu kembali IQueryable<TEntity>. Untuk pembuatan data, ia mengembalikan TEntity. Repositori menangani pemfilteran tingkat dasar saya seperti status "aktif" otorisasi untuk sistem yang menggunakan pola hapus-lunak, dan status "saat ini" untuk sistem yang menggunakan data historis. Pembuatan data bertanggung jawab hanya untuk memastikan bahwa referensi yang diperlukan diselesaikan dan dikaitkan dan bahwa entitas diatur dan siap untuk pergi.

Pembaruan data adalah tanggung jawab logika bisnis yang bekerja dengan entitas yang dipertanyakan. Ini dapat mencakup hal-hal seperti aturan validasi. Saya tidak mencoba merangkumnya dalam metode repositori.

Penghapusan di sebagian besar sistem saya adalah soft-delete sehingga akan jatuh di bawah pembaruan data. (IsActive = false) Dalam kasus hard-delete, ini akan menjadi satu-baris dalam Repositori.

Kenapa harus repositori? Kemampuan uji. Tentu, DbContext dapat diejek, tetapi lebih mudah untuk mengejek kelas yang kembaliIQueryable<TEntity>. Mereka juga bermain bagus dengan pola UoW, secara pribadi saya menggunakan pola DbContextScope Mehdime untuk lingkup unit kerja pada tingkat yang saya inginkan (Yaitu Pengendali di MVC) dan biarkan pengendali dan kelas layanan penolong saya menggunakan repositori tanpa perlu memberikan referensi ke UoW / dbContext sekitar. Menggunakan IQueryable berarti Anda tidak memerlukan banyak metode pembungkus dalam repositori, dan kode Anda dapat mengoptimalkan bagaimana data akan dikonsumsi. Misalnya repositori tidak perlu mengekspos metode seperti "Ada" atau "Hitung", atau mencoba untuk membungkus entitas dengan POCO lain dalam kasus di mana Anda ingin sub-set data. Mereka bahkan tidak perlu menangani opsi pemuatan cepat untuk data terkait yang mungkin atau mungkin tidak Anda butuhkan. Dengan melewati IQueryable, kode panggilan dapat:

.Any()
.Count()
.Include() // Generally avoided, instead I use .Select()
.Where()
.Select(x => new ViewModel or Anon. Type)
.Skip().Take()
.FirstOrDefault() / .SingleOrDefault() / .ToList()

Sangat fleksibel, dan dari PoV pengujian, repositori tiruan saya hanya perlu mengembalikan Daftar objek entitas yang dihuni.

Adapun repositori generik, saya telah pindah dari ini untuk sebagian besar karena ketika Anda berakhir dengan repositori per tabel pengendali / layanan Anda berakhir dengan referensi ke beberapa repositori untuk melakukan satu tindakan bisnis. Dalam kebanyakan kasus, hanya satu atau dua dari repositori ini yang benar-benar melakukan operasi penulisan (asalkan Anda menggunakan properti navigasi dengan benar) sementara sisanya mendukung yang dengan Reads. Saya lebih suka memiliki sesuatu seperti OrdersRepository yang mampu membaca dan membuat pesanan, dan membaca setiap pencarian yang relevan, dll. (Objek pelanggan ringan, produk, dll.) Untuk referensi ketika membuat pesanan, daripada memukul 5 atau 6 repositori yang berbeda. Ini mungkin melanggar puritan DNRY, tetapi argumen saya adalah bahwa tujuan repositori adalah untuk melayani pembuatan Pesanan yang mencakup referensi terkait.Repository<Product>untuk mendapatkan produk yang berdasarkan pesanan saya hanya membutuhkan entitas dengan beberapa bidang. Tempat penyimpanan saya mungkin memiliki .GetProducts()metode pengembalian IQueryable<ProductSummary>yang saya temukan lebih bagus daripada Repository<Product>yang akhirnya memiliki beberapa metode "Dapatkan" untuk mencoba dan melayani area yang berbeda dari kebutuhan aplikasi, dan / atau beberapa ekspresi penyaringan pass-in yang kompleks.

Saya memilih kode sederhana yang mudah diikuti, diuji, dan disesuaikan. Itu bisa disalahgunakan dengan cara yang buruk, tapi saya lebih suka memiliki sesuatu di mana pelanggaran mudah dikenali dan diperbaiki daripada mencoba untuk "mengunci" kode dengan cara yang tidak dapat disalahgunakan, gagal, dan kemudian memiliki sesuatu yang mimpi buruk untuk benar-benar melakukan apa yang dibayar klien untuk dilakukan pada akhirnya. :)

Steve Py
sumber