Бизнес-объекты - контейнеры или функционал?

19

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

Там, где я работаю, мы несколько раз обсуждали эту тему и ищем проверку на здоровье. Вот вопрос: должны ли Business Objects быть контейнерами данных (больше похожими на DTO ) или они также содержат логику, которая может выполнять некоторые функции для этого объекта.

Пример. Возьмите объект customer, он, вероятно, содержит некоторые общие свойства (Name, Id и т. Д.). Должен ли этот объект также включать функции (Save, Calc и т. Д.)?

В одной из рассуждений говорится, что отделите объект от функциональности (принципал единой ответственности) и поместите функциональность в слой или объект Business Logic.

Другая линия рассуждений говорит: нет, если у меня есть объект customer, я просто хочу вызвать Customer.Save и покончить с этим. Почему я должен знать о другом классе, чтобы спасти клиента, если я потребляю объект?

В двух последних наших проектах объекты были отделены от функциональности, но вновь возникли споры о новом проекте.

Что имеет больше смысла и почему?

Вальтер
источник
1
Не могли бы вы рассказать нам больше о вашем проекте? Характер вашего проекта определит, какое решение лучше.

Ответы:

5

Если вы считаете, что Заказчик является частью модели предметной области, тогда имеет смысл (особенно в контексте DDD, но не ограничивается этим) иметь свойства и операции для этого объекта.

Тем не менее, с учетом сказанного, я думаю, что пример, который вы использовали, плохой, и является причиной спора. Если вы говорите о постоянстве, то клиенты обычно не «спасают» себя; все, что вы используете для настойчивости, будет. Имеет смысл, что любой тип постоянства должен принадлежать постоянному слою / разделу. Это, как правило, основа шаблона хранилища. ** Такой метод, как Customer.UpgradeWarranty () или Customer.PromoteToPreferred (), делает аргумент более понятным.

Это также не исключает возможности наличия DTO . Рассмотрим ситуацию, когда вы собираетесь передать информацию о клиенте, например, в удаленный сервис. Для клиента может не иметь смысла создавать DTO для транспорта, это архитектурная проблема, но для этого можно было бы обосновать это на уровне персистентности или на сетевом уровне / раздел / код / ​​что-у-вас. В этом случае такой объект может иметь методы, которые выглядят так

public static CustomerDTO GetDTO(Customer c) { /* ... */ }

public static Customer GetCustomer(CustomerDTO cdto) { /* ... */ }

Итак, в целом, имеет смысл иметь операции над объектом домена, которые соответствуют логическим операциям в домене.

Google для «Постоянства невежества» для ряда хороших обсуждений по этому вопросу ( этот вопрос SO , и его принятый ответ является хорошим местом для начала).

** Это немного запутано с определенным программным обеспечением OR / M, где вы вынуждены наследовать от постоянной базы, которая имеет метод «Сохранить».

Стивен Эверс
источник
3

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

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

Рейчел
источник
3

Я думаю, что оба способа сделать это могут иметь свои преимущества, но я бы посмотрел на это с точки зрения объектно-ориентированного или доменного управления: каковы обязанности различных классов и объектов.

Итак, я бы спросил себя в вашем конкретном случае: должен ли клиент знать, как себя спасти?

Для меня ответом будет «нет»: логически для меня это не имеет смысла, и клиент вообще не должен ничего знать о какой-либо структуре постоянства (разделение обязанностей).

Что касается примеров SnOrfus с Customer.UpgradeWarranty () или Customer.PromoteToPreferred (), они, очевидно, более ориентированы на бизнес-логику, чем Customer.Save (). Существуют различные подходы к этому, но, опять же, если вы спросите себя, должен ли клиент иметь возможность обновить свою гарантию, ответ может быть как да, так и нет, в зависимости от того, как вы на это смотрите:

  • да, клиент может, конечно, обновить свою гарантию
  • нет: клиент может запросить обновление, но обновление выполнено кем-то другим

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

Однако, по моему опыту, разделение данных и (бизнес) логики делает архитектуру более простой, хотя и не такой захватывающей.

Vetle
источник
2

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

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

Структура данных:

class CustomerController
{
    public int MostRecentOrderLines(Customer customer)
    {
        var o = (from order in customer.Orders
                orderby order.OrderDate desc
                select order).First();
        return o.OrderLines.ToList().Count;
    }
}

Структура без данных:

class Customer
{
    public int MostRecentOrderLines()
    {
        // ... same ...
    }
}

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

Итак, вы хотите, чтобы ваши объекты были структурами данных или службами? Для согласованности, кажется, лучше придерживаться одного. В первом случае поместите свою логику типа «сервис» в другое место, а не в сущность.

Скотт Уитлок
источник
1

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

Я постараюсь обобщить факты, которые они приводят:

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

  • Проще понять код и функциональность объекта бу.

  • Меньше сложности в дизайне

Я говорю им, что они ленивы, и я сделал бы это так:

  • Да, бизнес-классы представляют вещи, поэтому он содержит данные. Но кажется неправильным спасать себя или даже копировать. Вы не можете сделать это в RL.

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

  • Имея один объект, способный сохранять данные, этот объект может содержать соединение с базой данных, и весь трафик базы данных направляется на этот объект. (в основном это слой доступа к данным), иначе весь объект Business должен был бы содержать соединение. (что добавляет сложности)

Ну, так или иначе, я собираюсь спросить учителя. Когда я это сделаю, я также опубликую его ответ здесь, если хотите. ;)

редактировать:

Я забыл упомянуть, что этот проект был о книжном магазине.

Emerion
источник
Ваши одноклассники правы, за исключением того, что они путают бизнес-логику с другими формами логики (в данном случае, архитектурой или логикой постоянства).
Стивен Эверс
1

Ответ на самом деле будет зависеть от того, какова ваша архитектура / дизайн - архитектура DDD с моделью предметной области будет сильно отличаться от модели, ориентированной на данные CRUD, когда речь идет о проектировании объектов.

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

В доменной модели вы хотите отделить бизнес-модель от инфраструктурных проблем, поэтому вам абсолютно необходимо избегать таких методов, как «.Save». Я не помню, когда в последний раз я «спасал» клиента в реальной жизни!

В приложении CRUD данные относятся к первоклассному гражданину, поэтому «.Save» вполне уместен.

Большинство значительных реальных приложений будут иметь смесь этих парадигм - модель предметной области, где существуют быстро меняющиеся или сложные бизнес-правила, DTO для передачи данных через границы, CRUD (или что-то между CRUD и моделью предметной области, такой как activerecord) в другом месте. Не существует единого правила, подходящего для всех.

FinnNk
источник
0

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

Я также изменил бы точку зрения Скотта Уитлока сверху (за исключением того, что у меня нет точек, являющихся новым членом), классы данных или бизнес-логики не должны беспокоиться о том, как объект хранится постоянно.

Тем не менее, если вы имеете дело с существующим продуктом, если у вас есть чистые, контрактные интерфейсы - это нормально и обслуживаемо тоже ...

heretik
источник