Não me considero um especialista em DDD, mas, como arquiteto de soluções, tento aplicar as melhores práticas sempre que possível. Eu sei que há muita discussão em torno dos prós e contras do "estilo" de setter (público) no DDD e posso ver os dois lados do argumento. Meu problema é que trabalho em uma equipe com uma ampla diversidade de habilidades, conhecimentos e experiência, o que significa que não posso confiar que todo desenvolvedor fará as coisas da maneira "certa". Por exemplo, se nossos objetos de domínio forem projetados para que as alterações no estado interno do objeto sejam executadas por um método, mas forneçam configuradores de propriedade pública, alguém inevitavelmente definirá a propriedade em vez de chamar o método. Use este exemplo:
public class MyClass
{
public Boolean IsPublished
{
get { return PublishDate != null; }
}
public DateTime? PublishDate { get; set; }
public void Publish()
{
if (IsPublished)
throw new InvalidOperationException("Already published.");
PublishDate = DateTime.Today;
Raise(new PublishedEvent());
}
}
Minha solução foi tornar privados os setters de propriedades, o que é possível porque o ORM que estamos usando para hidratar os objetos usa reflexão, para que ele possa acessar os setters privados. No entanto, isso apresenta um problema ao tentar escrever testes de unidade. Por exemplo, quando eu quero escrever um teste de unidade que verifique o requisito de que não podemos republicar, preciso indicar que o objeto já foi publicado. Certamente, posso fazer isso chamando Publish duas vezes, mas meu teste pressupõe que o Publish foi implementado corretamente na primeira chamada. Isso parece um pouco fedido.
Vamos tornar o cenário um pouco mais real com o seguinte código:
public class Document
{
public Document(String title)
{
if (String.IsNullOrWhiteSpace(title))
throw new ArgumentException("title");
Title = title;
}
public String ApprovedBy { get; private set; }
public DateTime? ApprovedOn { get; private set; }
public Boolean IsApproved { get; private set; }
public Boolean IsPublished { get; private set; }
public String PublishedBy { get; private set; }
public DateTime? PublishedOn { get; private set; }
public String Title { get; private set; }
public void Approve(String by)
{
if (IsApproved)
throw new InvalidOperationException("Already approved.");
ApprovedBy = by;
ApprovedOn = DateTime.Today;
IsApproved = true;
Raise(new ApprovedEvent(Title));
}
public void Publish(String by)
{
if (IsPublished)
throw new InvalidOperationException("Already published.");
if (!IsApproved)
throw new InvalidOperationException("Cannot publish until approved.");
PublishedBy = by;
PublishedOn = DateTime.Today;
IsPublished = true;
Raise(new PublishedEvent(Title));
}
}
Quero escrever testes de unidade que verifiquem:
- Não posso publicar a menos que o Documento tenha sido aprovado
- Não consigo republicar um documento
- Quando publicados, os valores PublishedBy e PublishedOn são definidos corretamente
- Quando publicado, o PublishedEvent é gerado
Sem acesso aos setters, não consigo colocar o objeto no estado necessário para executar os testes. A abertura do acesso aos levantadores anula o objetivo de impedir o acesso.
Como você resolveu esse problema?