-
Notifications
You must be signed in to change notification settings - Fork 10
Validations
Monaco implements its validation mechanism by leveraging FluentValidations, a library that helps us to encapsulate the validation logic in separate classes to decouple it from the caller and keep our application logic much cleaner.
As seen previously, Monaco also leverages MediatR to implement the commands execution mechanism, which allows us to also leverage its pipeline behaviors as a sort of middleware which in turn helps us better control the execution flow.
For making use of this mechanism, it is enough to declare our commands as derived classes of CommandBase
or CommandBase<T>
, and then have the validators inherit from AbstractValidator<TCommand>
as below:
public class CompanyCreateCommand : CommandBase<Guid>
{
//Removed for brevity
}
public sealed class CompanyCreateCommandValidator : AbstractValidator<CompanyCreateCommand>
{
//Removed for brevity
}
The DI mechanism that configures the Application will detect all the classes derived from the CommandBase
classes and will register them along with their corresponding validator.
IMPORTANT: whenever a command is inheriting from CommandBase
/CommandBase<T>
, it is required to also declare its corresponding validator. Otherwise the DI registration will throw an exception during the app startup and will prevent it from running.
All CommandBase
-derived commands will return an ICommandResult
interface, which includes the property ValidationResult ValidationResult { get; set; }
that contains whether the validation process resulted valid or not, and all the fields failed to validate, along with their respective validation errors. This data will be populated automatically by Monaco's built-in pipeline behaviors that form part of this validation mechanism.
Monaco uses FluentValidations for 2 types of validations:
-
Regular validations: validations intended for checking if a command has valid data to continue running its handler, like for example: that certain fields are not null or empty, or that some of them don't exceed a given length, etc. In a REST API these validations will normally end up in a
400 - Bad Request
response with a list of the fields with their respective validation errors. -
Element existance validations: validations intended for checking that an element that is supposed to exist for being edited/deleted actually exists on the system or in the context of the user accessing it. Examples of this are when a product is being edited with a
PUT
method on a REST API; if the product doesn't exist (or if it's out of user's permitted data scope), a typical response should be a404 - Not Found
as a result of the request execution.
For the first type of validations, there's nothing special to do about. Just declare the validator and write the rules or rulesets required to perform the command validation.
For the second type of validations, Monaco's built-in pipeline behaviors will automatically check if a ruleset named Exists
has failed validation or not. In this case, the only extra thing required to fulfill this requirement is to include this ruleset on the validator with a single internal rule to perform the validation.
Because this is a very repetitive task in most of the cases, Monaco offers several extension methods in ValidatorsExtensions
that can be used on an implemented validator as long as the command being validated inherits from CommandBase
or CommandBase<T>
:
this.CheckIfExists<CompanyEditCommand, Domain.Model.Company>(dbContext);
This will check if there is any record on Company
by using the Id
from CompanyEditCommand
.
this.CheckIfExists((cmd, id, ct) => dbContext.ExistsAsync<Site>(x => cmd.CustomerId == x.CustomerId && x.Id == id, ct));
This will check if there is any record for Site
that complies with the predicate condition taking into account some other param coming along in the command.
These and other overloads of the CheckIfExists
extension method are available to leverage on different scenarios according to your needs and to take advantage of all the possible overloads FluentValidation's MustAsync
validator has to offer.
- Index
- Getting started
- Architecture overview
-
Solution/Projects structure
- Solution structure
-
Projects structure
Monaco.Template.Common.Api
Monaco.Template.Common.Api.Application
Monaco.Template.Common.ApiGateway
Monaco.Template.Common.Application
Monaco.Template.Common.BlobStorage
Monaco.Template.Common.Domain
Monaco.Template.Common.Infrastructure
Monaco.Template.Common.Messaging.Messages
Monaco.Template.Common.Serilog
Monaco.Template.Common.Tests
Monaco.Template.Application.Tests
Monaco.Template.Domain.Tests
Monaco.Template.Api
Monaco.Template.Application
Monaco.Template.Application.Infrastructure
Monaco.Template.Application.Infrastructure.Migrations
Monaco.Template.Domain
- Features/Slices
- Validations
- Application configuration
- Template options
- Examples
[Wiki still in construction]