Seperti yang saya pahami, dalam DDD, adalah tepat untuk menggunakan pola repositori dengan root agregat. Pertanyaan saya adalah, apakah saya harus mengembalikan data sebagai entitas atau objek domain / DTO?
Mungkin beberapa kode akan menjelaskan pertanyaan saya lebih lanjut:
Kesatuan
public class Customer
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Haruskah saya melakukan sesuatu seperti ini?
public Customer GetCustomerByName(string name) { /*some code*/ }
Atau sesuatu seperti ini?
public class CustomerDTO
{
public Guid Id { get; set; }
public FullName { get; set; }
}
public CustomerDTO GetCustomerByName(string name) { /*some code*/ }
Pertanyaan tambahan:
- Dalam repositori, haruskah saya mengembalikan IQueryable atau IEnumerable?
- Dalam layanan atau repositori, saya harus melakukan sesuatu seperti ..
GetCustomerByLastName
,GetCustomerByFirstName
,GetCustomerByEmail
? atau hanya membuat metode seperti ituGetCustomerBy(Func<string, bool> predicate)
?
c#
domain-driven-design
repository-pattern
ikan kode
sumber
sumber
GetCustomerByName('John Smith')
kembali jika Anda memiliki dua puluh John Smiths di basis data Anda? Sepertinya Anda mengasumsikan tidak ada dua orang yang memiliki nama yang sama.Jawaban:
Yah itu sepenuhnya tergantung pada kasus penggunaan Anda. Satu-satunya alasan saya bisa memikirkan mengembalikan DTO daripada entitas penuh adalah jika entitas Anda sangat besar dan Anda hanya perlu mengerjakan subsetnya.
Jika ini masalahnya, maka mungkin Anda harus mempertimbangkan kembali model domain Anda dan membagi entitas besar Anda menjadi entitas yang lebih kecil terkait.
Aturan praktis yang baik adalah untuk selalu mengembalikan tipe yang paling sederhana (tertinggi dalam hierarki warisan). Jadi, kembali
IEnumerable
kecuali jika Anda ingin membiarkan konsumen repositori bekerja denganIQueryable
.Secara pribadi, saya pikir mengembalikan sebuah
IQueryable
abstraksi bocor tetapi saya telah bertemu beberapa pengembang yang dengan penuh semangat berpendapat bahwa itu bukan abstraksi. Dalam pikiran saya semua logika kueri harus dimuat dan disembunyikan oleh Repositori. Jika Anda mengizinkan kode panggilan untuk menyesuaikan kueri mereka, lalu apa gunanya repositori?Untuk alasan yang sama seperti yang saya sebutkan pada poin 1, pasti tidak menggunakan
GetCustomerBy(Func<string, bool> predicate)
. Pada awalnya mungkin terlihat menggoda, tetapi inilah mengapa orang-orang belajar membenci repositori generik. Itu bocor.Hal-hal seperti
GetByPredicate(Func<T, bool> predicate)
hanya berguna ketika mereka tersembunyi di balik kelas yang konkret. Jadi, jika Anda memiliki kelas dasar abstrakRepositoryBase<T>
yang disebut terpaparprotected T GetByPredicate(Func<T, bool> predicate)
yang hanya digunakan oleh repositori beton (misalnya,public class CustomerRepository : RepositoryBase<Customer>
) maka itu akan baik-baik saja.sumber
Ada komunitas orang yang cukup besar yang menggunakan CQRS untuk mengimplementasikan domain mereka. Perasaan saya adalah bahwa, jika antarmuka repositori Anda analog dengan praktik terbaik yang digunakan oleh mereka, Anda tidak akan tersesat terlalu jauh.
Berdasarkan apa yang saya lihat ...
1) Penangan perintah biasanya menggunakan repositori untuk memuat agregat melalui repositori. Perintah menargetkan contoh spesifik agregat; repositori memuat root dengan ID. Tidak ada, yang bisa saya lihat, kasus di mana perintah dijalankan terhadap kumpulan agregat (sebagai gantinya, Anda pertama-tama akan menjalankan kueri untuk mendapatkan kumpulan agregat, kemudian menghitung koleksi dan mengeluarkan perintah untuk masing-masing.
Oleh karena itu, dalam konteks di mana Anda akan memodifikasi agregat, saya berharap repositori mengembalikan entitas (alias akar agregat).
2) Penangan permintaan tidak menyentuh agregat sama sekali; sebagai gantinya, mereka bekerja dengan proyeksi agregat - objek nilai yang menggambarkan keadaan agregat / agregat pada beberapa titik waktu. Jadi pikirkan ProjectionDTO, daripada AggregateDTO, dan Anda memiliki ide yang tepat.
Dalam konteks di mana Anda akan menjalankan kueri terhadap agregat, menyiapkannya untuk ditampilkan, dan seterusnya, saya berharap untuk melihat DTO, atau koleksi DTO, dikembalikan, bukan entitas.
Semua
getCustomerByProperty
panggilan Anda tampak seperti pertanyaan bagi saya, sehingga mereka akan masuk ke dalam kategori yang terakhir. Saya mungkin ingin menggunakan satu titik masuk untuk menghasilkan koleksi, jadi saya akan mencari tahu apakahadalah pilihan yang masuk akal; penangan permintaan kemudian akan membangun spesifikasi yang sesuai dari parameter yang diberikan, dan meneruskan spesifikasi itu ke repositori. The downside adalah bahwa tanda tangan benar-benar menunjukkan bahwa repositori adalah kumpulan dalam memori; tidak jelas bagi saya bahwa predikat membeli Anda banyak jika repositori hanyalah abstraksi menjalankan pernyataan SQL terhadap database relasional.
Ada beberapa pola yang bisa membantu. Sebagai contoh, alih-alih membangun spesifikasi dengan tangan, sampaikan ke repositori deskripsi kendala, dan biarkan implementasi repositori untuk memutuskan apa yang harus dilakukan.
Peringatan: java seperti mengetik terdeteksi
Kesimpulannya: pilihan antara menyediakan agregat dan menyediakan DTO tergantung pada apa yang Anda harapkan konsumen lakukan dengannya. Dugaan saya akan menjadi salah satu implementasi konkret yang mendukung antarmuka untuk setiap konteks.
sumber
getCustomersThatSatisfy(Specification spec)
is saya hanya akan daftar properti yang saya butuhkan untuk opsi pencarian. Apakah saya benar?Berdasarkan pengetahuan saya tentang DDD Anda harus memiliki ini,
Itu tergantung, Anda akan menemukan pandangan campuran dari orang yang berbeda untuk pertanyaan ini. Tapi saya pribadi percaya bahwa jika repositori Anda akan digunakan oleh layanan seperti CustomerService maka Anda dapat menggunakan IQueryable jika tidak tetap dengan IEnumerable.
Haruskah Repositori mengembalikan IQueryable?
Dalam repositori Anda, Anda harus memiliki fungsi generik seperti yang Anda katakan
GetCustomer(Func predicate)
tetapi di lapisan layanan Anda tambahkan tiga metode yang berbeda, ini karena ada kemungkinan layanan Anda dipanggil dari klien yang berbeda dan mereka membutuhkan DTO yang berbeda.Anda juga dapat menggunakan Pola Repositori Generik untuk CRUD umum, jika Anda belum menyadarinya.
sumber