No me considero un experto en DDD pero, como arquitecto de soluciones, intento aplicar las mejores prácticas siempre que sea posible. Sé que hay mucha discusión sobre los pros y los contras del "estilo" de setter no (público) en DDD y puedo ver ambos lados del argumento. Mi problema es que trabajo en un equipo con una amplia diversidad de habilidades, conocimientos y experiencia, lo que significa que no puedo confiar en que cada desarrollador haga las cosas de la manera "correcta". Por ejemplo, si nuestros objetos de dominio están diseñados para que los cambios en el estado interno del objeto se realicen mediante un método pero proporcionen establecedores de propiedad pública, alguien inevitablemente establecerá la propiedad en lugar de llamar al método. Usa este ejemplo:
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());
}
}
Mi solución ha sido hacer que los establecedores de propiedades sean privados, lo cual es posible porque el ORM que estamos usando para hidratar los objetos usa la reflexión para que pueda acceder a los establecedores privados. Sin embargo, esto presenta un problema al intentar escribir pruebas unitarias. Por ejemplo, cuando quiero escribir una prueba unitaria que verifique el requisito de que no podemos volver a publicar, debo indicar que el objeto ya ha sido publicado. Ciertamente puedo hacer esto llamando a Publicar dos veces, pero luego mi prueba asume que Publicar se implementa correctamente para la primera llamada. Eso parece un poco maloliente.
Hagamos el escenario un poco más real con el siguiente 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));
}
}
Quiero escribir pruebas unitarias que verifiquen:
- No puedo publicar a menos que el documento haya sido aprobado
- No puedo volver a publicar un documento
- Cuando se publican, los valores PublicadosPor y Publicados están establecidos correctamente
- Cuando se publica, se publica el Evento publicado
Sin acceso a los setters, no puedo poner el objeto en el estado necesario para realizar las pruebas. Abrir el acceso a los setters anula el propósito de evitar el acceso.
¿Cómo (tiene) que resuelve (d) este problema?