Я не вважаю себе експертом DDD, але, як архітектор рішення, намагаюся застосовувати кращі практики, коли це можливо. Я знаю, що існує багато дискусій навколо плюсів і мінусів не (публічного) сеттерського "стилю" в DDD, і я можу побачити обидві сторони аргументу. Моя проблема полягає в тому, що я працюю в команді з широким розмаїттям вмінь, знань та досвіду, що означає, що я не можу вірити, що кожен розробник буде робити справи "правильним" шляхом. Наприклад, якщо наші об’єкти домену розроблені таким чином, що зміни у внутрішньому стані об'єкта здійснюються методом, але надають налаштуванням публічної власності, хтось неминуче встановить властивість замість виклику методу. Скористайтеся цим прикладом:
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());
}
}
Моє рішення полягало в тому, щоб зробити налаштування властивостей приватними, що можливо, тому що ORM, який ми використовуємо для гідратації об'єктів, використовує відображення, щоб він мав доступ до приватних установників. Однак це створює проблему при спробі написання одиничних тестів. Наприклад, коли я хочу написати одиничний тест, який підтверджує вимогу, що ми не можемо повторно опублікувати, мені потрібно зазначити, що об’єкт уже опублікований. Я, безумовно, можу це зробити, зателефонувавши Publish двічі, але тоді мій тест передбачає, що Publish реалізований правильно для першого дзвінка. Це здається трохи смердючим.
Давайте зробимо сценарій трохи більш реальним із наступним кодом:
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));
}
}
Я хочу написати одиничні тести, які підтверджують:
- Я не можу публікувати, якщо документ не затверджено
- Я не можу повторно опублікувати документ
- Після публікації значення Pubqy та PublishedOn правильно встановлені
- Коли публікується, PubEvent піднімається
Без доступу до сетерів я не можу поставити об'єкт у стан, необхідний для проведення тестів. Відкриття доступу до сетерів перемагає мету запобігання доступу.
Як ви (вирішили) вирішити цю проблему?