0

Padronizando Value Objects

#.NET C#
T
Thiago Oliveira

Value Object

Caso você não saiba, Value Object é um conceito muito utilizado dentro do DDD(Domain Driven Design) que é uma abordagem para modelagem de software. Dentro desta abordagem nós temos as Entidades, que explicando de forma sucinta, são objetos que possuem uma identidade, ou seja, pode ser um cliente, produto etc.


Caso nós queiramos criar uma propriedade CPF dentro da entidade Pessoa, talvez possamos ficar tentados a fazer isto usando o tipo string, entretanto, para validar o CPF, uma alternativa seria criar um método dentro da entidade Pessoa, mas e se precisarmos adicionar uma propriedade CPF em outra entidade? Poderíamos criar uma classe 'utils' que possui um método de validação, entretanto, sempre precisaríamos lembrar de em todo momento que recebermos uma string que representa um CPF usar esta classe 'utils' para validar. Neste ponto é que entra o Value Object.


Value Object é um objeto simples que por princípio é imutável, sua igualdade é dada por valor e não por referência, além de saber validar a si mesmo. Sendo assim, nós podemos criar uma classe CPF que guarda uma string e possui um método para validar a si, fazendo isso ganhamos a possibilidade de reutilizar esta classe onde for necessário.


ValueOf

Pensando nisso, eu e meu amigo Ricardo Augusto Vicentini, idealizamos e criamos um projeto open-source que visa padronizar a criação de Value Objects, onde ao referenciar o pacote nuget ValueOfLib e herdar da classe ValueOf você precisará adequar seu objeto aos requisitos mínimos para se ter um Value Object válido, como por exemplo, precisará escrever um método Equals que determine como seu objeto identificará uma igualdade, e terá que escrever um método para validar o valor do seu objeto. Com essas poucas implementações você terá um objeto que ao ser criado valida a si, e guarda o estado de sua validade para que você possa tomar suas decisões.


Observe o exemplo abaixo:

Não foi fornecido texto alternativo para esta imagem

Neste exemplo, podemos ver um Value Object que representa as temperaturas, ele herda de ValueOf informando que seu valor será uma Tupla (double Celsius, double kelvin, double Fahrenheit) e referenciando ele mesmo como segundo parâmetro. Temos um construtor que recebe graus Celsius e informa para o construtor base a Tupla que deseja armazenar. Logo abaixo temos um método Equals que está informando o critério de igualdade ao acessar a propriedade Value que armazena a Tupla informada no construtor. Mais abaixo temos o método Validate que possui os critérios para considerar este valor válido ou inválido.


Abaixo segue um exemplo de como seria a utilização:

Não foi fornecido texto alternativo para esta imagem

Na imagem acima podemos ver que por herdar de ValueOf, nosso Value Object possui a propriedade IsValid que nos informa a validade deste objeto, caso a instância seja inválida, ao tentar acessar a propriedade Value será lançada uma exceção ValueObjectInvalidException.


ValueOf com FluentValidation

O exemplo de temperatura possuí uma validação simples e de fácil implementação, para Value Objects mais complexos com múltiplas validações criamos também a extensão ValueOfLib.FluentValidation que permite a criação das validações com AbstractValidation da lib FluentValidation.


Essa dependência nos permitirá criar validações complexas de forma fluída e com mais expressividade, facilitando o entendimento do código e assim reduzindo custos futuros na manutenção dessas validações. Com essa extensão nós mostramos também como é possível estender o projeto ValueOf para atender suas próprias especificidades.


Veja no exemplo abaixo:

Não foi fornecido texto alternativo para esta imagem

Acima nós temos a classe TemperaturaValidator herdando de AbstractValidator que é o padrão para validação com FluentValidation. Depois temos nosso Value Object Temperatura agora herdando de ValueOfWithFluentValidation.WithNotifications e seguindo uma estrutura parecida, informando qual será seu valor, a si mesmo e a classe que será responsável por fazer sua validação. Com isso podemos ver que não precisamos escrever o método Validate, uma vez que quem irá validar este objeto é a classe TemperaturaValidator.


Abaixo um exemplo da utilização:

Não foi fornecido texto alternativo para esta imagem

Com essa abordagem, além de termos acesso a propriedade IsValid, também temos acesso a propriedade ValidationResult que é o resultado da validação feita através do FluentValidation, podendo assim, acessar a lista de erros encontrados.

Caso você prefira disparar exceções de validações do Fluent no lugar das mensagens de erro, você pode estender seu Value Object com ValueOfWithFluentValidation.WithExceptions.


Olhemos o exemplo abaixo:

Não foi fornecido texto alternativo para esta imagem

A implementação do Value Object não muda muito, a não ser que ao invés de herdar da especialização WithNotifications nós herdamos de WithExceptions.


Abaixo um exemplo da utilização:

Não foi fornecido texto alternativo para esta imagem

No exemplo acima não me preocupei em checar a propriedade IsValid, porque uma vez que a validação do objeto falhar será lançada uma exceção do tipo ValidationException.

Desta forma, deixamos a lib bem flexível para se trabalhar tanto com notifications quanto com exceptions. Fica o convite para que você venha contribuir com este projeto, sugerir melhorias e estende-lo para suas próprias necessidades.


Abaixo alguns links sobre o projeto:

Repositório no github: https://github.com/ThiagoAcam/ValueOf

Documentação: ValueOf e ValueOf.FluentValidation

Nuget: ValueOfLib e ValueOfLib.FluentValidation

Demos: ValueOfValueOfWithFluentValidation.WithNotifications e ValueOfWithFluentValidation.WithExceptions

Testes das demos: ValueOfValueOfWithFluentValidation.WithNotifications e ValueOfWithFluentValidation.WithExceptions

0
3

Comentários (0)

None

Brasil