At first pass, my ErrorCollection.cfc could only accept error messages for data validation issues discovered in the Service Layer of the application. This was based on the thought that all validation would need to go in the Service layer as this was the master mind behind the business logic of the application. Well, German Buela just pointed me to a good post that really rocked that assumption to the ground.
At first I was thinking that the only validation that mattered was the business logic validation, but this is not true. The user interface has validation to it as well. One of the examples used in the above link included password verification - the common approach in which the user has to enter their password twice to help ensure that there are no spelling mistakes. When the form is submitted, checking that those two form fields are the same (that the password has no spelling mistakes) is not really a business logic validation item. This two-password-entry scenario is simply a construct of a user interface that is providing a better user experience; it has nothing to do with the business logic of the application at all. In fact, if the interface provided only one password input, the business logic would remain unchanged.
So, where does the validation between the two password fields have to occur? In the controller logic as part of the form data validation and form processing. This raises two hugely important points:
- Not all validation can be done in the service layer; some of it can be and must be done in the controller layer.
- The controller needs access to the ErrorCollection.cfc ColdFusion component BEFORE any calls to the Save() method located in the Service Layer.
Here's my logic on #2: Let's say we have a form where the user is entering their password. All the form has is the two password fields (one real one and one for spelling validation). That's it. Now, when that form gets submitted to the server, the controller cannot rely on a ErrorCollection.cfc to come back from the Save() method. If we did that, and the first password field was "valid", then the password would get sent to the database before we checked for matching field values. Therefore, it is essential that the field value symmetry must be checked before any calls to Save(). Since we want to have a unified object for error collection, the controller layer either needs to instantiate an ErrorCollection.cfc outside of the service layer, or it needs to be able to call a non-committing Validate() method in the service layer.
I think what we are seeing here is that we should come up with a way to merge error collection objects. Like ColdFusion's StructAppend(), we need a way to take error collection from multiple places and concatenate them in such a way that when passed to the View, all errors will centrally located and the View won't need to care about where they are coming from.
What we are also seeing is that the ErrorCollection.cfc needs to be able to take random messages that aren't necessarily tied to any service layer validation. Messages like, "Your passwords do not match", which has nothing to do with the service layer, must be accepted by the error collection object.
This whole revelation makes me lean towards the idea of having the controller layer call a Validate() method outside of any Save() method. Of course that would mean that the Save() method would have to perform duplicate validation logic. This would be a violation of the DRY principle (don't repeat yourself), but as Henry pointed out in a previous comment, it is OK to sacrifice some performance for robustness.