Использование бизнес-объектов в моделях представления

11

При использовании бизнес-объектов многократного использования, что считается наилучшей практикой при построении моделей представлений?

Мы используем объект, который мы вызываем, Builderдля построения наших моделей представлений. Один конструктор для каждой логической единицы представлений (заказов, пользователей и т. Д.), Где каждая единица может содержать несколько различных моделей представлений (заказы содержат сводку, строки заказа и т. Д.).

Разработчик может провести данные через один или несколько стандартных бизнес-объектов, чтобы построить модель представления.

Что считается лучшей практикой, когда речь идет об использовании бизнес-объектов / моделей в моделях представления?

Подход 1

Разрешить использование бизнес-объектов в модели представления?

//Business object in some library
public class Order
{
    public int OrderNum;
    public int NumOrderLines;
    //...
}

//Order builder in website
public class OrderBuilder
{
    public OrderSummary BuildSummaryForOrder(int OrderNum)
    {
        Some.Business.Logic.Order obOrder = Some.Business.Logic.GetOrder(OrderNum);
        //Any exception handling, additional logic, or whatever

        OrderSummary obModel = new OrderSummary();
        obModel.Order = obOrder;

        return obModel;
    }
}

//View model
public class OrderSummary
{
    public Some.Business.Logic.Order Order;
    //Other methods for additional logic based on the order
    //and other properties
}

Подход 2

Взять только необходимые данные из бизнес-объектов

//Business object in some library
public class Order
{
    public int OrderNum;
    public int NumOrderLines;
    //...
}

//Order builder in website
public class OrderBuilder
{
    public OrderSummary BuildSummaryForOrder(int OrderNum)
    {
        Some.Business.Logic.Order obOrder = Some.Business.Logic.GetOrder(OrderNum);
        //Any exception handling, additional logic, or whatever

        OrderSummary obModel = new OrderSummary()
        {
            OrderNum = obOrder.OrderNum,
            NumOrderLnes = obOrder.NumOrderLines,
        }

        return obModel;
    }
}

//View model
public class OrderSummary
{
    public int OrderNum;
    public int NumOrderLines
    //Other methods for additional logic based on the order
    //and other properties
}

Я вижу преимущества и недостатки обоих, но мне интересно, есть ли приемлемый подход? В подходе 1 нет дублирования кода вокруг моделей, но это создает зависимость от бизнес-логики. При подходе 2 вы берете только данные, необходимые для представления, но дублируете код вокруг моделей.

Энди Хант
источник

Ответы:

12

Вариант 1 создает тесную связь между моделью домена и представлением. Это противоречит самой проблемной модели, предназначенной для решения.

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

Я бы всегда поддерживал подход 2. Часто случается, что модели представлений могут выглядеть очень похожими, даже идентичными объектам модели предметной области, но различие, как я упомянул, заключается в их различных причинах изменений.

MattDavey
источник
Правильно ли я считаю, что под «причиной изменения» вы подразумеваете изменение в смысле обслуживания, а не изменение в смысле обновления (например, события пользовательского интерфейса)?
Энди Хант
@AndyBursh Да, это правильно - см. Эту статью , особенно строку «Роберт С. Мартин определяет ответственность как причину изменения, и приходит к выводу, что у класса или модуля должна быть одна и только одна причина для изменения».
MattDavey
Мне нравится ваш ответ, но некоторые мысли ... Модель представления не обязательно меняется только потому, что модель меняется. Только если вы связываете или используете конкретное свойство, которое изменилось, это будет проблемой, поскольку вы ссылаетесь на весь объект. Наличие ссылки на объект домена облегчает внесение изменений и сохранение их снова. Ваши методы сохранения также зависят от объекта домена, поэтому вам придется преобразовать модель представления обратно или настроить свой бизнес-метод для принятия моделей представления, что тоже не хорошо. Я все еще думаю, что № 2 имеет больше смысла, но только на два цента.
KingOfHypocrites
Если вы не можете иметь доменные объекты в виртуальной машине, то как бы вы представили нечто более сложное, например, массив Orders?
Джефф
Значит ли это, что такие вещи, как, скажем, форматирование отметки времени для отображения пользователем, должны принадлежать слою представления, а не слою домена, а объекты уровня домена должны возвращать только необработанную, неформатированную отметку времени объектам представления, а последние должны содержать логику форматера?
The_Sympathizer
2

