sexta-feira, 9 de outubro de 2009

DDD: Como validar entidades de negócio

Eu respondi uma pergunta sobre como é realizada as validações de entidade de negócio no DDD.
No mesmo post eu coloquei um exemplo obtido no livro do Tim McCarthy.

No exemplo abaixo é gerado uma exceção caso a entidade não esteja válida.


public sealed class Address
{
private string street;
private string city;
private string state;
private string postalCode;

public Address(string street, string city, string state, string postalCode)
: this(street, city, state, postalCode, true)
{
}

private Address(string street, string city, string state, string postalCode,
bool validate)
{
this.street = street;
this.city = city;
this.state = state;
this.postalCode = postalCode;
if (validate)
{
this.Validate();
}
}

private void Validate()
{
if (string.IsNullOrEmpty(this.street)
string.IsNullOrEmpty(this.city)
string.IsNullOrEmpty(this.state)
string.IsNullOrEmpty(this.postalCode))
{
throw new InvalidOperationException("Invalid address.");
}
}
}

Já no exemplo abaixo é verificado se a lista de regras quebradas esta preenchida, caso ela esteja vazia a entidade esta ok.
public class Company : EntityBase
{
private string name;
private string abbreviation;
private Address headquartersAddress;
private List<Address> addresses;
private string phoneNumber;
private string faxNumber;
private string url;
private string remarks;

public Company()
: this(null)
{
}

public Company(object key)
: base(key)
{
this.name = string.Empty;
this.abbreviation = string.Empty;
this.headquartersAddress = null;
this.addresses = new List<Address>();
this.phoneNumber = string.Empty;
this.faxNumber = string.Empty;
this.url = string.Empty;
this.remarks = string.Empty;
}

public string Name
{
get { return this.name; }
set { this.name = value; }
}

protected override void Validate()
{
// The Company must have a name
if (!string.IsNullOrEmpty(this.name))
{
this.BrokenRules.Add(new BrokenRule("", ""));
}
}
public ReadOnlyCollection<BrokenRule> GetBrokenRules()
{
this.Validate();
return this.brokenRules.AsReadOnly
}
}

Mas os exemplos acima demonstram uma forma mais "genérica" de como realizar essa validação. Por que genérica? Por que podemos ter vários tipos de validações para uma mesma entidade dentro de processos específicos.

Imagine por exemplo que para cadastrar um cliente você não precise do CPF, mas para finalizar um processo de compra essa informação passe a ser necessário, se você chamar o método "Validate" da classe cliente com o CPF em branco ele vai dizer que esta válido, mas para o processo de compra isso não é verdade.

As validações colocadas na própria entidade de negócio, geralmente são utilizadas nos processos de CRUD para garantir que todos os campos obrigatórios estejam preenchidos e de forma adequada. Mas cada processo de negócio necessita de informações específicas de cada entidade, por tanto, ele deve se preocupar em garantir essas informações.

Na prática o que eu quero dizer com isso? Que se o processo precisa do campo CPF do cliente, o processo (ou um serviço referente ao processo) é responsável em verificar se o CPF esta preenchido e não o cliente.

Conclusão, nossos métodos referentes a processos como o de compra, por exemplo, devem fazer mais do que simplesmente persistência de banco de dados (Insert e Update) mas também
garantir com que as entidades estejam com estado adequado.

Nenhum comentário: