sexta-feira, 23 de outubro de 2009

Modelo anêmico realmente é ruim?

Eu sempre costumo dizer para o pessoal que se você não enxerga o problema não tem por que aplicar uma solução.

O mesmo vale para o modelo anêmico, se você não consegue enxergar os problemas dessa modelagem com certeza você não vê valor nenhum nas outras formas de modelar o domínio.

Com certeza você vai dizer que conhece aplicações desenvolvidas com modelo anêmico e que funcionam muito bem, e com certeza isso é verdade, da mesma forma que conheço aplicações procedurais que funcionam muito bem. Então o que nos motiva a não ter modelos anêmicos?

Quando você decide pelo modelo anêmico você tem que estar ciente que você esta abrindo mão de vários benefícios que o OO pode lhe proporcionar. Fowler diz o seguinte:

“The fundamental horror of this anti-pattern is that it's so contrary to the basic idea of object-oriented design; which is to combine data and process together. The anemic domain model is really just a procedural style design, exactly the kind of thing that object bigots like me (and Eric) have been fighting since our early days in Smalltalk. What's worse, many people think that anemic objects are real objects, and thus completely miss the point of what object-oriented design is all about.”

http://martinfowler.com/bliki/AnemicDomainModel.html

Ele reforça a idéia de que o modelo anêmico não é OO, podemos ver essa afirmação na prática trazendo o clássico exemplo do carro para o mundo do modelo anêmico. Quando você modela um carro geralmente você faz algo assim:


public class Carro
{
private int _posicaoAtual;

public void Andar()
{
this._posicaoAtual += 10;
}
}

Agora no mundo anêmico o carro ficaria assim:

public class Carro
{
private int _posicaoAtual;

public int PosicaoAtual
{
get{ return this._posicaoAtual;}
set{ this._posicaoAtual = value;}
}
}

public class BLCarro
{
publicvoid Andar(Carro carro)
{
carro.PosicaoAtual += 10;
}
}


Perceba que agora criamos um “andador de carro” isso faz algum sentido? Creio que não.
Mas se o modelo anêmico é um anti-padrão, por que temos o DTO (Data Transfer Object Pattern)?

Modelo anêmico não tem nada haver com DTO, eu falo que DTO é um mal necessário, ele foi criado para realizar transporte de dados, não para transporte entre as camadas lógicas como se tem feito com os modelos anêmicos.

Um fator que motiva a utilização do modelo anêmico é o acesso a dados, mas acredite esse problema tem solução, você pode utilizar o Repository ou Active Record.

Active Record with Castle
http://www.castleproject.org/activerecord/index.html

Using Repository and Unit of Work patterns with Entity Framework 4.0

http://blogs.msdn.com/adonet/archive/2009/06/16/using-repository-and-unit-of-work-patterns-with-entity-framework-4-0.aspx

NHibernate and the Unit of Work Pattern
http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/04/10/nhibernate-and-the-unit-of-work-pattern.aspx

Outro fator que confunde um pouco a cabeça do pessoal é que se tem um caminho fixo para se chegar ao banco de dados com o modelo anêmico sendo UI > BL > DL utilizando o modelo o anêmico para trafegar os dados entre a camada, com uma entidade como fica esse caminho?

Pra não fugir do costume vou soltar a frase “cada caso é um caso” você pode por exemplo utilizar um façade para ter acesso ao domínio. Ex.: UI > Façade > Domínio.

sexta-feira, 9 de outubro de 2009

Personalizando o toString em enums

Os enumerados são, sem dúvida, grandes ferramentas para auxiliar e padronizar o desenvolvimento de aplicativos. Eu, por exemplo, o considero excelente para sumir com os famosos números mágicos (magic numbers) dentro das entidades, ex:


//Antes
productionOrder.State = 3;
//Depois
productionOrder.State = ProductionOrderState.Canceled;

Porém, uma desvantagem do enum é quando você quer exibir o texto do valor contido no enum. Ex.:
ProductionOrderState productionOrderState = ProductionOrderState.Canceled;
Console.Writeline(productionOrderState);
//Print: Canceled


Como fazer para imprimir "Production Order is Canceled" no lugar de Canceled?
Abaixo vai uma dica bem simples de como fazer isso. Primeiro copie o código abaixo:
public struct Described<T> where T : struct
{

private T _value;

public Described(T value)
{
_value = value;
}

public override string ToString()
{
string text = _value.ToString();
object[] attr = typeof(T).GetField(text).GetCustomAttributes(typeof(DescriptionAttribute), false);

if (attr.Length == 1)
text = ((DescriptionAttribute)attr[0]).Description;

return text;
}

public static implicit operator Described<T>(T value)
{
return new Described<T>(value);
}

public static implicit operator T(Described<T> value)
{
return value._value;
}
}
Fonte: Post StackOverFlow (http://stackoverflow.com/questions/796607/how-do-i-override-tostring-in-c-enums)
Agora no seu enum implemente o atributo Description, do namespace System.ComponentModel em cada item do enum:
public enum ProductionOrderState
{
[Description("Production Order is Open")]
Open,
[Description("Production Order is Canceled")]
Canceled
}

Para obter a descrição do valor do enum faça o seguinte:
Described<ProductionOrderState> status = ProductionOrderState.Open;
Console.WriteLine(status);
//Print: Production Order is Open

Veja que a classe Described não influencia em nada o funcionamento do enum, veja o teste abaixo:

Described<ProductionOrderState> status = ProductionOrderState.Open;
Console.WriteLine(status);
ProductionOrderState other = status;
Console.WriteLine(other);

Mas ainda não é o suficiente certo? A descrição esta hard code como vamos fazer quando tiver que alterar a descrição? E para multi idioma como ficaria?

Para resolver esse problema crie um Resource no seu projeto e coloque nele as descrições que precisa para o seu enum.

resource

Para obter o valor do Resource nos items do enum podemos utilizar uma classe disponível no Enterprise Library (http://msdn.microsoft.com/en-us/library/cc467894.aspx), faça referência no seu projeto a dll Microsoft.Practices.EnterpriseLibrary.Configuration.Design, dentro dela você contará com o atributo SRDescription.

Troque no seu enum o attribute Description pelo SRDescription informando o nome do Resource que você criou e o nome da chave do resource que você criou, ex:
public enum ProductionOrderState
{
[SRDescription("Sts_Open", typeof(Resources))]
Open,
[SRDescription("Sts_Canceled", typeof(Resources))]
Canceled
}

Prontinho, agora temos disponível um meio prático de obter e personalizar as descrições dos nossos enums.

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.