Validation in a tiered application

I am wondering what is the best way to do a database constraint check (for example, UNIQUE) in an ASP.NET MVC application, build with DDD in mind, where the underlying levels are Application Layer (application), Domain Layer (domain model) and infrastructure level (logic) sustainability, logging, etc.).

I looked through a lot of DDD samples, but many of them do not mention how to do validation in the repository (I believe that this type of validation is suitable here). If you know any samples, do it, please share them, it will be very appreciated.

More specifically, I have two questions. How would you do the actual check? . Would you explicitly check if the client name exists by querying the database, or would you try to insert it directly into the database and catch the error if there is one (it seems randomly)? I prefer the first one, and if you choose this, should it be done in the repository, or will it be the work of the application service?

When an error is detected, how do you pass it on to ASP.NET MVC so that the user can be well informed about the error? It is preferable to use ModelStateDictionary so that the error is easily highlighted on the form.

In the N-Lyered application from Microsoft Spain, they use the IValidatableObject interface, and the simplest property check is placed on the object itself, for example:

 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; } 

Before the object is saved, a Validate message is called to verify that the properties are 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)); } 

An ApplicationValidationErrorsException may be detected in the MVC application, and validation error messages can be parsed and inserted into ModelStateDictionary .

I could add all the validation logic to the SaveCustomer method, for example. requesting a database check if the client already exists using this column (UNIQUE one). Perhaps this is normal, but I would prefer that validator.IsValid (or something similar) do it for me or this check is again performed at the infrastructure level (if it is here, I'm not sure).

What do you think? How do you do this? I am very interested in getting more information about the various validation methods in layered applications.


Possible Solution # 1

In the case where the validation logic cannot be performed in the presentation layer (as suggested by Iulian Margarintescu) and must be performed at the service level, how would you pass validation errors to the presentation level?

Microsoft has a suggestion here (see list 5). What do you think about this?

+6
source share
2 answers

You mentioned DDD, but DDD is much more than entities and repositories. I assume that you are familiar with Mr. Eric Evans' book, Domain Name Management, and I highly recommend that you re-read the chapters on strategic design and limited contexts. Mr. Evans also has a very pleasant conversation called “What I Learned About DDD with a Book,” which you can find here here . Talks about SOA, CQRS and event sources from Greg Young or Udi Dahan also contain a lot of information about DDD and the application of DDD. I must warn you that you may find things that will change the way you use DDD.

Now for your validation question. One approach might be to request db (using an Ajax call directed to the application service) as soon as the user types something in the "name" field and tries to offer an alternative to the name if the one he entered already exists. When the user submits the form, try inserting a record in db and handle any duplicate key exception (at the repository or application level). Since you already check for duplicates ahead of time, cases when you get an exception should be quite rare, so any decent message “We're sorry, please try again” should appear from then on if you don’t have MANY users, they probably never see him

This post from Udi Dahan also contains some information on the approach of verification. Remember that this may be the restriction that you impose on the business, and not the restriction that the business imposes on you. Perhaps this provides more benefits for the business, allowing you to register users with the same name rather than reject them.

Also remember that DDD is much more about business than technology. You can make DDD and deploy the application as a single assembly. Layers of client code on top of services on top of entities on top of repositories on top of databases have repeatedly been abused in the name of a "good" design without any reason why it is a good design.

I'm not sure this will answer your questions, but I hope this helps you find the answers yourself.

+2
source

I am wondering what is the best way to do a database constraint check (e.g. UNIQUE)

and if you choose this, does it need to be done in the repository or will it be the work of the application service?

It depends on what you check.

If this is a cumulative root creation that you are trying to verify, then there is nothing global except the application itself that "holds" it. In this case, I apply the verification directly to the repository.

If he is an entity, he lives in an aggregate root context. In this case, I check the uniqueness of the entity in the aggregate root itself against all other objects in this particular aggregated root. The same goes for value objects in entities / roots.

Ps repository is a service. Do not look at services as a one-stop shop for the right, but hard, indication of the correct code. Naming questions. The same goes for the names Helpers, Managers, General, Utilities, etc. - they are practically meaningless.

In addition, you do not need to pollute your code base with pattern names: AllProducts> ProductRepository; OrderRegistrator> OrderService; order.isCompleted> IsOrderCompletedSpecification.IsSatisfiedBy.

More specifically, I have two questions. How would you do the actual check? Could you explicitly check if the client name already exists by querying the database, or will you try to insert it directly into the database and catch the error if it exists (seems messy)?

I would query the database. Although, if high performance is a problem, and the availability of the customer’s name is the only thing the database should provide, I would rely on the database (1 time back and forth).

When an error is detected, how do you pass it on to ASP.NET MVC so that the user can be well informed about the error? It is preferable to use ModelStateDictionary so that the error is easily highlighted in the form.

It is generally not recommended to use exceptions to control application flow, but since I want the user interface to show only the available things that can be done, I just throw an exception if the check fails. There is a handler in the user interface layer that carefully picks it up and spits it out in html.

It’s also important to understand what the team’s scope is (for example, the product ordering team can check 2 things - if the client is not a debtor and the product is in stock). If a team has several related checks, they must be linked together, so the user interface will receive them at the same time. Otherwise, it will lead to an annoying user experience (going through a lot of mistakes when trying to arrange this damned product again and again).

0
source

Source: https://habr.com/ru/post/914672/


All Articles