Validasi desain di lapisan model domain
Tip
Konten ini adalah kutipan dari eBook, .NET Microservices Architecture for Containerized .NET Applications, tersedia di .NET Docs atau sebagai PDF yang dapat diunduh gratis dan dapat dibaca secara offline.
Dalam DDD, aturan validasi dapat dianggap sebagai invarian. Tanggung jawab utama agregat adalah memberlakukan invarian di seluruh perubahan status untuk semua entitas dalam agregat tersebut.
Entitas domain harus selalu menjadi entitas yang valid. Ada sejumlah invarian untuk objek yang harus selalu benar. Misalnya, objek item pesanan selalu harus memiliki kuantitas yang harus berupa bilangan bulat positif, ditambah nama artikel dan harga. Oleh karena itu, penegakan invarian adalah tanggung jawab entitas domain (terutama akar agregat) dan objek entitas tidak boleh ada tanpa valid. Aturan invarian hanya dinyatakan sebagai kontrak, dan pengecualian atau pemberitahuan dinaikkan ketika dilanggar.
Alasan di balik ini adalah bahwa banyak bug terjadi karena objek dalam keadaan mereka seharusnya tidak pernah masuk.
Mari kita usulkan kita sekarang memiliki SendUserCreationEmailService yang mengambil UserProfile ... bagaimana kita dapat merasionalisasi dalam layanan itu bahwa Nama tidak null? Apakah kita memeriksanya lagi? Atau lebih mungkin ... Anda hanya tidak repot-repot memeriksa dan "berharap yang terbaik"—Anda berharap seseorang repot-repot memvalidasinya sebelum mengirimkannya kepada Anda. Tentu saja, menggunakan TDD salah satu tes pertama yang harus kita tulis adalah bahwa jika saya mengirim pelanggan nama null maka itu akan menimbulkan kesalahan. Tapi begitu kita mulai menulis tes semacam ini berulang-ulang kita menyadari ... "Bagaimana jika kita tidak pernah mengizinkan nama menjadi null? kami tidak akan menyediakan semua tes ini!".
Menerapkan validasi di lapisan model domain
Validasi biasanya diimplementasikan dalam konstruktor entitas domain atau dalam metode yang dapat memperbarui entitas. Ada beberapa cara untuk menerapkan validasi, seperti memverifikasi data dan menaikkan pengecualian jika validasi gagal. Ada juga pola yang lebih canggih seperti menggunakan pola Spesifikasi untuk validasi, dan pola Pemberitahuan untuk mengembalikan kumpulan kesalahan alih-alih mengembalikan pengecualian untuk setiap validasi saat terjadi.
Memvalidasi kondisi dan melepaskan pengecualian
Contoh kode berikut menunjukkan pendekatan paling sederhana untuk validasi di entitas domain dengan menaikkan pengecualian. Dalam tabel referensi di akhir bagian ini Anda dapat melihat tautan ke implementasi yang lebih canggih berdasarkan pola yang telah kita bahas sebelumnya.
public void SetAddress(Address address)
{
_shippingAddress = address?? throw new ArgumentNullException(nameof(address));
}
Contoh yang lebih baik akan menunjukkan kebutuhan untuk memastikan bahwa status internal tidak berubah, atau bahwa semua mutasi untuk metode terjadi. Misalnya, implementasi berikut akan meninggalkan objek dalam status tidak valid:
public void SetAddress(string line1, string line2,
string city, string state, int zip)
{
_shippingAddress.line1 = line1 ?? throw new ...
_shippingAddress.line2 = line2;
_shippingAddress.city = city ?? throw new ...
_shippingAddress.state = (IsValid(state) ? state : throw new …);
}
Jika nilai status tidak valid, baris alamat pertama dan kota telah diubah. Itu mungkin membuat alamat tidak valid.
Pendekatan serupa dapat digunakan dalam konstruktor entitas, meningkatkan pengecualian untuk memastikan bahwa entitas valid setelah dibuat.
Menggunakan atribut validasi dalam model berdasarkan anotasi data
Anotasi data, seperti atribut Wajib atau MaxLength, dapat digunakan untuk mengonfigurasi properti bidang database EF Core, seperti yang dijelaskan secara detail di bagian Pemetaan tabel, tetapi tidak lagi berfungsi untuk validasi entitas di EF Core (tidak juga IValidatableObject.Validate metode ), seperti yang telah mereka lakukan sejak EF 4.x di .NET Framework.
Anotasi data dan IValidatableObject antarmuka masih dapat digunakan untuk validasi model selama pengikatan model, sebelum pemanggilan tindakan pengontrol seperti biasa, tetapi model tersebut dimaksudkan untuk menjadi ViewModel atau DTO dan itu MVC atau API bukan masalah model domain.
Setelah membuat perbedaan konseptual menjadi jelas, Anda masih dapat menggunakan anotasi data dan IValidatableObject
di kelas entitas untuk validasi, jika tindakan Anda menerima parameter objek kelas entitas, yang tidak disarankan. Dalam hal ini, validasi akan terjadi setelah pengikatan model, tepat sebelum memanggil tindakan dan Anda dapat memeriksa properti ModelState.IsValid pengontrol untuk memeriksa hasilnya, tetapi kemudian sekali lagi, itu terjadi di pengontrol, bukan sebelum mempertahankan objek entitas di DbContext, seperti yang telah dilakukan sejak EF 4.x.
Anda masih dapat menerapkan validasi kustom di kelas entitas menggunakan anotasi data dan IValidatableObject.Validate
metode, dengan mengganti metode SaveChanges DbContext.
Anda dapat melihat contoh implementasi untuk memvalidasi IValidatableObject
entitas dalam komentar ini di GitHub. Sampel tersebut tidak melakukan validasi berbasis atribut, tetapi seharusnya mudah diterapkan menggunakan pantulan dalam penimpaan yang sama.
Namun, dari sudut pandang DDD, model domain paling baik disandingkan dengan penggunaan pengecualian dalam metode perilaku entitas Anda, atau dengan menerapkan pola Spesifikasi dan Pemberitahuan untuk menerapkan aturan validasi.
Adalah wajar untuk menggunakan anotasi data pada lapisan aplikasi di kelas ViewModel (bukan entitas domain) yang akan menerima input, untuk memungkinkan validasi model dalam lapisan UI. Namun, cara ini tidak boleh dilakukan pada pengecualian validasi dalam model domain.
Memvalidasi entitas dengan menerapkan pola Spesifikasi dan pola Pemberitahuan
Akhirnya, pendekatan yang lebih jelas untuk menerapkan validasi dalam model domain adalah dengan menerapkan pola Spesifikasi bersama dengan pola Pemberitahuan, seperti yang dijelaskan dalam beberapa sumber daya tambahan yang tercantum nanti.
Perlu disebutkan bahwa Anda juga dapat menggunakan hanya salah satu pola tersebut—misalnya, memvalidasi secara manual dengan pernyataan kontrol, tetapi menggunakan pola Pemberitahuan untuk menumpuk dan mengembalikan daftar kesalahan validasi.
Menggunakan validasi yang ditangguhkan di domain
Ada berbagai pendekatan untuk menangani validasi yang ditangguhkan di domain. Dalam bukunya Implementing Domain-Driven Design, Vaughn Vernon membahas ini di bagian tentang validasi.
Validasi dua langkah
Pertimbangkan juga validasi dua langkah. Gunakan validasi tingkat bidang pada perintah Objek Transfer Data (DTO) dan validasi tingkat domain di dalam entitas Anda. Anda dapat melakukan ini dengan mengembalikan objek hasil alih-alih pengecualian untuk mempermudah penanganan kesalahan validasi.
Menggunakan validasi bidang dengan anotasi data, misalnya, Anda tidak menduplikasi definisi validasi. Namun, eksekusi dapat berupa sisi server dan sisi klien dalam kasus DTO (perintah dan ViewModels, misalnya).
Sumber daya tambahan
Rachel Appel. Pengenalan validasi model di ASP.NET Core MVC
https://learn.microsoft.com/aspnet/core/mvc/models/validationRick Anderson. Menambahkan validasi
https://learn.microsoft.com/aspnet/core/tutorials/first-mvc-app/validationMartin Fowler. Mengganti Pengecualian Pelepasan dengan Pemberitahuan di Validasi
https://martinfowler.com/articles/replaceThrowWithNotification.htmlPola Spesifikasi dan Pemberitahuan
https://www.codeproject.com/Tips/790758/Specification-and-Notification-PatternsLev Gorodinski. Validasi dalam Domain-Driven Design (DDD)
http://gorodinski.com/blog/2012/05/19/validation-in-domain-driven-design-ddd/Colin Jack. Validasi Model Domain
https://colinjack.blogspot.com/2008/03/domain-model-validation.htmlJimmy Bogard. Validasi di dunia DDD
https://lostechies.com/jimmybogard/2009/02/15/validation-in-a-ddd-world/