Non mi considero un esperto di DDD ma, come architetto di soluzioni, cerco di applicare le migliori pratiche ogni volta che è possibile. So che ci sono molte discussioni sui pro e contro dello "stile" di setter (pubblico) in DDD e posso vedere entrambi i lati dell'argomento. Il mio problema è che lavoro in un team con una grande diversità di competenze, conoscenze ed esperienze, il che significa che non posso fidarmi che ogni sviluppatore farà le cose nel modo "giusto". Ad esempio, se i nostri oggetti di dominio sono progettati in modo tale che le modifiche allo stato interno dell'oggetto vengano eseguite da un metodo ma forniscano setter di proprietà pubbliche, qualcuno inevitabilmente imposterà la proprietà invece di chiamare il metodo. Usa questo esempio:
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());
}
}
La mia soluzione è stata quella di rendere privati i setter di proprietà, il che è possibile perché l'ORM che stiamo usando per idratare gli oggetti usa la riflessione in modo da poter accedere ai setter privati. Tuttavia, ciò presenta un problema quando si tenta di scrivere unit test. Ad esempio, quando voglio scrivere un unit test che verifica il requisito che non possiamo ripubblicare, devo indicare che l'oggetto è già stato pubblicato. Posso certamente farlo chiamando Pubblica due volte, ma poi il mio test presuppone che Pubblica sia implementato correttamente per la prima chiamata. Sembra un po 'puzzolente.
Rendiamo lo scenario un po 'più reale con il seguente codice:
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));
}
}
Voglio scrivere unit test che verificano:
- Non posso pubblicare se il documento non è stato approvato
- Non riesco a ripubblicare un documento
- Quando pubblicati, i valori PublishedBy e PublishedOn sono impostati correttamente
- Quando viene pubblicato, il PublishedEvent viene generato
Senza accesso ai setter, non posso mettere l'oggetto nello stato necessario per eseguire i test. L'apertura dell'accesso ai setter vanifica lo scopo di impedire l'accesso.
In che modo (hai) risolvi (d) questo problema?