Validando regras de negócio em C# com specification pattern
Quem nunca se perdeu com as dezenas de regras de negócio de um projeto que atire a primeira pedra!
Muitas vezes por conta do projeto possuir uma regra de negócio complexa e com muitas validações, acabamos nos perdendo no meio do código e deixando-o mal organizado, e até tornamos pior a legibilidade dele.
O que queríamos deixar fácil acaba se tornando algo maçante e desanimador, tanto para nós, quanto para o próximo a mexer naquele pedaço de código.
Há algum tempo atrás o grande MVP e hoje diretor na Microsoft, Eduardo Pires, me trouxe a tona um tal de Specification pattern que ajudou DEMAIS a reorganizar todas aquelas regras.
A ideia deste pattern é deixar o código bem mais atômico (vide conceito de SOLID), ajudando-nos a reutilizar código e tornar bem mais legível e intuitiva as nossas validações de regras de negócio.
Mas chega de papo e vamos ver um pouco de código!
Considere uma entidade Pessoa -Person
:
Precisamos validar algumas informações antes de inseri-las em nosso Afinal, é melhor validar do que deixar estourar uma exception por parte do banco de dados, não?
Além disso, deixar sempre por conta do nosso front-end pra validar essas informações pode ser um tanto ruim, já que ainda hoje é bem possível burlar alguns formulários.
Vamos montar nosso método Add
que vai inserir uma pessoa no nosso banco de dados, pra isso vamos validar dentro do próprio método:
Perceba que precisamos de vários ifs para validar se nossa entidade está OK, desde validações pra identificar se não está nulo, até algumas mais especificas como: Tamanho do campo CPF em 11 e ser um número (poderíamos usar regex ali também).
Funciona certo? Mas… e se usarmos o specification pattern com a biblioteca DomainValidation desenvolvida pelo Eduardo Pires? O que conseguimos melhorar?
Começamos baixando o pacote diretamente pelo NuGet explorer:
E agora vamos criar nossas especificações. Basicamente elas vão ser exatamente os nossos ifs do código anterior:
E com elas fazemos nossa validação, deixando nossa service da seguinte maneira:
Assim deixamos nosso método muito mais limpo e conseguimos cumprir o conceito de atomicidade do SOLID, podendo reutilizar nossas specifications em qualquer outra validação!!
Cumprimos nosso objetivo, mas vamos mais além!
Como bônus quero refatorar essas nossas especificações, pois é ruim ficar com várias delas só pra fazer um string.IsNullOrWhiteSpace()
não?
Então bora fazer de um jeito bem mais fácil usando lambda expressions:
Reduzimos pra apenas duas (e totalmente reutilizáveis) especificações!!
E se olharmos a classe IsPersonValid
ainda podemos entender tranquilamente o que estamos validando.
Generics e lambda expression são sensacionais!