Должен ли я сопоставить объект домена с моделью представления с помощью необязательного конструктора?

Я хотел бы иметь возможность сопоставить модель домена с моделью представлений, создав модель представления и передав ее в качестве модели (например, код ниже). Моя мотивация заключается в том, чтобы избежать повторного использования кода сопоставления И, чтобы обеспечить простой способ сопоставления (еще не используя automapper). Друг говорит, что модель представления не должна ничего знать о модели домена оплаты, которая передается в необязательный конструктор. Как вы думаете?

public class LineItemsViewModel
{
    public LineItemsViewModel()
    {
    }

    public LineItemsViewModel(IPayment payment)
    {
        LineItemColumnHeaders = payment.MerchantContext.Profile.UiPreferences.LineItemColumnHeaders;
        LineItems = LineItemDomainToViewModelMapper.MapToViewModel(payment.LineItems);
        ConvenienceFeeAmount = payment.ConvenienceFee.Fee;
        SubTotal = payment.PaymentAmount;
        Total = payment.PaymentAmount + payment.ConvenienceFee.Fee;
    }

    public IEnumerable<Dictionary<int, string>> LineItems { get; set; }
    public Dictionary<int, string> LineItemColumnHeaders { get; set; }
    public decimal SubTotal { get; set; }
    public decimal ConvenienceFeeAmount { get; set; }
    public decimal Total { get; set; }
}

Ответ 1

Твой друг прав. Представления должны быть немыми и ничего не знать о вашей модели домена.

Пробовали ли вы использовать Automapper для сопоставления сущностей/моделей вашего бизнеса/домена с вашими dto/viewmodels?

Подробнее из-за комментария:

Помещение кода сопоставления в ваши модели просмотра нарушает разделение беспокойства, принципа единоличной ответственности SOLID, шаблона MVC и принципов проектирования, ориентированных на домен. Взгляды имеют одну ответственность, получают данные на экране, вот и все. ИМХО не о чем спорить. Его просто плохая идея, которая нарушает множество основных разработчиков программного обеспечения.

Ответ 2

Это довольно старый вопрос, для которого ответ уже принят, но для того, чтобы подробно описать предоставленный ответ и ответить на вопросы, возникшие в разделе комментариев, этот дополнительный ответ предоставляется.

Как поставлено, вопрос немного неопределен. Когда его спрашивают, нужно ли что-то делать определенным образом в отношении разработки программного обеспечения, можно понять вопрос, касающийся основополагающих принципов проектирования, которые регулируют рассматриваемую тему, когда такие принципы проектирования должны применяться, если не единообразно, или оба, Чтобы помочь объективно обсуждать эту тему, рассмотрим эти два аспекта в свою очередь.

Конкретная практика создания конструктора для сопоставления значений с одного объекта на другой создает связь между этими двумя объектами. Модель просмотра содержит свойства и/или поведение, относящиеся к определенному виду внутри системы. Поскольку целью такого объекта является моделирование определенного вида, инкапсуляция логики отображения для инициализации внутреннего состояния/значений модели из другого типа в системе означает, что модель представления теперь содержит код, который может потребоваться изменить по причинам кроме изменений в моделировании представления. Такие изменения открывают возможность неблагоприятного воздействия на другие аспекты поведения модели, что приводит к непреднамеренной регрессии в поведении системы. Принцип, определяющий развязку компонентов внутри системы для предотвращения непреднамеренной регрессии поведения путем сочетания множественных проблем, называется принципом единой ответственности.

Вопрос о том, когда такие принципы следует применять, несколько сложнее. Важно помнить, что программное обеспечение, как правило, написано с определенной целью (например, решение некоторых бизнес-задач, облегчение развлечений или образования и т.д.), И то, что лучше всего подходит для любого данного программного обеспечения, относится к задаче. Выбор, сделанный для программной системы, создаваемой для использования в качестве флагманского продукта для конкретной компании, может быть совершенно иным, чем выбор, сделанный для системы, разрабатываемой для решения неотложной проблемы. Необходимо также рассмотреть объем работы, требуемый для облегчения определенных типов развязки. Некоторые способы развязки относительно легко внедряются, в то время как другие могут быть более сложными и даже поставляются с кривыми постепенного обучения для первоначальной реализации, а также для каждого нового разработчика, добавленного в команду, ответственную за обслуживание программного обеспечения. Хотя эвристика не идеальна для принятия таких решений, Test-Driven Development устанавливает эвристику, заключающуюся в том, чтобы не вводить абстракции, пока не будет дублирование. Например, шаблон стратегии - превосходный способ придерживаться принципа "открытый/закрытый", принцип, определяющий дизайн объектов, позволяющих их применение в разных сценариях, без необходимости изменять существующий код. Однако, следуя методам, основанным на испытаниях, нельзя было бы ввести стратегический паттерн до тех пор, пока не будет наблюдаться второй случай использования. Следуя этой эвристике, разработчики вынуждены ограничивать свои усилия своей задачей только написанием кода, необходимого для выполнения задачи без дублирования, что приводит к минимизации отходов и максимизации ремонтопригодности (посредством минимизации сложности).

Тем не менее, разработка программного обеспечения - это и наука, и искусство. Это наука в том, что существуют правила, которые регулируют то, что можно и не может сделать для достижения определенных целей, но это также искусство в том, что вы становитесь лучше, чем больше вы это делаете, и есть определенные компромиссы, которые должны быть сделаны которые в конечном итоге должны быть сделаны субъективно. Например, как разработчик программного обеспечения для клиентов, я, как правило, никогда не участвую в разработке и разработке приложений, которые имеют короткий срок службы. Таким образом, я не жду, пока не увижу дублирование, прежде чем вводить инъекции зависимостей на основе конвенций в свои приложения. Внедрение последовательного использования инъекции зависимостей в приложении имеет гораздо более низкую стоимость в начале жизненного цикла программного обеспечения, чем ожидалось, пока вы не почувствуете необходимость в нем.

Что касается конкретного примера добавления кода сопоставления в модели просмотра, в то время как он соединяет модель представления с конкретной моделью домена, на практике я бы не нашел, что это большая проблема. Модель представления вряд ли будет использоваться с другими моделями домена, а характер типа вводимого кода (т.е. сопоставление) обычно не содержит бизнес-логику, поэтому вероятность этого нарушения SRP, вызывающего значительную регрессию в системе, гораздо меньше, чем нарушение SRP на уровне приложений или доменов.

Тем не менее, я не считаю, что процесс добавления логики отображения в конструкторах может быть какой-то значительной временной заставкой. Если бы кто-то создавал отдельный класс для инкапсуляции сопоставления между объектом домена и моделью просмотра на большинстве языков, мы говорим только о нескольких строках кода. Здесь разница в реализации:

// constructor
public ViewType(DomainType domainType) {
...
}


// mapper class
public class ViewTypeMapper { 
  public ViewType Map(DomainType domainType) {
  ...
  }
}

Итак, вы либо возвращаете новый ViewType (domainType), либо возвращаете новый ViewTypeMapper(). Map (domainType). Я просто не вижу, где развязка в этом случае добавляет значительную работу. В большинстве случаев вы уже потратили время и деньги своей компании или клиента, даже обсудив это, потому что вы всегда будете говорить об этом в течение более длительного периода времени, чем если бы вы просто создали отдельные классы для представления сопоставления, или если вы должны идти вперед и просто настроить Automapper.