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.
Subscribe to my newsletter
Read articles from Nabidul Islam directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Nabidul Islam
Nabidul Islam
Hey, I am Nabidul. I am a Software Engineering student at the University of Technology Sydney (UTS). I love gaming with my friends, going to the gym, trying new foods & exploring new places. My passion for programming started in high school, and I decided to pursue a career in Software Engineering.