Validasi dalam aplikasi berlapis

Saya bertanya-tanya apa cara terbaik untuk melakukan validasi batasan basis data (misalnya UNIK) dalam aplikasi ASP.NET MVC, dibangun dengan mempertimbangkan DDD, dengan lapisan yang mendasarinya adalah Lapisan Aplikasi (layanan aplikasi), Lapisan Domain (model domain) dan Lapisan Infrastruktur (logika persistensi, logging, dll.).

Saya telah mencari banyak contoh DDD, tetapi yang tidak disebutkan oleh banyak dari mereka adalah bagaimana melakukan validasi dalam repositori (saya kira di sinilah jenis validasi ini cocok). Jika Anda mengetahui sampel yang melakukan hal ini, silakan bagikan, itu akan sangat dihargai.

Lebih spesifiknya, saya punya dua pertanyaan. Bagaimana Anda melakukan validasi sebenarnya? Apakah Anda akan memeriksa secara eksplisit apakah nama pelanggan sudah ada dengan menanyakan database, atau apakah Anda akan mencoba memasukkannya langsung ke database dan menemukan kesalahan jika ada (tampaknya berantakan) ? Saya lebih suka yang pertama, dan jika memilih ini, haruskah itu dilakukan di repositori, atau haruskah itu menjadi tugas layanan aplikasi?

Ketika kesalahan terdeteksi, bagaimana Anda meneruskannya ke ASP.NET MVC sehingga pengguna dapat diberitahu dengan baik tentang kesalahan tersebut? Lebih disukai menggunakan ModelStateDictionary sehingga kesalahan mudah disorot pada formulir.

Di aplikasi N-Lyered oleh Microsoft Spanyol, mereka menggunakan antarmuka IValidatableObject dan validasi properti paling sederhana ditempatkan pada entitas itu sendiri, seperti:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    var validationResults = new List<ValidationResult>();

    if (String.IsNullOrWhiteSpace(this.FirstName))
        validationResults.Add(new ValidationResult(Messages.validation_CustomerFirstNameCannotBeNull, new string[] { "FirstName" }));

    return validationResults;
}

Sebelum entitas dipertahankan, pesan Validasi dipanggil untuk memastikan bahwa propertinya valid:

void SaveCustomer(Customer customer)
{
    var validator = EntityValidatorFactory.CreateValidator();

    if (validator.IsValid(customer)) //if customer is valid
    {
        _customerRepository.Add(customer);
        _customerRepository.UnitOfWork.Commit();
    }
    else
        throw new ApplicationValidationErrorsException(validator.GetInvalidMessages<Customer>(customer));
}

ApplicationValidationErrorsException kemudian dapat ditangkap dalam aplikasi MVC dan pesan kesalahan validasi dapat diurai dan dimasukkan ke dalam ModelStateDictionary.

Saya dapat menambahkan semua logika validasi ke dalam metode SaveCustomer, mis. menanyakan database memeriksa apakah pelanggan sudah ada menggunakan kolom tertentu (yang UNIK). Mungkin ini oke, tapi saya lebih suka validator.IsValid (atau yang serupa) melakukan ini untuk saya, atau validasi dilakukan sekali lagi di lapisan Infrastruktur (jika termasuk di sini, saya tidak yakin).

Bagaimana menurutmu? Bagaimana Anda melakukannya? Saya sangat tertarik untuk mendapatkan lebih banyak wawasan tentang berbagai teknik validasi dalam aplikasi berlapis.


Kemungkinan solusi #1

Jika logika validasi tidak dapat dilakukan di lapisan presentasi (seperti yang disarankan Iulian Margarintescu) dan perlu dilakukan di lapisan layanan, bagaimana Anda meneruskan kesalahan validasi ke lapisan presentasi?

Microsoft memiliki saran di sini (lihat daftar 5). Apa pendapat Anda tentang pendekatan itu?


person Tommy Jakobsen    schedule 01.05.2012    source sumber
comment
Saya menyarankan Anda untuk membaca ulasan Ayende tentang aplikasi sampel, yang terdiri dari beberapa posting, mulai di sini. Yang ditanggapi oleh pembuat aplikasi contoh di sini. Kesimpulan saya adalah jangan menghabiskan terlalu banyak waktu dengan aplikasi contoh ini.   -  person Marijn    schedule 02.05.2012
comment
Buang semua kekacauan teknis MS itu ke luar jendela. Mulai dari awal, dalam c# murni. Buat proyek perpustakaan kelas sederhana dan coba modelkan domain Anda seolah-olah tidak perlu menyimpan apa pun di database. Hanya dengan begitu Anda akan dapat benar-benar memahami kerangka kerja seperti apa yang perlu Anda tambahkan agar berjalan/terlihat bagus dan dampak buruk apa yang ditimbulkannya pada basis kode Anda. Ketika Anda memulai dengan arsitektur besar tanpa memikirkan domain, tenggelam dalam labirin teka-teki yang tidak berarti tidak bisa dihindari.   -  person Arnis Lapsa    schedule 02.05.2012
comment
Saya tahu itu, dan ya, saya menggunakan kata itu seperti yang sebenarnya tidak terjadi. Beberapa implementasi seperti unit kerja dan repositori terinspirasi oleh sampelnya, tetapi saya masih jauh dari menggunakan semua komponennya. Aplikasi saya sangat sederhana C# kecuali dari Unity, Entity Framework (DbContext) dan AutoMapper.   -  person Tommy Jakobsen    schedule 02.05.2012
comment
Saya telah menghapus penyebutan aplikasi sampel MS karena tidak relevan dengan pertanyaan.   -  person Tommy Jakobsen    schedule 02.05.2012


