Validación Unificada en Cliente y Servidor con Net.Extensions.FormsValidation

Un enfoque desacoplado para la validación de datos en aplicaciones .NET multiplataforma


🎯 Planteamiento del problema

En los entornos industriales, financieros, sanitarios y logísticos actuales, el software .NET sigue ocupando un rol esencial. Sin embargo, la gestión de la validación de datos dentro de estas aplicaciones arrastra una serie de deficiencias estructurales heredadas del diseño de capas acopladas. Las validaciones suelen estar:

Ver en Github
Ver en NuGet


  • Distribuidas de forma inconsistente: reglas de validación fragmentadas entre la UI, los servicios y las entidades.
  • Acopladas a frameworks de interfaz (WinForms, Razor, XAML...): difícilmente testeables o reutilizables.
  • Redundantes y propensas a errores: mismas reglas escritas múltiples veces, con diferentes niveles de exigencia.

Implicaciones reales en la industria

  • Aumento de la deuda técnica y pérdida de control validacional en sistemas legados.
  • Frágil portabilidad entre capas y tecnologías (ej. pasar un modelo validado en WPF a una API).
  • Alto costo de mantenimiento en validaciones dispares.
  • Pruebas limitadas: imposible validar lógica de entrada sin levantar todo el sistema.

La validación, al ser una preocupación transversal (cross-cutting concern), debe abstraerse en una unidad coherente y desacoplada del resto del sistema. Aquí es donde entra en juego Net.Extensions.FormsValidation.


🔬 Propuesta: Un modelo validacional declarativo, aislado y reutilizable

Net.Extensions.FormsValidation introduce una arquitectura validacional basada en principios sólidos de ingeniería de software:

  • Separación de responsabilidades (SRP): cada validación vive fuera del dominio de presentación.
  • Programación funcional y declarativa: definición fluida de reglas como composición de funciones.
  • Inversión de control (IoC): el motor de validación es invocado de forma explícita y desacoplada.
  • Compatibilidad multiplataforma: aplicable en WinForms, WPF, Blazor, ASP.NET Core, APIs REST, MAUI y más.

Este modelo permite diseñar reglas de validación fuertes, coherentes y reutilizables sin depender del tipo de presentación ni de los patrones arquitectónicos (MVVM, MVP, MVC, etc.).

🔐 Ventaja clave: el mismo conjunto de validaciones puede ejecutarse en el cliente (frontend) y en el servidor (backend) sin duplicar lógica, asegurando una doble validación coherente, segura y mantenible. A diferencia de otros frameworks, que obligan a replicar o traducir reglas entre capas (como ocurre con DataAnnotations en Blazor o ASP.NET), aquí las validaciones son portables de forma nativa. Esto representa una ventaja competitiva directa y única.


🧱 Componentes fundamentales

  • FormValidator: coordinador central de campos y resultados.
  • Validator<T>: unidad fluida para la definición de reglas para cualquier tipo de dato.
  • ValidationRule<T>: encapsula lógica específica con semántica reutilizable.
  • ValidationResult: representa el estado de validez, gravedad y mensaje.
  • Extensiones: set funcional que ofrece reglas ya listas (Required, Regex, Email, etc.).

🧪 Ejemplo técnico básico

var validator = new FormValidator();

var nameValidator = new Validator<string>()
    .Required("Nombre obligatorio")
    .MinLength(3)
    .MaxLength(50);

validator.AddFieldValidator("Nombre", () => textBoxNombre.Text, nameValidator);

var result = validator.Validate();
if (!result.IsValid)
    MessageBox.Show(result.Message);

Este patrón puede replicarse en cualquier capa o stack tecnológico, lo que habilita la validación centralizada.


🧩 Malas prácticas que resuelve

Mala práctica Solución proporcionada por el framework
Validación en if imperativos Validación declarativa, encadenada y composable.
Reglas duplicadas en UI y backend Reutilización de validadores en diferentes capas.
Dependencia de frameworks UI Aislamiento completo: los validadores no conocen el entorno.
Poca trazabilidad de errores Resultados explícitos con mensajes estructurados.
Tests acoplados a formularios Validadores independientes, testeables con mocks y expresiones lambda.
Falta de validación coherente Validaciones homogéneas entre cliente y servidor sin duplicación.

🔍 Catálogo de reglas disponibles

