Может ли "богатая модель домена" нарушать принцип единой ответственности?
Интересный поток появился, когда я набрал этот вопрос только сейчас. Я не думаю, что он отвечает на мой вопрос.
Я много работал с .NET MVC3, где желательно иметь анемичную модель. Просмотр моделей и моделей редактирования лучше всего использовать в качестве немых контейнеров данных, которые вы можете просто передать с контроллера на представление. Любые потоки приложений должны поступать от контроллеров, а представления обрабатывают пользовательский интерфейс. В MVC мы не хотим никакого поведения в модели.
Однако мы не хотим, чтобы в контроллерах не было никакой бизнес-логики. Для более крупных приложений лучше всего сохранить код домена отдельно от моделей, представлений и контроллеров и независимо от них (и, в общем, HTTP в этом случае). Таким образом, существует отдельный проект, который прежде всего предоставляет модель домена (с объектами и объектами ценности, составленными в агрегаты в соответствии с DDD).
Я сделал несколько попыток отойти от анемичной модели к более богатому в доменном коде, и я собираюсь отказаться. Мне кажется, что классы объектов, которые содержат данные и поведение, нарушают SRP.
Возьмем, к примеру, один очень распространенный сценарий в Интернете, составляющий электронные письма. Принимая во внимание какое-то событие, ответственность за создание объекта EmailMessage лежит на домене с учетом электронной почты, электронной почты и пользовательских значений. Шаблон существует как объект со свойствами, а пользовательские значения предоставляются в качестве ввода пользователем. Позвольте также сказать ради аргумента, что адрес EmailMessage FROM может быть предоставлен внешней службой (IConfigurationManager.DefaultFromMailAddress). Учитывая эти требования, кажется, что модель с богатым доменом может дать EmailTemplate ответственность за составление EmailMessage:
public class EmailTemplate
{
public EmailMessage ComposeMessageTo(EmailAddress to,
IDictionary<string, string> customValues, IConfigurationManager config)
{
var emailMessage = new EmailMessage(); // internal constructor
// extension method
emailMessage.Body = this.BodyFormat.ApplyCustomValues(customValues);
emailMessage.From = this.From ?? config.DefaultFromMailAddress;
// bla bla bla
return emailMessage;
}
}
Это была одна из моих попыток в богатой доменной модели. Однако, добавив этот метод, ответственность за EmailTemplate заключалась в том, чтобы содержать свойства данных сущности и составлять сообщения. Это было около 15 строк в длину и, казалось, отвлекало класс от того, что на самом деле означает быть EmailTemplate, и это значит, что IMO просто хранит данные (формат темы, формат тела, вложения и необязательный из/ответ на адреса).
Я закончил рефакторинг этого метода специальным классом, который несет ответственность за составление EmailMessage с учетом предыдущих аргументов, и я гораздо счастливее с ним. Фактически, я начинаю предпочитать анемичные домены, потому что это помогает мне разделять обязанности, делая классы и модульные тесты короче, более краткими и более целенаправленными. Похоже, что создание объектов и других объектов данных "лишенных поведения" может быть хорошим для разделения ответственности. Или я здесь не здесь?
Ответы
Ответ 1
Аргумент в пользу модели с богатым доменом вместо анемической модели зависит от одного из значений предложений ООП, которое поддерживает поведение и данные рядом друг с другом. Основным преимуществом является инкапсуляция и сплоченность, которая помогает в рассуждении о коде. Богатую модель домена можно также рассматривать как экземпляр информационного эксперта. Однако ценность всех этих моделей в значительной степени субъективна. Если вам удобнее хранить данные и поведение отдельно, то пусть будет так, хотя вы можете также рассмотреть других людей, которые будут смотреть на код. Я предпочитаю инкапсулировать как можно больше. Другим преимуществом более богатой модели домена в этом случае будет возможность сделать некоторые свойства частными. Если свойство используется только одним методом для класса, зачем публиковать его?
Является ли модель богатого домена нарушением СРП, зависит от вашего определения ответственности. Согласно SRP, ответственность является причиной изменения, которая сама требует определения. Это определение, как правило, зависит от используемого варианта использования. Вы можете заявить, что ответственность за класс шаблона должна быть шаблоном со всеми вытекающими последствиями, одна из которых генерирует сообщение из шаблона. Изменение одного из свойств шаблона может повлиять на метод ComposeMessageTo
, который указывает, что, возможно, это одна ответственность. Более того, метод ComposeMessageTo
является наиболее интересной частью шаблона. Клиентам шаблона не важно, как реализован метод или какие свойства присутствуют в классе шаблона. Они хотят генерировать сообщение только на основе шаблона. Это также отвечает за сохранение данных рядом с методом.
Ответ 2
Ну, это зависит от того, как вы хотите посмотреть на него.
Другой способ: "Может ли принцип единой ответственности нарушать модель богатого домена?"
Оба являются рекомендациями. В разработке программного обеспечения нет "принципа". Есть, однако, хорошие конструкции и плохие конструкции. Обе эти концепции могут использоваться по-разному, чтобы достичь хорошего дизайна.