Я не считаю себя экспертом 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));
}
}
Я хочу написать модульные тесты, которые проверяют:
- Я не могу опубликовать, если документ не был утвержден
- Я не могу повторно опубликовать документ
- Когда опубликовано, значения ОпубликованоBy и ОпубликованоOn установлены правильно
- Когда опубликовано, ОпубликованоEvent повышается
Без доступа к установщикам я не могу перевести объект в состояние, необходимое для выполнения тестов. Открытие доступа к сеттерам сводит на нет цель предотвращения доступа.
Как (есть) вы решили (d) эту проблему?