Saya masih mencari praktik terbaik untuk validasi model domain. Apakah itu baik untuk meletakkan validasi dalam konstruktor model domain? contoh validasi model domain saya sebagai berikut:
public class Order
{
private readonly List<OrderLine> _lineItems;
public virtual Customer Customer { get; private set; }
public virtual DateTime OrderDate { get; private set; }
public virtual decimal OrderTotal { get; private set; }
public Order (Customer customer)
{
if (customer == null)
throw new ArgumentException("Customer name must be defined");
Customer = customer;
OrderDate = DateTime.Now;
_lineItems = new List<LineItem>();
}
public void AddOderLine //....
public IEnumerable<OrderLine> AddOderLine { get {return _lineItems;} }
}
public class OrderLine
{
public virtual Order Order { get; set; }
public virtual Product Product { get; set; }
public virtual int Quantity { get; set; }
public virtual decimal UnitPrice { get; set; }
public OrderLine(Order order, int quantity, Product product)
{
if (order == null)
throw new ArgumentException("Order name must be defined");
if (quantity <= 0)
throw new ArgumentException("Quantity must be greater than zero");
if (product == null)
throw new ArgumentException("Product name must be defined");
Order = order;
Quantity = quantity;
Product = product;
}
}
Terima kasih atas semua saran Anda.
sumber
Terlepas dari kenyataan, pertanyaan ini agak basi, saya ingin menambahkan sesuatu yang berharga:
Saya ingin setuju dengan @MichaelBorgwardt dan memperluas dengan membawa testability. Dalam "Bekerja Efektif dengan Kode Legacy", Michael Feathers berbicara banyak tentang hambatan untuk pengujian dan salah satu hambatan itu adalah objek "sulit untuk membangun". Membangun objek yang tidak valid harus dimungkinkan, dan seperti yang disarankan Fowler, pemeriksaan validitas tergantung konteks harus dapat mengidentifikasi kondisi tersebut. Jika Anda tidak dapat menemukan cara membangun objek dalam test harness, Anda akan kesulitan menguji kelas Anda.
Mengenai validitas saya suka memikirkan sistem kontrol. Sistem kontrol bekerja dengan secara konstan menganalisis keadaan output dan menerapkan tindakan korektif ketika output menyimpang dari titik setel, ini disebut kontrol loop tertutup. Kontrol loop tertutup pada dasarnya mengharapkan penyimpangan dan bertindak untuk memperbaikinya dan itulah cara dunia nyata bekerja, itulah sebabnya mengapa semua sistem kontrol nyata biasanya menggunakan pengontrol loop tertutup.
Saya pikir menggunakan validasi tergantung konteks dan objek yang mudah dibangun akan membuat sistem Anda lebih mudah untuk digunakan.
sumber
Karena saya yakin Anda sudah tahu ...
Memeriksa validitas data yang diteruskan sebagai parameter c'tor jelas valid di konstruktor - jika tidak, Anda mungkin mengizinkan pembangunan objek yang tidak valid.
Namun (dan ini hanya pendapat saya, tidak dapat menemukan dokumen yang bagus pada saat ini) - jika validasi data memerlukan operasi yang kompleks (seperti operasi async - mungkin validasi berbasis server jika mengembangkan aplikasi desktop), maka lebih baik dimasukkan ke dalam inisialisasi atau fungsi validasi eksplisit semacam dan anggota diatur ke nilai default (seperti
null
) dalam c'tor.Juga, hanya sebagai catatan samping saat Anda memasukkannya dalam contoh kode Anda ...
Kecuali jika Anda melakukan validasi lebih lanjut (atau fungsi lainnya)
AddOrderLine
, saya kemungkinan besar akan mengeksposList<LineItem>
sebagai properti daripadaOrder
bertindak sebagai fasad .sumber
AddLineItem
metode. Bahkan, untuk DDD, ini lebih disukai. JikaList<LineItem>
diubah menjadi objek koleksi khusus, maka properti yang terbuka dan segala sesuatu yang bergantung padaList<LineItem>
properti dapat berubah, salah, dan terkecuali.Validasi harus dilakukan sesegera mungkin.
Validasi dalam konteks apa pun, baik model Domain atau cara penulisan perangkat lunak lainnya, harus memenuhi tujuan APA yang ingin Anda validasi dan pada level apa Anda saat ini.
Berdasarkan pertanyaan Anda, saya kira jawabannya adalah dengan memecah validasi.
Validasi properti memeriksa apakah nilai untuk properti itu sudah benar, misalnya ketika rentang antara 1-10 dikeluarkan.
Validasi objek menjamin bahwa semua properti pada objek tersebut valid dalam hubungannya satu sama lain. misalnya BeginDate sebelum EndDate. Misalkan Anda membaca nilai dari penyimpanan data dan BeginDate dan EndDate diinisialisasi ke DateTime.Min secara default. Saat mengatur BeginDate, tidak ada alasan untuk menegakkan aturan "harus sebelum EndDate", karena ini tidak berlaku YET. Aturan ini harus diperiksa SETELAH semua properti telah ditetapkan. Ini bisa disebut pada level root agregat
Validasi juga harus dilakukan sebelumnya pada entitas agregat (atau akar agregat). Objek Pesanan dapat berisi data yang valid dan begitu juga dengan OrderLines. Tetapi kemudian aturan bisnis menyatakan bahwa tidak ada pesanan mungkin lebih dari $ 1.000. Bagaimana Anda akan menegakkan aturan ini dalam beberapa kasus, ini diizinkan. Anda tidak bisa hanya menambahkan properti "jangan validasi jumlah" karena ini akan menyebabkan penyalahgunaan (cepat atau lambat, bahkan mungkin Anda, hanya untuk menyingkirkan "permintaan jahat" ini).
selanjutnya ada validasi di lapisan presentasi. Apakah Anda benar-benar akan mengirim objek melalui jaringan, mengetahui itu akan gagal? Atau akankah Anda mengampuni pengguna burdon ini dan memberitahukannya segera setelah ia memasukkan nilai yang tidak valid. mis. sebagian besar waktu lingkungan DEV Anda akan lebih lambat daripada produksi. Apakah Anda ingin menunggu selama 30 detik sebelum Anda diberi tahu "Anda lupa bidang ini LAGI selama uji coba LAINNYA", terutama ketika ada bug produksi yang harus diperbaiki dengan atasan yang menghembuskan leher Anda?
Validasi pada tingkat persistensi seharusnya sedekat mungkin dengan validasi nilai properti. Ini akan membantu mencegah pengecualian dengan membaca kesalahan "nol" atau "nilai tidak valid" saat menggunakan pemetaan dari segala jenis atau pembaca data lama biasa. Menggunakan prosedur yang tersimpan tidak menyelesaikan masalah ini, tetapi mengharuskan untuk menulis logika pembatalan yang sama LAGI dan menjalankannya LAGI. Dan prosedur tersimpan adalah domain admin DB, jadi jangan coba-coba melakukan pekerjaannya juga (atau lebih buruk lagi mengganggunya dengan "seluk memetik dia tidak dibayar".
jadi untuk mengatakannya dengan beberapa kata terkenal "itu tergantung", tetapi setidaknya sekarang Anda tahu MENGAPA itu tergantung.
Saya berharap saya dapat menempatkan semua ini di satu tempat, tetapi sayangnya, ini tidak dapat dilakukan. Melakukan ini akan menempatkan ketergantungan pada "objek Dewa" yang berisi SEMUA validasi untuk SEMUA lapisan. Anda tidak ingin melewati jalan gelap itu.
Karena alasan ini saya hanya melempar pengecualian validasi ke tingkat properti. Semua level lain yang saya gunakan ValidationResult dengan metode IsValid untuk mengumpulkan semua "aturan yang rusak" dan meneruskannya kepada pengguna dalam satu AggregateException tunggal.
Ketika menyebarkan tumpukan panggilan, saya kemudian mengumpulkan ini lagi di AggregateExceptions sampai saya mencapai lapisan presentasi. Lapisan layanan dapat membuang pengecualian ini langsung ke klien dalam kasus WCF sebagai FaultException.
Ini memungkinkan saya untuk mengambil pengecualian dan membaginya untuk menunjukkan kesalahan individu pada setiap kontrol input atau meratakannya dan menunjukkannya dalam satu daftar. Pilihan ada padamu.
inilah mengapa saya juga menyebutkan validasi presentasi, untuk membuat hubungan arus pendek sebanyak mungkin.
Jika Anda bertanya-tanya mengapa saya juga memiliki validasi pada tingkat agregasi (atau tingkat layanan jika Anda suka), itu karena saya tidak memiliki bola kristal yang memberitahu saya siapa yang akan menggunakan layanan saya di masa depan. Anda akan cukup kesulitan menemukan kesalahan Anda sendiri untuk mencegah orang lain membuat kesalahan Anda :) dengan memasukkan data yang tidak valid. Jika Anda mengelola aplikasi A, tetapi aplikasi B memberi makan beberapa data menggunakan layanan Anda. Tebak siapa yang pertama mereka tanyakan ketika ada bug? Administrator aplikasi B dengan senang hati akan memberi tahu pengguna "tidak ada kesalahan di akhir saya, saya hanya memasukkan data".
sumber