DDD & client-side validation

跟風遠走 提交于 2019-12-03 13:49:33

I have not come across an all encompassing validation solution. One reason for this is that validation logic can be subtly different depending on the application layer. For example, not all rules enforced by the domain layer can be enforced on the client side and so there will always be cases where client side validation may pass and yet you still need to display a validation message which propagated from the domain layer.

However, the validation model in ASP.NET MVC is extensible and you can extend it to support additional validation rules or event a validation framework other than DataAnnotations. Here is an example of integrating Enterprise Library Validation block with ASP.NET MVC, however as the article points out, client side validation was not implemented. Another approach would be to use DataAnnotations attributes in your domain layer. The DataAnnotations namespace is not tied to ASP.NET MVC.

The challenge in these approaches however is that of propagating validation rules from domain objects to view models. In theory, you can extend AutoMapper such that validation rules from a domain model are carried over to view model classes, however the cost of implementation and maintenance may outweigh the benefits of this solution.

The Fluent Validation framework could be used as a starting point for an all encompassing validation solution. There are many examples of using this framework with ASP.NET MVC.

In DDD, domain is usually self validating. In other words objects are not allowed to be in invalid state. Value objects help a lot here. They simply encapsulate formatting rules. For example you can have class ZipCode that is guaranteed to always be well formed. As an additional responsibility it can have a static method like ZipCode.TryParse or ZipCode.Validate that will take arbitrary string as parameter and validate. This way validation logic is concentrated in one place. If your domain objects are accessible directly from UI than you don't need to duplicate this logic anywhere else. This is the case for fat clients (Windows Forms, WPF). Unfortunately there is no way to avoid some duplication for web clients when they are required to perform validation without round-tripping to server.

You should encapsulate the validation logic in simple classes which represent your domain knowledge.

I write about it in my primitive obsession blog post. Here's how your ASP.NET MVC controller may look like if you create such classes:

public class CustomerController : Controller
{
    [HttpPost]
    public ActionResult CreateCustomer(CustomerInfo customerInfo)
    {
        Result<Email> emailResult = Email.Create(customerInfo.Email);
        Result<CustomerName> nameResult = CustomerName.Create(customerInfo.Name);

        if (emailResult.Failure)
            ModelState.AddModelError("Email", emailResult.Error);
        if (nameResult.Failure)
            ModelState.AddModelError("Name", nameResult.Error);

        if (!ModelState.IsValid)
            return View(customerInfo);

        Customer customer = new Customer(nameResult.Value, emailResult.Value);
        // Rest of the method
    }
}

No need to use Annotations because they essentially encourage you to duplicate the validation logic.

Compare these code samples:

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!