Nie uważam się za eksperta DDD, ale jako architekt rozwiązań staram się stosować najlepsze praktyki, gdy tylko jest to możliwe. Wiem, że wokół DDD jest wiele dyskusji na temat przeciwników i przeciwników „stylu” setera no (publicznego) i widzę obie strony argumentu. Mój problem polega na tym, że pracuję w zespole o dużej różnorodności umiejętności, wiedzy i doświadczenia, co oznacza, że nie mogę ufać, że każdy programista zrobi wszystko „we właściwy sposób”. Na przykład, jeśli nasze obiekty domeny są zaprojektowane w taki sposób, że zmiany stanu wewnętrznego obiektu są przeprowadzane za pomocą metody, ale zapewniają ustawiacze właściwości publicznych, ktoś nieuchronnie ustawi właściwość zamiast wywoływać metodę. Skorzystaj z tego przykładu:
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());
}
}
Moim rozwiązaniem było uczynienie ustawień właściwości prywatnymi, co jest możliwe, ponieważ ORM, którego używamy do uwodnienia obiektów, wykorzystuje odbicie, aby mógł uzyskać dostęp do ustawień prywatnych. Stanowi to jednak problem podczas próby napisania testów jednostkowych. Na przykład, gdy chcę napisać test jednostkowy, który weryfikuje wymóg, że nie możemy ponownie opublikować, muszę wskazać, że obiekt został już opublikowany. Z pewnością mogę to zrobić przez dwukrotne wywołanie funkcji Publikuj, ale mój test zakłada, że Publikacja jest poprawnie zaimplementowana dla pierwszego wywołania. To wydaje się trochę śmierdzące.
Uczyńmy scenariusz nieco bardziej realistycznym za pomocą następującego kodu:
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));
}
}
Chcę napisać testy jednostkowe, które weryfikują:
- Nie mogę opublikować, dopóki dokument nie zostanie zatwierdzony
- Nie mogę ponownie opublikować dokumentu
- Po opublikowaniu wartości Opublikowany przez i Opublikowane przez są odpowiednio ustawione
- Po opublikowaniu publikowane jest EventEvent
Bez dostępu do ustawiaczy nie mogę wprowadzić obiektu w stan wymagany do przeprowadzenia testów. Otwarcie dostępu do seterów uniemożliwia dostęp.
Jak rozwiązałeś (d) ten problem?