Модель анемичной области: плюсы и минусы

Я хотел бы знать, каковы плюсы и минусы использования модели Anemic Domain (см. Ссылку ниже).

Статья Фаулера

16 ответов

Решение

Плюсы:

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

Минусы:

  • Ваша доменная логика существует где-то еще, вероятно, в классе, полном классовых (статических) методов. Или ваш код GUI. Или в нескольких местах, все с противоречивой логикой.
  • Это анти-шаблон, поэтому другие разработчики спросят, понимаете ли вы концепции объектно-ориентированного дизайна.

Почему "Модель анемичной области" является анти-паттерном, почему существует так много систем, которые реализуют это?

Я думаю, что есть несколько причин

1. Сложность системы

В простой системе (которая содержит почти все примеры и примеры кода, которые вы найдете в Интернете), если я хочу реализовать:

Добавление товара в заказ

Я поставил эту функцию на заказ

public void Order.AddOrderLine(Product product)
{
    OrderLines.Add(new OrderLine(product));
}

Хороший и супер объектно-ориентированный.

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

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

public void OrderService.AddOrderLine(Order order, Product product)
{
    if (!InventoryService.Has(product)
       throw new AddProductException

    order.AddOrderLine(product);
}

Я также мог бы передать IInventoryService в Order.AddOrderLine, который является еще одним вариантом, но все же делает Order зависимым от InventoryService.

В Order.AddOrderLine все еще есть некоторая функциональность, но обычно она ограничена областью заказа, хотя, по моему опыту, бизнес-логика выходит за рамки объема заказа.

Когда система представляет собой нечто большее, чем просто CRUD, вы в конечном итоге получите большую часть своей логики в OrderService и очень мало в Order.

2. Взгляд разработчика на ООП

В интернете много горячих дискуссий о том, какая логика должна действовать на сущностях.

Что-то вроде

Order.Save

Должен ли Порядок знать, как себя спасти или нет? Допустим, у нас есть хранилища для этого.

Теперь можно заказать добавить строки заказа? Если я попытаюсь разобраться в этом, используя простой английский, это тоже не имеет смысла. Пользователь добавляет продукт в заказ, поэтому мы должны сделать User.AddOrderLineToOrder()? Это кажется излишним.

Как насчет OrderService.AddOrderLine(). Теперь это имеет смысл!

Мое понимание ООП состоит в том, что для инкапсуляции вы помещаете функции в классы, где функция должна будет получить доступ к внутреннему состоянию класса. Если мне нужно получить доступ к коллекции Order.OrderLines, я помещаю Order.AddOrderLine() в Order. Таким образом, внутреннее состояние класса не раскрывается.

3. Контейнеры IoC

Системы, которые используют контейнеры IoC, обычно полностью анемичны.

Это потому, что вы можете тестировать свои сервисы / репозитории, которые имеют интерфейсы, но не могут тестировать доменные объекты (легко), если вы не установите интерфейсы для всех из них.

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

4. ООП сложно, процедурно легко

У меня есть немного " проклятия знаний", но я обнаружил, что для новых разработчиков, имеющих DTO и сервисы, намного проще, чем Rich Domain.

Возможно, это связано с тем, что с Rich Domain сложнее узнать, на какие классы поместить логику. Когда создавать новые классы? Какие шаблоны использовать? так далее..

С сервисами без сохранения состояния вы просто добавляете их в сервис с ближайшим именем.

После этого у меня в голове очень долго была мысль. Я считаю, что термин "ООП" приобрел значение, которое на самом деле не предназначалось для него. Анаграмма означает "объектно-ориентированное программирование", как мы все хорошо знаем. Основное внимание, конечно, уделяется слову "ориентированный". Это не "OMP", что означает "объектно-ориентированное программирование". И ADM, и RDM являются примерами ООП. Они используют объекты, свойства, методы интерфейсов и так далее. Однако существует разница между ADM и RDM в том, как мы выбираем инкапсуляцию. Это две разные вещи. Сказать, что ADM плохой ООП, не является точным утверждением. Возможно, нам нужны разные термины для разных уровней инкапсуляции. Кроме того, мне никогда не нравился термин анти-паттерн. Это обычно назначается на что-то членами противоположной группы. И ADM, и RDM являются допустимым шаблоном, они просто преследуют разные цели и предназначены для решения различных бизнес-задач. Те из нас, кто практикует DDD, должны, по крайней мере, ценить это и не опускаться до уровня других, избивая тех, кто решает внедрить ADM. Просто мои мысли.

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

"Модель анемичной области - это анти-паттерн. У анти-паттернов нет плюсов".

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

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

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

Я полагаю, что есть как минимум две силы, способные создать такой дизайн:

  1. Дизайнеры / программисты, которые все еще думают, что процедурно необходимо работать в объектно-ориентированной среде (или предполагать, что они могут...) создавать новую систему, и

  2. Разработчики, работающие над созданием сервис-подобного "лица" в унаследованной системе, разработанной не по принципу OO (независимо от языка).

Если, например, кто-то создает набор сервисов для предоставления функциональных возможностей существующего приложения мэйнфрейма COBOL, можно определить сервисы и интерфейсы в рамках концептуальной модели, которая не отражает внутренние структуры данных COBOL. Однако, если служба отображает новую модель на устаревшие данные, чтобы использовать существующую, но скрытую реализацию, то новая модель вполне может быть "анемичной" в смысле статьи Фаулера - например, набор определений в стиле TransferObject. и отношения без реального поведения.

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

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

И энтропийная RDM вредит, в частности. Его разработчики будут усваивать суровые уроки. Сначала они смогут оправдать ожидания своих заинтересованных сторон, потому что у них не будет истории, чтобы соответствовать. Но по мере того, как их система становится более сложной (не сложной), она становится хрупкой; разработчики будут пытаться повторно использовать код, но, как правило, приводят к появлению новых ошибок или отклонению в процессе разработки (и, таким образом, переоценивают свои оценки).

Напротив, разработчики ADM будут устанавливать более низкие ожидания для себя, потому что они не ожидают повторного использования такого большого количества кода для новых функций. Со временем у них будет система с множеством несоответствий, но она, вероятно, не сломается. Их время выхода на рынок будет больше, чем при успешном RDM, но их заинтересованные стороны вряд ли осознают такую ​​возможность.

Поработав со "зрелой" системой с ADM, я чувствую, что могу дать, по крайней мере, некоторую обратную связь по этому вопросу.

1) Отсутствие инкапсуляции

В действующей системе с ADM есть возможность написать, например, 'obj.x = 100; obj.save', даже если это нарушает бизнес-логику. Это приводит к ряду ошибок, которые не встретились бы, если бы инварианты были смоделированы на объекте. Это серьезность и распространенность этих ошибок, которые я считаю наиболее серьезным негативом для ADM.

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

2) Код наворотов

Я предполагаю, что объем кода, создаваемого в ADM, в 5-10 раз больше, чем было бы создано решением OOP/RDM. Это объясняется, возможно, 50% повторением кода, 30% кодом котельной пластины и 20% решением или решением проблем, которые возникают из-за отсутствия RDM.

3) Плохое понимание проблем домена

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

4) сложность обслуживания

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

5) Повышенная сложность при посадке

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

Мне также хотелось включить расходы на операционную поддержку для ADM, которые выше, чем для RDM, но это будет зависеть от ряда факторов.

Как уже отмечали другие, посмотрите на DDD (Грег Эванс, Винс Вон и Скотт Миллетт) на преимущества RDM.

"Разработчики, работающие над созданием сервисного" лица "в унаследованной системе, разработанной не по принципу OO (независимо от языка)".

Если вы думаете о многих LOB-приложениях, эти устаревшие системы часто не будут использовать ту же модель домена, что и вы. Модель Anemic Domain решает эту проблему с использованием бизнес-логики в классах обслуживания. Вы можете поместить весь этот интерфейсный код в вашу модель (в традиционном смысле ОО) - но обычно вы теряете модульность.

Когда я впервые наткнулся на статью об Анемичной модели предметной области, я подумал: "Святой с ***, вот что я делаю. Ужас!" Я выстоял и следовал ссылкам на книгу Эрика Эвана, считался хорошим примером и загружал источник. Оказывается, что "не использование модели анемичной области" не означает "не использование классов обслуживания, не использование посредников, не использование стратегий" или даже "наложение логики на класс, которым манипулируют".

Примеры DDD имеют классы обслуживания, XyzUpdaters, синглтоны и IoC.

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

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

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

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

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

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

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

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

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

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

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

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

В соответствии с ответом Эрика П, а также тем, что написали некоторые другие выше, кажется, что основным недостатком ADM является потеря OOD, а именно - сохранение логики и данных концепции предметной области вместе, так что детали реализации скрыты, а API может быть богатым.

Далее Эрик отмечает, что за пределами класса домена часто существует информация, необходимая для логики работы с этим классом, такой как проверка инвентаря перед добавлением товара в заказ. Я спрашиваю, однако, является ли ответ уровнем обслуживания, который содержит эту всеобъемлющую логику, или лучше обрабатывать его как часть проектирования объекта. Кто-то должен знать об объекте Inventory, объекте Product и объекте Order. Возможно, это просто объект OrderSystem, в котором есть член Inventory, список Orders и так далее. Это не будет сильно отличаться от Сервиса, но я думаю, что это концептуально более согласованно.

Или посмотрите на это так: у вас может быть Пользователь с внутренним кредитным балансом, и каждый раз, когда вызывается User.addItemToOrder(item), он получает цену элемента и проверяет кредит перед его добавлением и т. Д. Это кажется разумным OO дизайн. Я не уверен точно, что потеряно, заменяя это Service.addItemToUserOrder(пользователь, элемент), но я также не уверен, что получилось. Я предполагаю, что потеря была бы дополнительным слоем кода, плюс более грубый стиль написания и вынужденное незнание базовой Доменной Модели.

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

Слои сервисов, созданные ADM, безусловно, проще в реализации (поскольку они требуют относительно небольшого обдумывания и имеют много децентрализованных точек интерфейса), вероятно, создадут проблемы в будущем, когда придет время изменять живую и растущую систему.

Я мог бы также добавить, что не во всех случаях требуется модель домена (не говоря уже о модели ADM). Иногда лучше использовать более процедурный / функциональный стиль задачи, основанный на данных и не зависящий от логики / бизнес-правил всего приложения.

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

Также подумайте заранее, какой из них будет легче поддерживать...

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

Чтобы расширить ответ Майкла, я бы подумал, что (довольно) ясно, куда должен идти этот код: в отдельного Посредника, который управляет взаимодействием между Орденом и Инвентаризацией.

Исходя из моего POV, ключевым моментом в отношении домена является то, что он ДОЛЖЕН содержать простое поведение тестирования isInThisState() методы и т. д. По моему опыту, они также разбросаны по слезам обслуживания (sic:)) в большинстве компаний и либо копируются, либо переписываются бесконечно. Все это нарушает стандартные правила конхезии.

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

Моя команда лично предпочитает ADM. у нас есть набор бизнес-объектов, которые представляют определенные части нашего домена. Мы используем сервисы для сохранения этих объектов в БД. Наши бизнес-объекты имеют методы, однако эти методы только манипулируют его внутренним состоянием.

Для нас преимущество использования ADM над RDM можно увидеть в том, как мы сохраняем объекты в db. Разработчики, работающие с нашими устаревшими системами кода, могут использовать наши бизнес-объекты (из новой системы) и продолжать использовать свой текущий уровень доступа к данным для сохранения этих объектов в БД. Использование RDM вынудит разработчиков нашей унаследованной системы внедрить объекты репозитория в нашу бизнес-модель... что не будет соответствовать их текущему уровню доступа к данным.

Модель анемичной области - это анти-паттерн. У анти-паттернов нет плюсов.

Другие вопросы по тегам