Je ne me considère pas comme un expert DDD mais, en tant qu'architecte de solution, j'essaie d'appliquer les meilleures pratiques chaque fois que possible. Je sais qu'il y a beaucoup de discussions autour du pour et du contre du "style" de setter (public) dans DDD et je peux voir les deux côtés de l'argument. Mon problème est que je travaille dans une équipe avec une grande diversité de compétences, de connaissances et d'expérience, ce qui signifie que je ne peux pas croire que chaque développeur fera les choses de la "bonne" manière. Par exemple, si nos objets de domaine sont conçus de sorte que les modifications de l'état interne de l'objet soient effectuées par une méthode mais fournissent des paramètres de propriété publique, quelqu'un définira inévitablement la propriété au lieu d'appeler la méthode. Utilisez cet exemple:
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());
}
}
Ma solution a été de rendre les setters privés privés, ce qui est possible car l'ORM que nous utilisons pour hydrater les objets utilise la réflexion pour pouvoir accéder aux setters privés. Cependant, cela présente un problème lors de l'écriture de tests unitaires. Par exemple, lorsque je veux écrire un test unitaire qui vérifie l'exigence selon laquelle nous ne pouvons pas republier, je dois indiquer que l'objet a déjà été publié. Je peux certainement le faire en appelant deux fois Publish, mais mon test suppose que Publish est correctement implémenté pour le premier appel. Cela semble un peu malodorant.
Rendons le scénario un peu plus réel avec le code suivant:
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));
}
}
Je veux écrire des tests unitaires qui vérifient:
- Je ne peux publier que si le document a été approuvé
- Je ne peux pas republier un document
- Lorsqu'elles sont publiées, les valeurs PublishedBy et PublishedOn sont correctement définies
- Une fois publié, le PublishedEvent est déclenché
Sans accès aux setters, je ne peux pas mettre l'objet dans l'état nécessaire pour effectuer les tests. Ouvrir l'accès aux colons va à l'encontre du but d'empêcher l'accès.
Comment (avez-vous) résolu (d) ce problème?