Jawaban (2)


Anda menyebutkan DDD, namun DDD lebih dari sekadar entitas dan repositori. Saya berasumsi Anda sudah familiar dengan buku Mr Eric Evans Domain Driven Design dan saya sangat menyarankan Anda membaca kembali bab tentang desain strategis dan konteks terbatas. Tuan Evans juga mempunyai ceramah yang sangat bagus berjudul "Apa yang telah saya pelajari tentang DDD sejak buku ini" yang dapat Anda temukan di sini. Pembicaraan tentang SOA, CQRS dan event sourcing dari Greg Young atau Udi Dahan juga banyak memuat informasi tentang DDD dan penerapan DDD. Saya harus memperingatkan Anda bahwa Anda mungkin menemukan hal-hal yang akan mengubah cara Anda berpikir tentang penerapan DDD.

Sekarang untuk pertanyaan Anda tentang validasi - Salah satu pendekatannya adalah dengan menanyakan db (menggunakan panggilan Ajax yang diarahkan ke layanan aplikasi) segera setelah pengguna mengetikkan sesuatu di bidang "nama" dan mencoba menyarankan nama alternatif jika yang dia masukkan sudah ada. Saat pengguna mengirimkan formulir, coba masukkan catatan ke dalam db dan tangani pengecualian kunci duplikat apa pun (di tingkat layanan repositori atau aplikasi). Karena Anda sudah memeriksa duplikat sebelumnya, kasus di mana Anda mendapatkan pengecualian seharusnya cukup jarang terjadi sehingga pesan "Maaf, silakan coba lagi" yang layak harus dilakukan, kecuali Anda memiliki BANYAK pengguna, mereka mungkin tidak akan pernah melihatnya .

Postingan ini dari Udi Dahan juga memiliki beberapa informasi tentang pendekatan validasi. Ingatlah bahwa ini mungkin merupakan batasan yang Anda terapkan pada bisnis, bukan batasan yang diterapkan bisnis pada Anda - Mungkin akan memberikan nilai lebih bagi bisnis dengan mengizinkan pelanggan dengan nama yang sama untuk mendaftar, daripada menolaknya.

Ingat juga bahwa DDD lebih banyak berkaitan dengan bisnis dibandingkan dengan teknologi. Anda dapat melakukan DDD dan menerapkan aplikasi Anda sebagai satu perakitan. Lapisan kode klien di atas layanan di atas entitas di atas repositori di atas database telah disalahgunakan berkali-kali atas nama desain yang "bagus", tanpa alasan apa pun mengapa desain tersebut bagus.

Saya tidak yakin ini akan menjawab pertanyaan Anda, tetapi saya harap ini akan memandu Anda menemukan jawabannya sendiri.

