Validación Fuertemente Tipada en Delphi: Presentando la Fluent Validation API de Dext

La validación de datos es uno de los puntos más críticos de cualquier software. Por años, los desarrolladores de Delphi han dependido de extensos bloques condicionales (if/then) dispersos por el código o de strings mágicas para validar datos. Dext ya ofrecía una alternativa robusta con la Validación basada en Atributos ([Required], [Range]), pero las aplicaciones corporativas complejas demandaban una flexibilidad aún mayor.
Hoy, nos complace anunciar la finalización de la Fluent Validation API en Dext — un motor de validación basado en código fluido, fuertemente tipado y totalmente integrado con las Smart Properties (Prop<T>) del ORM y con el flujo de Model Binding Web.
El Límite de los Atributos Declarativos
Sección titulada «El Límite de los Atributos Declarativos»Aunque decorar propiedades con atributos (como [Required] o [StringLength(3, 50)]) funciona muy bien para escenarios simples, el enfoque declarativo presenta limitaciones en reglas de negocio más complejas:
- Falta de Condicionales Dinámicas: No es sencillo validar un campo basado en el estado de otro en tiempo de ejecución (ej. “El correo electrónico es obligatorio solo si el tipo de notificación es Correo”).
- Dependencia de Strings Mágicas: Los validadores personalizados suelen requerir nombres de propiedades como string literals, introduciendo riesgos durante las refactorizaciones.
- Acoplamiento con la Entidad: Las validaciones quedan estáticamente ligadas a la clase de datos, lo que dificulta el uso de reglas diferentes para contextos de negocios distintos.
La Solución: Fluent Validation API
Sección titulada «La Solución: Fluent Validation API»La Fluent Validation API de Dext introduce un enfoque programático limpio inspirado en FluentValidation de C#. Al heredar de TAbstractValidator<T>, usted declara las reglas de forma fluida en el constructor de la clase validadora utilizando el método RuleFor.
1. Uso Básico
Sección titulada «1. Uso Básico»type TUserValidator = class(TAbstractValidator<TUser>) public constructor Create; override; end;
constructor TUserValidator.Create;begin inherited Create;
RuleFor('Name').Required.Length(3, 50); RuleFor('Email').EmailAddress; RuleFor('Age').Range(18, 99);end;Para ejecutar la validación de forma programática en su capa de servicio:
var Validator: TUserValidator; Result: TValidationResult;begin Validator := TUserValidator.Create; try Result := Validator.Validate(UserInstance); try if not Result.IsValid then raise Exception.Create(Result.ErrorMessage); finally Result.Free; end; finally Validator.Free; end;end;Recursos Avanzados
Sección titulada «Recursos Avanzados»1. Integración con Smart Properties (Tipado Fuerte sin Strings)
Sección titulada «1. Integración con Smart Properties (Tipado Fuerte sin Strings)»Si sus entidades utilizan Smart Properties (Prop<T>, StringType, IntType), puede eliminar por completo cualquier string literal mapeando las propiedades a través de un objeto fantasma generado por Prototype:
constructor TOrderValidator.Create;begin inherited Create; var m := Prototype.Entity<TOrder>;
RuleFor(m.CustomerName).Required.Length(3, 100); RuleFor(m.Total).Range(1.0, 10000.0);end;[!NOTE] Internamente, Dext expone sobrecargas concretas de
RuleForpara los registrosProp<string>,Prop<Integer>, etc. Esto evita conversiones implícitas erróneas a tipos básicos vacíos y garantiza que los campos se validen en tiempo de compilación.
2. Validaciones Personalizadas y Condicionales
Sección titulada «2. Validaciones Personalizadas y Condicionales»Puede encadenar condicionales .When(...) y aserciones basadas en expresiones (Dext Style con Smart Properties) o métodos anónimos tradicionales:
// --- Estilo Dext (Recomendado - Usando Smart Properties y Expresiones) ---// El helper "Model" (o el alias corto "M") se expone automáticamente en TAbstractValidator<T>
// Aserción personalizada directamente sobre expresiones del modeloRuleFor(Model.Active = True).WithMessage('El usuario debe estar activo');
// Condicional fluido basado en expresiones fuertemente tipadasRuleFor(Model.Email).Required.When(Model.Age >= 18);
// --- Estilo Tradicional (Fallback - Usando Strings y Métodos Anónimos) ---// Útil para proyectos sin la infraestructura de Smart Properties de Dext
// Aserción personalizada con método anónimoRuleFor('Active', function(Model: TUser): TValue begin Result := Model.Active; end).Must(function(Val: TValue): Boolean begin Result := Val.AsBoolean = True; end).WithMessage('El usuario debe estar activo');
// Validar correo electrónico solo si el usuario es mayor de edadRuleFor('Email').Required.When(function(Model: TUser): Boolean begin Result := Model.Age >= 18; end);3. Registro de Patrones (TValidationPatterns)
Sección titulada «3. Registro de Patrones (TValidationPatterns)»En lugar de repetir expresiones regulares complejas en varios validadores, Dext proporciona un registro centralizado y localizado:
// Registro en StartupTValidationPatterns.Register('PostalCode', '^\d{5}$', 'fr-FR');
// En el ValidadorRuleFor(m.PostalCode).MatchesPattern('PostalCode', 'fr-FR');Integración Transparente con la Web
Sección titulada «Integración Transparente con la Web»El gran diferencial de Fluent Validation en Dext es su acoplamiento automático con el Model Binding del servidor Web:
- Registro en el Contenedor de DI: Registre su clase validadora en los servicios durante el startup:
Services.AddSingleton<IValidator<TUser>, TUserValidator>;
- Auto-Validación: Cuando una solicitud HTTP llega a un endpoint mapeado con el parámetro
TUser, Dext localiza automáticamente el validador correspondiente en el contenedor DI y ejecuta la validación antes de que se invoque su método. - Respuesta Estandarizada: Si hay fallos, la ejecución se interrumpe inmediatamente, lanzando una
TWebValidationException, lo que devuelve automáticamente un estado HTTP 400 Bad Request con un JSON detallado que contiene los errores específicos de cada campo.
Validación Automática en el ORM (Dext Entity)
Sección titulada «Validación Automática en el ORM (Dext Entity)»Además de validar al recibir solicitudes HTTP, Dext integra la validación de forma nativa en el ciclo de vida de persistencia de su ORM (TDbContext):
- Mismo Registro de DI: La misma inyección de dependencia registrada en el contenedor (
Services.AddSingleton<IValidator<TUser>, TUserValidator>) es aprovechada por el ORM. - Interceptación en
SaveChanges: Durante la ejecución deSaveChangesoSaveChangesAsync, elTDbContextlocaliza automáticamente el validador registrado para cada clase de entidad modificada o insertada. - Prevención de Datos Inválidos: La validación se ejecuta antes de generar los comandos SQL de inserción o actualización en la base de datos. Si falla, se lanza una excepción
EValidationExceptioncon todos los mensajes de error, abortando inmediatamente la transacción e impidiendo que los datos inválidos lleguen a la base de datos.
Calidad Garantizada
Sección titulada «Calidad Garantizada»Esta implementación ha sido ampliamente validada:
- Pruebas Unitarias: Cobertura completa probando las reglas fluidas, condicionales, personalizadas y auto-validación en llamadas REST web, con absoluto éxito.
- Compatibilidad con Versiones Anteriores: El código interno del framework fue escrito sin el uso de variables inline (
vardeclaradas en medio del bloque), lo que garantiza la compatibilidade total de Dext con versiones de Delphi anteriores a la 10.4.
La validación fluida de Dext está finalizada, limpia y lista para proteger las reglas de negocio de su dominio.
Enlaces Útiles y Documentación
Sección titulada «Enlaces Útiles y Documentación»Para profundizar en los detalles de implementación y revisar otros ejemplos prácticos de validación en el framework, consulte la documentación oficial:
- Documentación de Validación (Dext Book PT-BR): Acceda a los Detalles Técnicos
- Repositorio Oficial de Dext: github.com/cesarliws/dext