I think you need to separate two types of validation in this case; domain validation and application validation.
Application validation is what you have when you verify that the command property 'text' is between 20 and 200 characters; so you validate this with the GUI and with a view-model-validator that also executes at the server after a POST. The same goes for e-mail (btw, I hope you realize that an e-mail such as `32.d+"Hello World .42"@mindomän.local" is a valid according to the RFC).
Then you have another validation; check that article exists - you have to ask yourself the question why the article should not exist if there is indeed a command sent from the GUI that is about attaching a comment to it. Was your GUI eventually consistent and you have an aggregate root, the article, that can be physically deleted from the data store? In that case you just move the command to the error queue because the command handler fails to load the aggregate root.
In the above case, you would have infrastructure that handles poison messages - they would for example retry the message 1-5 times and then move it to a poision queue where you could manually inspect the collection of messages and re-dispatch those relevant. It's a good thing to monitor.
So now we've discussed:
What about commands that are out of sync with the domain? Perhaps you have a rule in your domain logic saying that after 5 comments to an article, only comments below 400 characters are allowed, but one guy was too late with the 5th comment and got to be the 6th - GUI didn't catch it because it was not consistent with the domain at the point of him sending his command - in this case you have a 'validation failure' as a part of your domain logic and you would return the corresponding failure event.
The event could be in the form of a message onto a message broker or your custom dispatcher. The web server, if the application is monolithic, could synchronously listen for both a success event and the failure event mentioned and display the appropriate view/partial.
Often you have custom event that means failure for many types of commands, and it is this event that you subscribe to from the web server's perspective.
In the system that we are working on, we're doing request-response with commands/events over a MassTransit+RabbitMQ message bus+broker and we have an event in this particular domain (modelling a workflow in part) that is named InvalidStateTransitionError
. Most commands that try to move along an edge in the state graph may cause this event to happen. In our case, we're modelling the GUI after an eventually consistent paradigm, and so we send the user to a 'command accepted' page and thereafter let the web server's views update passively through event subscriptions. It should be mentioned that we are also doing event-sourcing in the aggregate roots (and will be doing for sagas as well).
So you see, a lot of the validation you are talking about are actually application-type validations, not actual domain logic. There's no problem in having a simple domain model if your domain is simple but you are doing DDD. As you continue modelling your domain, however, you'll discover that the domain might not be as simple as it first turned out. In many cases the aggregate root/entity might just accept a method invocation caused by a command and change some of its state without even performing any validation - especially if you trust your commands like you'd do if you validate them in the web server that you control.
I can recommand watching the two presentations on DDD from Norwegian Developer Conference 2011 and also Greg's presentation at Öredev 2010.
Cheers,
Henke
If I needed to input data and capture errors, I would probably do something more like:
public class Employee
{
public int EmpID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Salary { get; set; }
}
public class ErrorEmployee : Employee
{
public string[] ErrorReason { get; set; }
}
- Read CSV file row by row
- For each element i.e EmpId, FirstName... etc do the required validations by calling individual methods having validation logic. eg: public bool ValidateIsDecimal(string value) { } public bool ValidateIsEmpIdExists(string value) { } etc
- If valid, fill corresponding property of "Employees" class. Add this Class to Employees collection (eg: List)
- If NOT VALID, fill "ErrorReason" property, with appropriate reason as to what caused the validation to fail.(eg: Required filed was missing or datatype is not decimal etc) Add this Class to ErrorEmployees collection (eg: List)
I wouldn't want to pollute the regular Employee (singular, use the plural for a collection of them) object with error messages. As in the note above, check out the FluentValidation library for your validation and capture a list of errors rather than just the first found.
Best Answer
Disclaimer: The following is my approach and not holy scripture. I'm at work so I wrote this swiftly and some code may not work/compile!
Controllers
They are components of the presentation layer so they shouldn't be concerned with business or database logic. Validation logic is a subset of business logic.
Services
I implement the Service Layer pattern which is basically a bunch of classes that are loosely associated with your domain models and act upon them. These service classes do all of the business logic. This includes the database stuff. If you're not too keen on putting database logic here you can implement the Repository pattern and inject the repository classes into your service classes.
So now the flow of things is becoming clear
and this equates to
Every service class is described by an interface so now you can use DI to inject them into your Controllers like so:
It's now clear exactly what dependencies your Controller has since it's all there in the constructor. The Controllers are also cleaner since all of the business logic is in the service layer. Here's an insert example:
Now the validation part
The type of validation that constitutes checking the database is what I call deep validation. I put simple (shallow) validation in the ViewModel (like yourself) and deep validation in the business layer aka services.
So the deep validation that needs to take place when you insert a
Client
is found in theInsert
method of theClientService
. All the database checks are done there, maybe by calling a repository or just directly in the service if you're not using repositories.So if the
client
is invalid how do we return the validation errors back to the Controller?We throw an exception since that's what exceptions are for. But it's going to be our own custom exception:
So when things are invalid (in our
ClientService
):We've made sure the insert failed at the soonest possible moment, which is good.
Now all that's left is to catch these custom exceptions in our Controller. You can do this many ways. One is to use
try/catch
clauses:but they will thoroughly litter your Controller.
The proper MVC approach is to catch all the errors in the
OnException
method of your Controller:You can put this code in a
BaseController
from which all of your other Controllers will inherit in order to avoid code duplication.That's the basic framework I work in. I hope I didn't make things too complicated or confusing.