Bulletproofing .NET Applications with FluentValidation

Photo by Ilya Pavlov on Unsplash

Bulletproofing .NET Applications with FluentValidation

What is FluentValidation

FluentValidation is a .NET library that builds strict and maintainable validation rules. It utilises the principles of "fluent interfaces" to provide a clean architecture, separating validation logic from the rest of the code. Centralising validation logic ensures data integrity and reliability across all layers of an application and simplifies development and maintenance.

FluentValidation Advantages

  • Easy to maintain and improves code readability.

  • Allows customisation of validation to be specific to business rules.

  • Validations are reusable across different parts of the application.

FluentValidation Disadvantages

  • FluentValidation is difficult for newcomers to understand and implement.

  • FluentValidation complicates the validation process for small-scale projects.

  • Operates on rule chaining, and extensive chaining will slow down the application's performance.

Data Validation Rule

Data validation checks that the input data into a system meets specific formats and constraints before being processed into the application's functionalities. FluentValidation facilitates validation through a set of rules applied to any property model. The framework allows the chaining of multiple rules for a single property to create complex validation logic in a readable format.

Below is the CreateUser class that represents a request to create a new user. It implements the IRequest<TResponse> from MediatR, indicating that once the request is processed, a CreateUserResponse will be generated, i.e., a UserId will be created.

This is a validator that I have implemented for my Roster project. The CreateValidation class inherits from the AbstractValidator and takes CreateUser as the type. This means that CreateUserValidator it will validate instances of the CreateUser class according to the specified rules.

In FluentValidation, the subsequent rule for the same property will be executed when a rule fails. However, this CascadeMode.stop prevents a single property's overload of validation errors.

The RuleFor method defines validation rules for the properties of CreateUser, the model, using x => x.FirstName. The NotEmpty() effectively makes it a required field and Matches(@"^[a-zA-Z'\s]+$"), ensuring the name doesn't include numbers or special characters, adhering to a standard naming convention.

Business Rule Validation

Business rules are in place to enforce the business requirements of the application. The rules ensure system functionality aligns with the specified business logic and constrain how records are created, updated and deleted. Below is a snapshot of some business rules I have implemented for my Roster project on making shifts.

The CreateShiftValidator class inherits from the abstract validator and takes the type, meaning CreateShift instances will be validated accordingly. A RosterContext is injected into the constructor and assigned to the read-only me field _context. As stated before, CascadeMode.stop it stops a single property from overloading validation errors. The code then checks whether the RosterId & UserId are greater than zero. The Must() method with boolean methods can determine if those records exist in the database with _context. The Must() method is a popular FluentValidation library for implementing complex and custom validation logic, ensuring data models adhere to business rules. Before creating a shift, the remaining requests are checked against the business rules.

The boolean method takes the request, payload information and validation context. A variable roster with the specified RosterId can access all information, including the shifts related to that roster. As you can see from the code, these business validations are more complex than simple data validations, and they specifically test the business logic. For example, a shift is considered valid if the shift's start and end dates are the same. The code validates this by checking that the dates are not equal, which raises a failure, returning an error message to the user that the dates must match. These are just some business validations, and if no errors occur, the requests are passed to the handler, which processes the information to create a shift.

Summary

Validation is a crucial aspect of software development, ensuring that data entered into a system adheres to specific rules and constraints. FluentValidation, a powerful .NET library, facilitates simple and complex validation logic through a fluent interface. This interface focuses on validation logic, making it easier to read, write, and maintain while also enabling the creation of custom rules that enforce specific business logic. Although FluentValidation may have a steep learning curve, add complexity to smaller projects, and potentially slow down performance with extensive rule chaining, its flexibility and reusability make it invaluable. FluentValidation benefits developers and end-users by ensuring applications function smoothly and reliably, meeting data integrity and business needs.