Considerando que, quando falamos de programação, grande parte do código desenvolvido pelos desenvolvedores são equivalentes a validações de dados, nesse artigo buscarei abordar maneiras de aplicar conceitos de qualidade de código tais quais como reutilização, redução de código, e código limpo através da biblioteca light-validate em camadas client/server que utilizam Typescript como linguagem de programação.
Sobre o Light Validate
https://www.npmjs.com/package/light-validate
Light Validate é uma biblioteca que permite ao desenvolvedor, criar validações de dados, e utilizar as mesmas em mapeamento de classes modelo através de Annotations / Decorators para automatizar a rotina de validação de dados diversos, permitindo uma implementação semelhante as implementações já utilizadas nas linguagens Java através do HIBERNATE/SPRINGDATA, ou C# através do EntityFramework ou bibliotecas semelhantes.
Algumas características importantes:
- Agnóstico a Framework.
- Paradigma Funcional.
- Paradigma Assíncrono.
Conceitual
Criando Validações (Light Rules)
As Validações que podem ser interpretadas pelo Light Validate, são basicamente funções que implementam a interface Light Rule.
Interface LightRule
A interface LightRule representa uma Função void que recebe 2 parâmetros, que são value, e target.
- Parâmetro Value, esse parâmetro representa o valor que será validado.
- Parâmetro Target, esse parâmetro representa o objeto que contem o parâmetro value.
Caso o valor a ser validado seja inválido, a função deve lançar via throw um código, ou mensagem de erro.
Caso o valor a ser validado seja válido, a função não deve lançar e nem retornar nada.
Criando Mapeamentos (Light Mapping)
Os Mapeamentos que são interpretados pelo Light Validate, são basicamente classes, com decorators LightValidate em decorando as propriedades cujo os valores deverão ser validados.
Decorator LightValidate
O Decorator LightValidate é basicamente uma função que recebe como parâmetros, as validações que deverão validar a propriedade cujo é decorada com o decorator LightValidate.
Validando Objetos
Para validar um objeto, o Light Validate disponibiliza algumas funções, que basicamente são funções que recebem como parâmetros, target e Klass, e retornam uma promise.
Caso haja algum erro, a promise será rejeitada com um vetor objetos que implementam a interface LightException
- Parâmetro Target, esse parâmetro representa o objeto que será validado.
- Parâmetro Klass, esse parâmetro representa o mapeamento que deverá ser utilizado para a validação do parâmetro Target.
Interface Light Exception
A Interface apresenta 4 propriedades, visando representar dados da exceção gerada pela validação, que são as seguintes :
- rule , A Rule que gerou a exceção.
- target, O Objeto que foi validado e gerou a exceção.
- property, A propriedade cujo o valor gerou a exceção.
- code, O Código lançado pela Rule como exceção.
Função validate
A função validate, é utilizada para validar apenas 1 ou vários objetos a partir de um mapeamento.
Função validateOne
A função validate, é utilizada para validar apenas 1 objeto a partir de um mapeamento.
Função validateEach
A função validate, é utilizada para validar vários objetos a partir de um mapeamento.
Na prática
Vamos considerar o seguinte conceito para mostrar na prática como a biblioteca funciona :
Você deve criar uma tela que efetua cadastro de Alunos em uma Instituição Educacional.
O Modelo Aluno, que será enviado para o servidor, é representado pela seguinte interface :
/* student.model.ts */
export interface StudentModel {
name:string;
document:string;
birthDate:string;
phone:string;
state:string;
country:string;
}
Considerar que o requisitante do sistema especifica que os campos devem possuir as seguintes validações
- name : não deve possuir caráteres numéricos, deve possuir ao menos 3 caracteres, e no máximo 40 carácteres.
- document : deve possuir 11 carácteres.
- birthDate : deve possuir o formato ##/##/####.
- phone : deve permitir número de telefone de 9 dígitos.
- state : deve possuir apenas um dos seguintes valores : São Paulo, Minas Gerais ou Rio de Janeiro, New York, Hollywood, Kansas
- country : deve possuir apenas um dos seguintes valores : Brasil para caso o campo state seja equivalente a São Paulo, Minas Gerais ou Rio de Janeiro, e United States para os demais.
Instalação
Executar os seguintes comandos para a instalação da biblioteca :
$ npm install -save light-validate
$ npm install -save reflect-metadata
Criando Validações (Light Rules)
Campo Name: NameLightRule
/* name.light-rule.ts */
import { LightRule } from 'light-validate';
export const NameLightRule:LightRule = async function (value:string, target:any) {
if(value){
if(!new RegExp("/^[A-Za-z\s]+$/").test(value)){
throw 'apenas-caracteres-alfabeticos';
}
if(value.length<3) {
throw 'ao-menos-3-caracteres';
}
if(value.length>40) {
throw 'ao-maximo-40-caracteres';
}
}
}
Campo Document: DocumentLightRule
/* document.light-rule.ts */
import { LightRule } from 'light-validate';
export const DocumentLightRule:LightRule = async function (value:string, target:any) {
if(value) {
if(value.length!=11) {
throw 'deve-possuir-11-numeros';
}
}
}
Campo BirthDate: BirthDateLightRule
/* birth-date.light-rule.ts */
import { LightRule } from 'light-validate';
export const BirthDateLightRule:LightRule = async function (value:string, target:any) {
const values = value.split('/');
if(values.length!=3) {
throw 'data-invalida';
}
if(values[0].length==2) {
throw 'data-invalida';
}
if(values[1].length==2) {
throw 'data-invalida';
}
if(values[2].length==4) {
throw 'data-invalida';
}
}
Campo Phone: PhoneLightRule
/* phone.light-rule.ts */
import { LightRule } from 'light-validate';
export const PhoneLightRule:LightRule = async function (value:string, target:any) {
if(value.length!==9){
throw 'telefone-invalido';
}
}
Campo State: StateLightRule
/* state.light-rule.ts */
import { LightRule } from 'light-validate';
export const StateLightRule:LightRule = async function (value:string, target:any) {
if(['São Paulo','Minas Gerais','Rio de Janeiro','New York','Hollywood','Kansas'].indexOf(value)===-1){
throw 'estado-invalido';
}
}
Campo Country: CountryLightRule
/* country.light-rule.ts */
import { LightRule } from 'light-validate';
export const CountryLightRule:LightRule = async function (value:string, target:any) {
if(value==='Brasil' && ['São Paulo','Minas Gerais','Rio de Janeiro'].indexOf(target['state'])===-1){
throw 'pais-invalido';
}
if(value==='United States' && ['New York','Holywood','Kansas'].indexOf(target['state'])===-1){
throw 'pais-invalido';
}
}
Criando Mapeamentos (Light Mapping)
Abaixo , a classe que representará o mapeamento de validações para validar objetos que sigam o formato da mesma.
/* student.light-mapping.ts */
import { StudentModel } from './student.model';
import { NameLightRule } from './name.light-rule';
import { DocumentLightRule } from './document.light-rule';
import { BirthDateLightRule } from './birth-date.light-rule';
import { PhoneLightRule } from './phone.light-rule';
import { StateLightRule } from './state.light-rule';
import { CountryLightRule } from './country.light-rule';
import { LightValidate } from 'light-validate';
export class StudentLightMapping implements StudentModel {
/*
Todas as propriedades que forem decoradas com o decorator @LightValidate
devem ser inicializadas com undefined, ou null, caso o contrário, serão ignoradas.
*/
@LightValidate(NameLightRule)
public name:string = undefined;
@LightValidate(DocumentLightRule)
public document:string = undefined;
@LightValidate(BirthDateLightRule)
public birthDate:string = undefined;
@LightValidate(PhoneLightRule)
public phone:string = undefined;
@LightValidate(StateLightRule)
public state:string = undefined;
@LightValidate(CountryLightRule)
public country:string = undefined;
}
Validando Objetos
import { StudentModel } from './student.model';
import { StudentLightMapping } from './student.light-mapping';
import { validate } from 'light-validate';
const model: StudentModel = {
name:'João da Silva Santos',
document:'444111222',
birthDate:'2019-12-12',
phone:'111133',
state:'New York',
country:'Brazil'
}
validate(model,StudentLightMapping)
.then(()=>console.log('Nenhuma exceção foi encontrada'))
.catch((exceptions:LightException[])=>
exceptions.forEach((exception:LightException)=>
console.log(`Exceção gerada pela Validação ${exception.rule}, utilizando como objeto alvo o objeto ${e.target}, validando o valor da propriedade ${exception.property}, com o código lançado equivalente a ${e.code}`)
)
)
);
Criando Validações Reutilizáveis
Como podem ver, todas as validações que criei nesse guia, podem ser otimizadas e separadas de maneira que possam ser reutilizadas, como por exemplo:
NameLightRule quebrada em 3 validações :
OnlyAlphaLightRule, que valida um campo que deve receber apenas caracteres alfabéticos.
/* only-alpha.light-rule */
import { LightRule } from 'light-validate';
export const OnlyAlphaLightRule:LightRule = async function (value:string, target:any) {*/
if(value){
if(!new RegExp("/^[A-Za-z\s]+$/").test(value)){
throw 'apenas-caracteres-alfabeticos';
}
}
}
MinLengthLightRule, que valida um campo que deve receber um valor que tenha no mínimo X carácteres.
/* min-length.light-rule */
export const MinLengthLightRule:LightRule = function(length:number) {
return async function (value:string, target:any) {*/
if(value && value.length<length) {
throw `ao-menos-${length}-caracteres`;
}
}
}
MaxLengthLightRule, que valida um campo que deve receber um valor que tenha no máximo X carácteres.
/* max-length.light-rule */
export const MaxLengthLightRule:LightRule = function(length:number) {
return async function (value:string, target:any) {*/
if(value && value.length>length) {
throw `ao-maximo-${length}-caracteres`;
}
}
}
Dessa forma, já conseguimos eliminar 3 validações específicas, através da utilização de 3 validações genéricas, que podem ser reutilizadas em diversas situações.
import { StudentModel } from './student.model';
import { OnlyAlphaLightRule } from './only-alpha.light-rule';
import { MinLengthLightRule } from './min-length.light-rule';
import { MaxLengthLightRule } from './max-length.light-rule';
import { BirthDateLightRule } from './birth-date.light-rule';
import { StateLightRule } from './state.light-rule';
import { CountryLightRule } from './country.light-rule';
import { LightValidate } from 'light-validate';
export class StudentLightMapping implements StudentModel {
/*
Todas as propriedades que forem decoradas com o decorator @LightValidate
devem ser inicializadas com undefined, ou null, caso o contrário, serão ignoradas.
*/
@LightValidate(OnlyAlphaLightRule,MinLengthLightRule(3),MaxLengthLightRule(40))
public name:string = undefined;
@LightValidate(MinLengthLightRule(11),MaxLengthLightRule(11))
public document:string = undefined;
@LightValidate(BirthDateLightRule)
public birthDate:string = undefined;
@LightValidate(MinLengthLightRule(9),MaxLengthLightRule(9))
public phone:string = undefined;
@LightValidate(StateLightRule)
public state:string = undefined;
@LightValidate(CountryLightRule)
public country:string = undefined;
}
Utilizando com Frameworks SPA
A biblioteca Light Validate possuí algumas abstrações para alguns frameworks de mercado que fornecem suporte para Typescript, que são elas :
São basicamente, módulos de diretivas que se comunicam com a biblioteca Light Validate para executar validações, e exibir as exceções de forma semelhante ao Jquery Validate .
Top comments (1)
Ótimo artigo, muito útil.