Textos (string)

  • .Required()
  • .MinLength(), .MaxLength()
  • .Email(), .Regex(), .UrlRule()
  • .Contains(), .StartsWith(), .EndsWith(), .NotContains()

Números (int, decimal, etc.)

  • .GreaterThan(), .LessThan()
  • .EqualTo(), .Range()

Fechas (DateTime)

  • .PastDateRule(), .FutureDateRule()
  • .DateRange()

Colecciones (ICollection<T>, arrays, listas)

  • .NotEmpty()
  • .MinCount(), .MaxCount()
  • .AnyMatch(), .AllMatch()

Custom rules :

Definición de reglas personalizadas con IValidationRule<T>

Además de las reglas incorporadas, el framework permite definir reglas específicas mediante la implementación directa de IValidationRule<T>. Esto permite expresar validaciones de negocio complejas o muy específicas, encapsuladas de forma reutilizable y con control total de la lógica.

Ejemplo completo

public class CustomRule<T> : IValidationRule<T>
{
    private readonly Func<T, bool> _predicate;
    private readonly string _message;
    private readonly ValidationSeverity _severity;

    public CustomRule(Func<T, bool> predicate, string message, ValidationSeverity severity = ValidationSeverity.Error)
    {
        _predicate = predicate ?? throw new ArgumentNullException(nameof(predicate));
        _message = message ?? throw new ArgumentNullException(nameof(message));
        _severity = severity;
    }

    public ValidationResult Validate(T value)
    {
        if (!_predicate(value))
            return ValidationResult.Fail(_message, _severity);

        return ValidationResult.Success();
    }
}

Integración

Las reglas personalizadas pueden integrarse dentro de un Validator<T> como cualquier otra:

var customValidator = new Validator<string>()
    .AddRule(new CustomRule<string>(s => s.StartsWith("INV-"), "Debe empezar con 'INV-'"));

Ventajas clave

  • Control total sobre el comportamiento de validación.
  • Inyectabilidad de lógica personalizada sin modificar el framework.
  • Alta expresividad para casos de negocio no cubiertos por reglas genéricas.

Este mecanismo potencia el uso del framework no solo en validaciones de formularios, sino también en validaciones de dominio, reglas fiscales, políticas de seguridad o verificaciones contractuales.

Compatibilidad tecnológica

Implementado de forma agnóstica, el framework puede integrarse en:

  • WinForms: textBox.Text() => textBox.Text
  • WPF: TextBox.Text() => MyViewModel.Propiedad
  • Blazor: @bind-Value="model.Campo"() => model.Campo
  • ASP.NET Core MVC: model.Propiedad() => model.Propiedad
  • Backends o APIs: validaciones independientes de la UI.

Esta arquitectura permite validar los mismos datos tanto antes del envío como al recibirlos, con la misma instancia de Validator<T>, fortaleciendo así el control validacional extremo a extremo.


⚙️ Ventajas técnicas y científicas

  • Modelado unificado de reglas con semántica consistente.
  • Evita acoplamientos estructurales, facilitando la escalabilidad del sistema.
  • Reduce el coste de refactor en proyectos legacy.
  • Soporte para pruebas unitarias en cualquier fase del ciclo de desarrollo.
  • Facilita la gobernanza validacional en arquitecturas distribuidas o modulares.
  • Portabilidad bidireccional entre cliente y servidor, sin pérdida de semántica ni reglas.

🧭 Roadmap científico-técnico

  • ✅ Validaciones asincrónicas para fuentes externas (bases de datos, APIs...)
  • ✅ Agrupación de campos y validaciones jerárquicas
  • ✅ Mensajes localizables por recurso
  • ✅ Soporte DataAnnotations y transición progresiva desde validaciones legacy

📦 Instalación (próximamente en NuGet)

dotnet add package JoeDevSharp.Net.Extensions.FormsValidation

🧑‍🔬 Conclusión

Net.Extensions.FormsValidation ofrece una solución robusta y sistemática al problema universal de la validación desacoplada. Resuelve de raíz las malas prácticas comunes en soluciones .NET legacy y modernas, aportando orden, coherencia y escalabilidad a los sistemas que lo integran.

Recomendado para desarrolladores, arquitectos y equipos que buscan rigor, eficiencia y trazabilidad en sus validaciones, tanto del lado del cliente como del servidor, sin duplicar esfuerzos.