I am learning software design by building a CRUD web application (ASP.NET MVC with Entity Framework). I split it into two projects: first is Core library, that contains business logic, second is Web GUI. I got an idea, that separated Core library can be used elsewhere (for example desktop application) or GUI can be completely changed later if needed.
Business logic is stored in "managers" which operate with database entities and provide results using data transfer objects, because I do not want to expose db entities outside the Core. Web GUI accepts user input and uses managers to do the job. It also defines view model objects, which are basically copycat from data transfer objects, but enhanced with data annotation attributes.
Web project performs server side validation on view model objects because input from user should be verified. Core project performs same validation again on data transfer objects, because it knows nothing about GUI layer above it. Question is how can I avoid duplicated data validation, because sometimes it requires queries to database and in general adds overhead and duplicated code. What are the best practices in my case? Or the whole idea of separating solution into two projects is wrong?
Solutions I came up to
- Let GUI to perform validation and ignore any in Core.
- Let managers to perform validation and catch raised exceptions in GUI. I've read that this is bad practice[citation-needed].
- Move validation inside the data transfer objects with private "already validated" flag. Provide managers with ValidateDto method which triggers validation on given DTO. Validation results in dictionary of error messages with property names as keys, sets flag, but if already set does nothing. Also mechanism is needed to unset the flag if DTO was changed.
I think I need to clarify: Core is class library, Web is normal ASP.NET MVC application with views, controllers, etc.
All answers so far suggest to put validation into Core class library. So question now is how should I return information about invalid input errors.
Will it be special methods in Core "API" which should be called every time before data are provided to Core. This API will return detailed error information which can be provided to user. This requires user of Core library (programmer) to know about such special behavior (or a lot of coding otherwise, to decide if validation was called by user before or not).
Will it be specially defined exception which will be thrown by Core if input is invalid. This exception will contain detailed errors information.
Thank you all for answers and comments! I ended up with removing all server side validation from Web project and validating only inside Core project. I also created InvalidModelException as user Machado suggested and used Exception.Data property to store all model errors in form of key/value pairs, where key was name of invalid property and value was error code previously defined in Core as enum. Inside Web project I am catching this InvalidModelException exception and once caught I am filling ModelState with model errors to provide better feedback to user. I am storing all user friendly error messages in resource .resx file in Web project. Based on property name and error code from InvalidModelException I am generating ErrorMessageName string to pull out friendly error message from resource file.