Вариант 1 предпочтительнее, поскольку позволяет избежать дублирования кода. Вот и все.

Если модель предметной области существенно меняется, то почти наверняка придется изменить представление. При выборе варианта 2 вам необходимо изменить модель представления и конструктор, а также само представление. Такие вещи являются абсолютным ядом для обслуживания. YAGNI.

Смысл наличия отдельной модели представления состоит в том, чтобы сохранять состояние, которое имеет смысл только для представления (например, какая вкладка выбрана в настоящее время), отдельно от бизнес-модели. Но сами бизнес-данные следует использовать повторно, а не дублировать.

Майкл Боргвардт
источник
YAGNI - секретный убийца, решающий большинство задач разработки программного обеспечения.
Мартин Блур,
6
Извините, но это ужасный совет для всех, кроме самых тривиальных приложений. Просмотр моделей не имеет состояния. Это объекты передачи данных. То, что выбрана вкладка, является частью СТРУКТУРЫ представления и НИЧЕГО не имеет никакого отношения к ДАННЫМ в модели представления. Обслуживание не является кошмаром, если вы правильно структурируете свою программу и используете что-то вроде Automapper для увлажнения ваших моделей представлений.
Люцифер Сэм
«Если модель предметной области значительно изменится, то почти наверняка придется изменить представление». - Согласовано. Но что делать, когда у вас есть небольшие изменения в домене? При первом варианте каждое небольшое изменение в домене (даже просто переименование свойства) требует соответствующего изменения представления. Это также абсолютный яд для ремонтопригодности.
MattDavey
@MattDavey: если вы переименовываете свойство, то с помощью отдельной модели представления вы также должны изменить представление (или любое другое сопоставление между доменом и моделью представления) и теперь иметь два разных имени для одной и той же вещи, что наверняка вызовет путаницу.
Майкл Боргвардт
@ Люцифер Сэм: очевидно, у нас совершенно разные представления о том, что такое модель представления. Ваш звучит очень, очень странно для меня, как будто вы описываете приложения для мэйнфреймов для тупых терминалов, но, конечно, не для современных веб-приложений или приложений для толстых клиентов.
Майкл Боргвардт
2

Принципы и мантры иногда ценны для руководства дизайном ... но вот мой практический ответ:

Представьте, что ваши модели представлений сериализуются в JSON или XML. Если вы попытаетесь сериализовать свои доменные модели, вы в конечном итоге получите отвратительный текстовый беспорядок и, скорее всего, столкнетесь с проблемами с циклическими ссылками и другими проблемами.

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

В идеале ваша модель представления должна состоять почти полностью из предварительно отформатированных строк. Подумайте об этом ... вам даже не нужен DateTime или десятичное число в вашей модели представления, потому что вы застряли, делая логику форматирования в C #, Javascript, Objective-C и т. Д.

Люцифер Сэм
источник
2
У меня никогда не было проблем с сериализацией моделей доменов. И преобразовать все в строки в модели? Шутки в сторону?
Майкл Боргвардт
3
@MichaelBorgwardt Да, это то, чем должна быть модель представления. Вы не хотите сериализовать свои доменные модели и отправлять их повсюду. Вся бизнес-логика должна оставаться в безопасности дома в одном месте. Однако представления должны быть гибкими и могут отображаться на любом устройстве, поэтому вы хотите полностью отделить вашу СТРУКТУРУ, ДАННЫЕ и СТИЛЬ.
Люцифер Сэм
Извините, но это ужасный совет для любого приложения, точка. Это приводит к тому, что приложения, разработанные с использованием дубликата кода, переполняются, что является полной противоположностью гибкости.
Майкл Боргвардт
1
@MichaelBorgwardt это звучит так, как будто вы привыкли работать с анемичными моделями доменов, в которых сущности представляют собой нечто большее, чем пакеты свойств с небольшим или отсутствующим поведением. В этом случае да, DTO / View-модель будет в основном дубликатом. Однако, если у вас есть богатая модель предметной области со сложными отношениями, становится необходимым слой DTO / View-моделей, и они не будут так похожи на сущности домена.
MattDavey
@MattDavey: Похоже, что доменные модели, с которыми вы привыкли работать, не просто богатые, а настоящие клептократы. Мне также не нравятся анемичные модели, но они все еще являются моделями, и их поведение должно ограничиваться представлением предметной области. Принцип единой ответственности и все такое ...
Майкл Боргвардт