C# -Você sabe quando usar Value Objects ?
afinal quando usar Value Objects ? |
Ao definir um
modelo de domínio, você cria as entidades definindo as propriedades e métodos.
As propriedades representam o estado interno da entidade e os métodos são as
ações que podem ser executadas. As propriedades geralmente são definidas usando
tipos primitivos, como strings, números, datas e assim por diante.
Mas o que são Value Objects e de onde eles surgiram ?
Os Value Objects ou Objetos de valor representam valores tipados, que não têm identidade conceitual, em seu domínio. Eles podem ajudá-lo a escrever um código melhor, menos sujeito a erros, mais eficiente e mais expressivo.
Os Value Objects são um dos blocos de construção introduzidos no livro Domain-Driven Design escrito por Eric Evans.
O Domain Driven Design - DDD é uma filosofia de desenvolvimento de softwares complexos usando boas práticas na qual a estrutura e a linguagem do seu código (nomes de classe, métodos, variáveis, etc.) devem estar focados no modelo de domínio ou negócio.
No DDD, os Value Objects diferem das entidades por não terem o conceito de identidade. Não nos importamos com quem eles são, mas sim o que são. Eles são definidos por seus atributos e devem ser imutáveis.
Principais características dos Value Objects
Podemos definir 3 principais características dos Value Objects:
1. Igualdade de Valor (Value Equality)
Os Value Objects são definidos por seus atributos. Eles são iguais se
seus atributos forem iguais. Um objeto de valor difere de uma entidade por não
ter um conceito de identidade.
Por exemplo, se considerarmos uma propriedade TempoGasto
como um objeto de valor, um tempo gasto de 60 segundos seria igual à duração de
um minuto, pois o valor subjacente é o mesmo.
Essa característica pode ser mais fácil de implementar em alguns idiomas do que
em outros. No C#, precisamos redefinir os métodos equals e hashCode.
2. Imutabilidade
A imutabilidade é um requisito importante. Os valores de um objeto de valor devem ser imutáveis depois que o objeto for criado. Portanto, quando o objeto é construído, você deve fornecer os valores necessários, mas não deve permitir que eles sejam alterados durante o tempo de vida do objeto.
Uma vez criado, um
objeto de valor deve ser sempre igual. A única maneira de alterar seu valor é
por substituição completa. O que isso significa, no código, é criar uma nova
instância com o novo valor.
Ao implementar um Value Object, precisamos nos certificar de que
removemos todos os setters e que os getters retornam objetos ou
cópias imutáveis para garantir que ninguém possa alterar esses valores
externamente.
3. Auto Validação
Um Value Object deve verificar a validade de seus atributos ao ser criado. Se algum de seus atributos for inválido, o objeto não deve ser criado e um erro ou exceção deve ser gerado. Por exemplo, se tivermos um conceito de Idade, não faria sentido criar uma instância de idade com um valor negativo.
Implementando um Value Object
Podemos usar uma abordagem definindo uma classe base contendo as definições comuns conforme o código abaixo:
public abstract class ValueObject<T> where T : ValueObject<T>
{
public override bool Equals(object obj)
{
var valueObject = obj as T;
if (ReferenceEquals(valueObject, null))
return false;
if (GetType() != obj.GetType())
return false;
return EqualsCore(valueObject);
}
protected abstract bool EqualsCore(T other);
public override int GetHashCode()
{
return GetHashCodeCore();
}
protected abstract int GetHashCodeCore();
public static bool operator ==(ValueObject<T> a, ValueObject<T> b)
{
if (ReferenceEquals(a, null) && ReferenceEquals(b, null))
return true;
if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
return false;
return a.Equals(b);
}
public static bool operator !=(ValueObject<T> a, ValueObject<T> b)
{
return !(a == b);
}
}
|
Neste código temos o tratamento das partes repetidas dos membros de igualdade e o fornecimento de pontos de extensão para classes derivadas para fazer a comparação real e o cálculo do código hash.
Assim uma propriedade Endereco que é definida como Value Object pode derivar desta classe base:
public class Endereco : ValueObject<Endereco> { public string Rua { get; } public string Cidade { get; } public string Cep { get; }
public Endereco(string rua, string cidade, string cep) protected override bool EqualsCore(Endereco other) protected override int GetHashCodeCore() |
Note que a classe base oculta a maior parte do código comum relacionado às operações de comparação.
Nesta abordagem basta declarar dois métodos:
É claro que você pode alterar o código implementando mais responsabilidades.
Assim podemos usar Value Objects nos seguintes cenários:
Agora você tem que considerar que como são objetos imutáveis os Value Objects podem não interagir muito bem com banco de dados, e assim talvez você tenha que relaxar as restrições de imutabilidade ou usar um objeto de transferência de dados(DTO) separado e fazer a conversão entre os objetos o que não é uma solução ideal, pois adiciona ainda mais classes ao seu projeto.
"Porque, se
alguém cuida ser alguma coisa, não sendo nada, engana-se a si mesmo. Mas prove
cada um a sua própria obra, e terá glória só em si mesmo, e não noutro."
Gálatas 6:3,4
Referências:
C# - Tasks x Threads. Qual a diferença
VB .NET - Datas, horas: conceitos e operações
C# - Programação Assíncrona como : Asycn e Task
O tratamento de datas no VB.NET
C# - Obtendo a data e a hora por TimeZone
C# - O Struct Guid - Macoratti.net
C# - Checando Null de uma forma mais elegante e concisa
DateTime - Macoratti.net
Null o que é isso ? - Macoratti.net
Formatação de data e hora para uma cultura ...
C# - Calculando a diferença entre duas datas
NET - Padrão de Projeto - Null Object Pattern
C# - Fundamentos : Definindo DateTime como Null ...
C# - Os tipos Nullable (Tipos Anuláveis)