person Iulian Margarintescu    schedule 02.05.2012
comment
Terima kasih atas jawaban Anda Lulian. Saya sangat menyadari bahwa DDD JAUH lebih dari apa yang telah saya sebutkan, dan saya telah membaca buku Evans berkali-kali dan melihat ceramahnya yang Anda tautkan, pertanyaan saya lebih banyak tentang validasi dan di mana melakukannya . Saya seharusnya tidak pernah menyebut DDD, karena orang cenderung panik (tidak mengatakan Anda melakukannya) :-) Tapi saya masih belajar dan saat ini tentang logika validasi dan di mana harus meletakkannya. Anda memberi saya beberapa instruksi bagus, dan saya akan melihat postingan yang Anda sebutkan. Terima kasih! - person Tommy Jakobsen; 02.05.2012
comment
Nama pelanggan adalah contoh sederhana yang dapat ditangani oleh klien dengan memberikan layanan seperti yang Anda sebutkan. Namun ketika validasi menjadi lebih rumit dan tidak dapat dilakukan di lapisan presentasi, bagaimana Anda meneruskan hasil validasi dari lapisan layanan ke lapisan presentasi? MS punya saran di sini: asp.net/mvc/tutorials/older-versions/models-(data)/ (lihat daftar 5). Apa pendapat Anda tentang pendekatan itu? - person Tommy Jakobsen; 02.05.2012
comment
Saya pribadi tidak menyukai pendekatan itu, karena ini menyiratkan Anda dapat membuat produk yang tidak valid dan mengandalkan layanan untuk menambahkan logika tambahan. Ini juga menyiratkan bahwa model tersebut sebenarnya adalah model domain anemia yang jika Anda mencoba melakukan DDD adalah anti-pola. Anda dapat meminta lapisan presentasi memanggil layanan dengan DTO sesegera mungkin (tetapi sebelum pembuatan AR sebenarnya) dengan satu-satunya tujuan menerapkan logika validasi. Layanan dapat mengembalikan hasil validasi secara langsung dan lapisan presentasi tinggal menampilkannya. - person Iulian Margarintescu; 03.05.2012
comment
Pada saat pembuatan AR, jika status tidak valid terdeteksi, berikan pengecualian. Logika validasi sebenarnya dapat berada dalam rakitan bersama jika duplikasi kode menjadi perhatian. Juga ada dua jenis validasi - validasi invarian Agregat Roos, dan validasi UI dasar. Anda tidak bisa dan tidak boleh melakukan validasi invarian di luar akar agregat karena ini terkait erat dengan batas AR. Validasi UI dasar harus Anda lakukan sesegera mungkin untuk mencegah keadaan tidak valid. Semoga ini masuk akal. - person Iulian Margarintescu; 03.05.2012

Saya bertanya-tanya apa cara terbaik untuk melakukan validasi batasan basis data (mis. UNIK)

dan jika memilih ini, haruskah itu dilakukan di repositori, atau haruskah itu tugas layanan aplikasi?

Itu tergantung pada apa yang Anda validasi.

Jika ini adalah kreasi root agregat yang Anda coba validasi - maka tidak ada yang lebih global selain aplikasi itu sendiri yang menampungnya. Dalam hal ini, saya menerapkan validasi langsung di repositori.

Jika itu sebuah entitas, ia berada dalam konteks akar agregat. Dalam hal ini saya memvalidasi keunikan entitas dalam akar agregat itu sendiri terhadap semua entitas lain dalam akar agregat khusus ini. Hal yang sama berlaku untuk objek nilai di entitas/akar.

hal. repositori adalah layanan. Jangan melihat layanan sebagai toko universal untuk kebutuhan tetapi sulit untuk menyebutkan kode dengan benar. Pemberian nama itu penting. Hal yang sama berlaku untuk nama-nama seperti Helpers, Managers, Common, Utilities, dll. - semuanya tidak ada artinya.

Selain itu - Anda tidak perlu mengotori basis kode Anda dengan nama pola: AllProducts › ProductRepository; Pendaftar Pesanan › Layanan Pesanan; pesanan.isCompleted › IsOrderCompletedSpecification.IsSatisfiedBy.

Lebih spesifiknya, saya punya dua pertanyaan. Bagaimana Anda melakukan validasi sebenarnya? Apakah Anda akan secara eksplisit memeriksa apakah nama pelanggan sudah ada dengan menanyakan database, atau apakah Anda akan mencoba memasukkannya langsung ke database dan menemukan kesalahan jika ada (tampaknya berantakan)?

Saya akan menanyakan database. Meskipun demikian, jika kinerja tinggi menjadi perhatian dan ketersediaan nama pelanggan adalah satu-satunya hal yang harus ditegakkan oleh basis data - saya akan memilih mengandalkan basis data (1 perjalanan pulang pergi lebih sedikit).

Ketika kesalahan terdeteksi, bagaimana Anda meneruskannya ke ASP.NET MVC sehingga pengguna dapat diberi tahu dengan baik tentang kesalahan tersebut? Sebaiknya menggunakan ModelStateDictionary sehingga kesalahan mudah disorot pada formulir.

Biasanya bukan ide yang baik untuk menggunakan pengecualian untuk mengontrol aliran aplikasi, tetapi, karena saya ingin menerapkan UI untuk hanya menampilkan hal-hal yang tersedia yang dapat dilakukan, saya hanya memberikan pengecualian jika validasi gagal. Di lapisan UI, ada penangan yang mengambilnya dengan rapi dan mengeluarkannya dalam html.

Selain itu - penting untuk memahami ruang lingkup perintah (misalnya perintah pemesanan produk mungkin memeriksa 2 hal - jika pelanggan bukan debitur dan apakah produk ada di toko). Jika perintah memiliki beberapa validasi terkait, validasi tersebut harus digabungkan sehingga UI akan menerimanya secara bersamaan. Jika tidak, hal ini akan menyebabkan pengalaman pengguna yang mengganggu (mengalami banyak kesalahan saat mencoba memesan produk sialan itu berulang kali).

person Arnis Lapsa    schedule 03.05.2012