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:
- Lapisan domain adalah jantung dari sistem, yang mengandung entitas, layanan, repositori ...
- Lapisan infrastruktur menyediakan implementasi antarmuka domain yang menjadi perhatian infrastruktur, misalnya file, basis data, protokol.
- 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 IModule2Service
yang memerlukan akses data, saya harus menyuntikkan DbContext
dan 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?
Jawaban:
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."
sumber
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:
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 kembali
IQueryable<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: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 pengembalianIQueryable<ProductSummary>
yang saya temukan lebih bagus daripadaRepository<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. :)
sumber