Validação Fortemente Tipada em Delphi: Apresentando a Fluent Validation API do Dext

A validação de dados é um dos pontos mais críticos de qualquer software. Por anos, desenvolvedores Delphi dependeram de extensos blocos condicionais (if/then) espalhados pelo código ou de strings mágicas para validar dados. O Dext já oferecia uma alternativa robusta com a Validação baseada em Atributos ([Required], [Range]), mas aplicações corporativas complexas demandavam uma flexibilidade ainda maior.
Hoje, temos o prazer de anunciar a conclusão da Fluent Validation API no Dext — um motor de validação baseado em código fluente, fortemente tipado e totalmente integrado com as Smart Properties (Prop<T>) do ORM e com o pipeline de Model Binding Web.
O Limite dos Atributos Declarativos
Seção intitulada “O Limite dos Atributos Declarativos”Embora decorar propriedades com atributos (como [Required] ou [StringLength(3, 50)]) funcione muito bem para cenários simples, a abordagem declarativa apresenta limitações em regras de negócio mais complexas:
- Falta de Condicionais Dinâmicas: Não é simples validar um campo baseado no estado de outro em tempo de execução (ex: “E-mail é obrigatório apenas se o tipo de notificação for por E-mail”).
- Dependência de Strings Mágicas: Validadores customizados costumam exigir os nomes das propriedades em string literals, introduzindo riscos durante refatorações.
- Acoplamento com a Entidade: As validações ficam estaticamente presas à classe de dados, dificultando o uso de regras diferentes para contextos de negócios distintos.
A Solução: Fluent Validation API
Seção intitulada “A Solução: Fluent Validation API”A Fluent Validation API do Dext introduz uma abordagem programática limpa inspirada no FluentValidation do C#. Herdando de TAbstractValidator<T>, você declara as regras de forma fluida no construtor da classe validadora utilizando o método RuleFor.
1. Uso Básico
Seção intitulada “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 executar a validação de forma programática na sua camada de serviço:
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 Avançados
Seção intitulada “Recursos Avançados”1. Integração com Smart Properties (Tipagem Forte sem Strings)
Seção intitulada “1. Integração com Smart Properties (Tipagem Forte sem Strings)”Se as suas entidades usam Smart Properties (Prop<T>, StringType, IntType), você pode eliminar completamente qualquer string literal mapeando as propriedades através de um objeto fantasma gerado pelo 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, o Dext expõe sobrecargas concretas do
RuleForpara os registrosProp<string>,Prop<Integer>, etc. Isso evita conversões implícitas errôneas para tipos básicos vazios e garante que os campos sejam validados em tempo de compilação.
2. Validações Customizadas e Condicionais
Seção intitulada “2. Validações Customizadas e Condicionais”Você pode encadear condicionais .When(...) e asserções baseadas em expressões (Dext Style com Smart Properties) ou métodos anônimos tradicionais:
// --- Estilo Dext (Recomendado - Usando Smart Properties & Expressões) ---// O helper "Model" (ou o alias curto "M") é exposto automaticamente pelo TAbstractValidator<T>
// Asserção customizada diretamente sobre expressões do modeloRuleFor(Model.Active = True).WithMessage('O usuário precisa estar ativo');
// Condicional fluida baseada em expressões fortemente tipadasRuleFor(Model.Email).Required.When(Model.Age >= 18);
// --- Estilo Tradicional (Fallback - Usando Strings e Métodos Anônimos) ---// Útil para projetos sem a infraestrutura de Smart Properties do Dext
// Asserção customizada com 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('O usuário precisa estar ativo');
// Validar e-mail apenas se o usuário for maior de idadeRuleFor('Email').Required.When(function(Model: TUser): Boolean begin Result := Model.Age >= 18; end);3. Registro de Padrões (TValidationPatterns)
Seção intitulada “3. Registro de Padrões (TValidationPatterns)”Em vez de repetir expressões regulares complexas em vários validadores, o Dext fornece um registro centralizado e localizado:
// Registro no StartupTValidationPatterns.Register('PostalCode', '^\d{5}$', 'fr-FR');
// No ValidadorRuleFor(m.PostalCode).MatchesPattern('PostalCode', 'fr-FR');Integração Transparente com a Web
Seção intitulada “Integração Transparente com a Web”O grande diferencial do Fluent Validation no Dext é seu acoplamento automático com o Model Binding do servidor Web:
- Registro no Container de DI: Registre sua classe validadora nos serviços durante o startup:
Services.AddSingleton<IValidator<TUser>, TUserValidator>;
- Auto-Validação: Quando uma requisição HTTP chega em um endpoint mapeado com o parâmetro
TUser, o Dext localiza automaticamente o validador correspondente no contêiner DI e executa a validação antes mesmo do seu método ser invocado. - Retorno Padronizado: Se houver falhas, a execução é interrompida imediatamente, disparando uma
TWebValidationException, o que retorna automaticamente um status HTTP 400 Bad Request contendo um JSON detalhado com os erros específicos de cada campo.
Validação Automática no ORM (Dext Entity)
Seção intitulada “Validação Automática no ORM (Dext Entity)”Além de validar no recebimento de requisições HTTP, o Dext integra a validação de forma nativa ao ciclo de vida de persistência do seu ORM (TDbContext):
- Mesmo Registro de DI: A mesma injeção de dependência registrada no contêiner (
Services.AddSingleton<IValidator<TUser>, TUserValidator>) é aproveitada pelo ORM. - Interceptação no
SaveChanges: Durante a execução deSaveChangesouSaveChangesAnsync, oTDbContextlocaliza automaticamente o validador registrado para cada classe de entidade modificada ou inserida. - Prevenção de Dados Inválidos: A validação roda antes de gerar os comandos SQL de inserção ou atualização no banco de dados. Caso falhe, uma exceção
EValidationExceptioncontendo todas as mensagens de erro é lançada, abortando imediatamente a transação e impedindo dados inválidos de chegarem ao banco.
Qualidade Garantida
Seção intitulada “Qualidade Garantida”Esta implementação foi amplamente validada:
- Testes Unitários: Cobertura completa testando as regras fluentes, condicionais, customizadas e auto-validação em chamadas REST web, com sucesso absoluto.
- Compatibilidade Retroativa: O código foi escrito sem o uso de variáveis inline (
vardeclaradas no meio do bloco) internas nos arquivos do framework, assegurando total compatibilidade do Dext com versões do Delphi anteriores à 10.4.
A validação fluente do Dext está finalizada, limpa e pronta para blindar as regras de negócio do seu domínio.
Links Úteis e Documentação
Seção intitulada “Links Úteis e Documentação”Para aprofundar-se nos detalhes de implementação e conferir outros exemplos práticos de validação no framework, consulte a documentação oficial:
- Documentação de Validação (Dext Book PT-BR): Acesse os Detalhes Técnicos
- Repositório Oficial do Dext: github.com/cesarliws/dext