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!