Thoughts on our approach to validation in a distributed application with multiple clients

I am here to hear your thoughts on the approach we used to test. We are still at an early stage of development, so we can still change it. Validation is very important for this application and our customers, so we need to find the most optimal way. Let me describe what we have done so far ...

We are creating this application that will be consumed by different customers. We do not control all customers and therefore have strict validation requirements in all layers. We control some client applications, one of which is the WPF application used by ~ 100 users. From this application, the workflow is as follows:

| Client | Backend Service | ViewModel -> ClientRepository -> ServiceClient -> Service (WCF) -> ApplicationService -> DomainModel -> Repository -> Database 

As candidates for validation, we see the following.

  • Client: ViewModel validation to support user interface with required fields, length, etc.
  • Backend: DTO request validation because we cannot rely on customers to always provide 100% valid values.
  • Backend: Domain Model Entity Verification. We do not want our objects to ever be in an invalid state, and therefore each object will contain different checks when performing operations.
  • Backend: checking the database, for example, failed restrictions (FK, uniqueness, lengths, etc.).

Verification of the validation of the ViewModel clients is pretty obvious, and for our own clients, as many errors as possible should be fixed there before contacting the service. I can’t talk about other applications that consume our service, although the worst must be accepted.

The DTO service request should be checked first of all in case of third-party applications and errors in our own client. Ensuring the correctness of the request can prevent an error from occurring in processing the request, thereby providing a more efficient service. Like checking a ViewModel, it comes down to the required fields, lengths and formats (e.g. email) of various properties.

The entities in the domain model themselves must ensure that they always have fully valid attributes / properties, we achieve this, as is the case, taking the Customer object as an example.

 public class Customer : Entity { private Customer() : base() { } public Customer(Guid id, string givenName, string surname) : this(id, givenName, null, surname) { } public Customer(Guid id, string givenName, string middleName, string surname) : base(id) { if (string.IsNullOrWhiteSpace(givenName)) throw new ArgumentException(GenericErrors.StringNullOrEmpty, "givenName"); if (string.IsNullOrWhiteSpace(surname)) throw new ArgumentException(GenericErrors.StringNullOrEmpty, "surname"); GivenName = givenName.Trim(); Surname = surname.Trim(); if (!string.IsNullOrWhiteSpace(middleName)) MiddleName = middleName.Trim(); } } 

Now, while this ensures that the attributes are valid, the CustomerValidator class validates the Customer class as a whole, ensuring that it is in a valid state and not only has valid attributes. CustomerValidator is implemented using a fluctuation system. It is called in the application service before passing the client object to the database.

What do you think of our approach so far?

What bothers me a bit is the use of exceptions that are thrown everywhere. For instance. ArgumentException example above, but also InvalidOperationException in case of a call to some method that is not allowed in the current state of the object.

We hope that this exception will be very rarely thrown because the DTO service request is checked, and therefore I think it might be good? For example, when a DTO service request is checked, argument exceptions should never occur unless there is an error in the validation. So you can say that these argument checks in the domain model act as an extra layer of security. InvalidOperationException , on the other hand, can be raised if the client calls a service method that calls the method on the Customer object, which is not available in its current state (and therefore, it must fail).

What do you think? If everything sounds good, how can I properly inform the user through WCF when something fails? Be it ArgumentException , InvalidOperationException or an exception containing a list of errors (thrown by ApplicationService after checking the client object using the CustomerValidator class). Do I have to somehow catch all of these exceptions and turn them into some kind of general exception raised by WCF, and so the client can respond to it and tell the user what happened?

I am interested in hearing your thoughts about our approach. We are at the beginning of creating this rather large application, and we really want to find a good way to do the verification. There are some very important parts in our application where the correctness of the data is very important, so validation is important!

+4
source share
2 answers

My own opinion is that the domain consistency should be processed by the domain. Therefore, there is no need for a CustomerValidator .

Regarding exceptions, you should ArgumentNullException in mind that individually ArgumentNullException must be in terms of the ubiquitous language (for a deeper explanation see http://epic.tesio.it/2013/03/04/exceptions-are-terms-ot-the- ubiquitous-language.html ).

By the way, even if all your DTOs have been pre-checked, you should never remove the correct check from the domain. Business invariants are our own responsibility.

Regarding performance: exceptions have computational cost, but in most DDD scenarios I've seen so far, this is not a problem. In particular, this is not a problem when teams come from people.

change
validation is always the responsibility of the domain. Take an ISIN value object: it is up to its constructor to guarantee its own invariants by throwing the correct exceptions. In a well-coded domain, you cannot contain an instance of an invalid object. Thus, you do not need any validator to accumulate errors.

In the same way, factories can provide business invariants if and only if they are the only way to get an instance. Technological invariants, such as db column length, should be outside the scope, so a factory can be a good place for them. This will also have the advantage of including exception chains: SqlExceptions are not very expressive to clients.

With expressive exceptions, clients simply need to try / catch those exceptions that they can handle (and remember that presenting an exception to the user is a way to handle it).

+2
source

There is a point at which data enters your system. This can be in the form of action arguments (presentation model) or parameters at the service level. You should always hyper-validate at this point (make everything nullable, then enable nulls, check negative numbers for integers, etc.) and make sure everything is 100% correct at these entry points. Then the rest of your system should not worry about this if validation is not suitable for a specific edge case. Customer verification is good, but you should never fully rely on it. It may be disabled from time to time. In addition, there is no promise that the customers who trigger your actions are the customers they think they are (for example, we all changed the request parameter in the URL to find out what will happen).

My problem with the code you posted is that there is currently data in your domain that may or may not be valid. If you always perform checks at the external borders of your process, you will never have to worry. Furthermore, you never wonder: "Where did I post this check?"

0
source

All Articles