Я хотів би знати, які плюси та мінуси використання Anemic Domain Model (див. Посилання нижче).
Я хотів би знати, які плюси та мінуси використання Anemic Domain Model (див. Посилання нижче).
Відповіді:
Плюси:
Мінуси:
Оскільки "Анемічна модель домену" є анти-шаблоном, чому існує так багато систем, які реалізують це?
Думаю, причин є кілька
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 і дуже мало в порядку.
2. Погляд розробника на ООП
В Інтернеті є багато бурхливих дискусій щодо того, яка логіка повинна йти на сутності.
Щось на зразок
Замовити. Зберегти
Чи повинен Орден знати, як врятувати себе чи ні? Скажімо, у нас є сховища для цього.
Тепер замовлення може додати рядки замовлення? Якщо я намагаюся зрозуміти це, використовуючи просту англійську мову, це насправді теж не має сенсу. Користувач додає Продукт до Замовлення, тож ми повинні робити User.AddOrderLineToOrder ()? Це здається надмірним.
Як щодо OrderService.AddOrderLine (). Тепер це має сенс!
Я розумію ООП полягає в тому, що для інкапсуляції ви ставите функції в класи, де функція потребуватиме доступу до внутрішнього стану класу. Якщо мені потрібно отримати доступ до колекції Order.OrderLines, я розміщую Order.AddOrderLine () на Order. Таким чином внутрішній стан класу не виявляється.
3. Контейнери IoC
Системи, що використовують контейнери IoC, як правило, повністю анемічні.
Це тому, що ви можете протестувати свої служби / сховища, які мають інтерфейси, але не можете перевірити об’єкти домену (легко), якщо ви не встановите інтерфейси на всіх них.
Оскільки "IoC" в даний час оцінюється як вирішення всіх ваших проблем програмування, багато людей сліпо дотримуються його, і таким чином в кінцевому підсумку виходять моделі Anemic Domain.
4. ООП - важко, процедурно - легко
У мене є трохи " Прокляття знань " з цього приводу, але я виявив, що для нових розробників використання DTO та служб набагато простіше, ніж Rich Domain.
Можливо, це тому, що з Rich Domain важче знати, на яких класах покласти логіку. Коли створювати нові класи? Які шаблони використовувати? тощо.
З послугами без громадянства ви просто ляпаєте їх у службу з найближчим іменем.
Після цього в мене в голові вже дуже давно виникає думка. Я переконаний, що термін "ООП" набув значення, не по-справжньому призначеного для нього. Як ми всі добре знаємо, анаграма означає "об'єктно-орієнтоване програмування". Фокус, звичайно, зосереджений на слові "орієнтований". Це не "OMP", що означає "програмоване програмування". І ADM, і RDM є прикладами ООП. Вони використовують об'єкти, властивості, інтерфейси методів тощо. Однак існує різниця між ADM та RDM у тому, як ми вирішили інкапулювати речі. Це дві різні речі. Сказати, що ADM поганий ООП, не є точним твердженням. Можливо, нам потрібні різні терміни для різних рівнів інкапсуляції. До того ж мені ніколи не подобався термін анти-шаблон. Зазвичай його призначають чомусь члени протиборчої групи. І ADM, і RDM є дійсним зразком, вони просто мають на увазі різні цілі і призначені для вирішення різних потреб бізнесу. Ті з нас, хто практикує DDD, повинні, принаймні, це оцінити, а не падати на рівень інших, розбиваючи тих, хто вирішив впровадити ADM. Тільки мої думки.
"Це анти-шаблон, тому інші розробники запитають, чи розумієте ви поняття об’єктно-орієнтованого дизайну".
"Анемічна модель домену є анти-шаблоном. Анти-шаблони не мають плюсів."
Чи є модель анемічного домену анти-шаблоном, це питання думки. Мартін Фаулер каже, що так, багато розробників, які знають ОО навиворіт, кажуть, що це не так. Висловлення думки як факту рідко буває корисним.
Навіть якби це було загальновизнаним анти-шаблоном, є ймовірність, що він все одно матиме деякий (хоча і відносно невеликий) верх.
the chances are it would still have some (though relatively little) upside.
Тоді назвіть, будь ласка! Принаймні один, замість гіпотези!
Мені здається, що головне заперечення Фаулера полягає в тому, що ADM не є ОО, у наступному сенсі. Якщо розробляти систему "з нуля" навколо пасивних структур даних, якими маніпулюють інші фрагменти коду, то це, безумовно, більше пахне процедурним дизайном, ніж об'єктно-орієнтованим дизайном.
Я припускаю, що існують принаймні дві сили, здатні створити такий дизайн:
Дизайнери / програмісти, які досі вважають, що процедурно потрібно працювати в об'єктно-орієнтованому середовищі (або припускати, що вони можуть ...) для створення нової системи, і
Розробники працюють над тим, щоб поставити сервісне "обличчя" на застарілу систему, розроблену за принципом, що не стосується ОО (незалежно від мови).
Якщо, наприклад, хтось будував набір служб, щоб викрити функціональність існуючого додатка COBOL на мейнфреймі, можна було б визначити служби та інтерфейси з точки зору концептуальної моделі, яка не відображає внутрішні структури даних COBOL. Однак якщо служба відображає нову модель із застарілими даними для використання існуючої, але прихованої реалізації, тоді нова модель цілком може бути "анемічною" у розумінні статті Фаулера - наприклад, набір визначень стилю TransferObject та стосунки без реальної поведінки.
Цей вид компромісу цілком може бути загальним для кордонів, при яких ідеалістично чисті системи ОО повинні взаємодіяти з існуючим середовищем, що не є ОО.
Анемічна модель домену (ADM) може бути хорошим вибором, якщо ваша команда не може або не хоче створити розширену модель домену (RDM) та підтримувати її з часом. Перемога з RDM вимагає пильної уваги до домінуючих абстракцій, що використовуються в системі. Згадайте, що в будь-якій групі розробників не більше половини, а можливо, лише десята частина її членів компетентна з абстракціями. Якщо цей персонал (можливо, лише один розробник) не зможе зберегти вплив на діяльність всієї групи, RDM піддасться ентропії.
І ентропійний RDM болить, зокрема,. Його розробники засвоять суворі уроки. Спочатку вони зможуть відповідати очікуванням своїх зацікавлених сторін, тому що не матимуть жодної історії, щоб виправдати. Але в міру ускладнення їх системи (не складної) вона стає крихкою; розробники намагатимуться повторно використовувати код, але, як правило, спричинятимуть нові помилки або відстежуватимуть розвиток (і, таким чином, перевищуватимуть їх оцінки).
На відміну від цього, розробники ADM встановлюватимуть для себе нижчі очікування, оскільки вони не сподіваються повторно використовувати стільки коду для нових функцій. Згодом у них з’явиться система із багатьма невідповідностями, але вона, ймовірно, не розірветься несподівано. Їхній час виходу на ринок буде довшим, ніж при успішному RDM, але їх зацікавлені сторони навряд чи сприймуть таку можливість.
"Розробники працюють над тим, щоб нанести послугу" обличчя "на застарілу систему, розроблену не-OO (незалежно від мови)."
Якщо ви думаєте про багато додатків LOB, ці застарілі системи часто не використовуватимуть ту саму модель домену, що і ви. Anemic Domain Model вирішує це за допомогою бізнес-логіки в класах обслуговування. Ви можете розмістити весь цей інтерфейсний код всередині вашої моделі (у традиційному розумінні ОО), - але, як правило, ви втрачаєте модульність.
Коли я вперше натрапив на статтю про Anemic Domain Model, я подумав: "Holy s ***, ось що я роблю. Жах!" Я витримав і слідував за посиланнями на книгу Еріка Евана, який вважався хорошим прикладом, і завантажив джерело. Виявляється, "не використовувати анемічну модель домену" не означає "не використовувати класи обслуговування, не використовувати посередники, не використовувати стратегії" і навіть "накладати логіку на клас, яким маніпулюють".
Приклади DDD мають класи обслуговування, XyzUpdaters, одиночні та IoC.
Я все ще бентежусь, що саме таке анемічна модель домену. Я очікую, що "я це дізнаюсь, коли побачу". Наразі я задоволений позитивним прикладом гарного дизайну.
Попрацювавши із "зрілою" системою з 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.
Це такий самий професіонал, як і у більшості анти-шаблонів: він дозволяє тривалий час зайняти багатьох людей. Оскільки менеджерам, як правило, платять більше, коли вони керують більшою кількістю людей, існує сильний стимул не вдосконалюватися.
Відповідно до відповіді Еріка П, а також того, що писали деякі інші вище, здається, що головним недоліком ADM є втрата ООД, зокрема збереження логіки та даних концепції домену разом, щоб деталі реалізації були приховані, поки API може бути багатим.
Далі Ерік зазначає, що за межами класу домену часто є інформація, необхідна для логіки дії на цей клас, наприклад перевірка інвентаризації перед додаванням товару до замовлення. Однак я сумніваюся, чи відповідь - це рівень обслуговування, який містить цю всеосяжну логіку, чи його краще обробляти як частину дизайну об’єкта. Хтось повинен знати про об’єкт «Інвентаризація», «Продукт» та «Замовлення». Можливо, це просто об’єкт OrderSystem, який має члена Інвентаризації, список Замовлення тощо. Це не буде сильно відрізнятися від Послуги, але я думаю, що це концептуально більш узгоджено.
Або подивіться на це так: Ви можете мати Користувача з внутрішнім кредитним балансом, і кожен раз, коли викликається User.addItemToOrder (item), він отримує ціну товару та перевіряє кредит перед його додаванням тощо. Це здається розумним OO дизайн. Я не впевнений, що саме втрачено, замінивши це на Service.addItemToUserOrder (користувач, елемент), але я також не впевнений, що виграли. Думаю, втратою став би додатковий рівень коду, плюс незграбніший стиль написання та вимушене незнання базової Моделі Домену.
Слід зазначити, що у міру зростання систем у міру ускладнення та деталізації варіацій, інкапсуляція та консолідація точок інтерфейсу, які надає добре розроблена об'єктна модель передачі повідомлень, робить набагато безпечнішими зміни та підтримку критичного коду без широкого рефакторингу.
Службові рівні, створені ADM, хоча, безсумнівно, простіші у впровадженні (оскільки вони вимагають порівняно невеликого продумування та мають багато децентралізованих точок інтерфейсу), швидше за все, створюватимуть проблеми в майбутньому, коли настане час модифікувати живучую та зростаючу систему.
Я міг би також додати, що далеко не у всіх випадках потрібна модель домену (не кажучи вже про ADM). Іноді краще використовувати більш процедурний / функціональний стиль завдання, керований даними, і це не залежить від загально логічної логіки / бізнес-правил.
Якщо ви намагаєтесь визначитися з плюсами і мінусами цілого додатка, я думаю, важливо спершу розробити, як би міг виглядати кожен із них для вашої програми ДО того, як ви навіть почнете писати один рядок коду. Після того, як ви оформили CRC або встановили каркасну програму в обох стилях, зробіть крок назад і вирішіть, який із них має більше сенсу та краще підходить для програми.
Також заздалегідь подумайте, який із них буде простіше підтримувати ...
Це дає кращу передбачуваність. Менеджерам це подобається, особливо якщо за проект витрачається час і матеріали. Кожна зміна означає багато роботи, тому складна робота може бути прихована за великою кількістю повторюваних робіт. У добре розробленій системі СУХОГО передбачуваність дуже погана, оскільки ви постійно робите нові справи.
Після того, як я вперше прочитав книгу Еріка Еванса про дизайн, керований доменом, я насправді не зрозумів, що це не просто купа тактичних зразків для створення хороших класів моделей доменів.
Дізнавшись більше про тему та використовуючи стратегічні зразки, я нарешті почав розуміти, що спочатку мова йде про глибоке розуміння ділових проблем, які ви намагаєтеся вирішити.
І лише після цього ви можете вирішити, які частини системи підходять для застосування тактичних зразків, таких як агрегати, сутності , сховища тощо, поряд із так званими розширеними моделями доменів (на відміну від анемічних ). Але для того, щоб скористатися цими закономірностями, має бути достатньо складнощів щодо бізнес-логіки для цієї частини системи .
Отже, коли справа стосується впровадження вирішення розглянутої проблеми, спочатку слід визначити, чи краще підходити до цієї конкретної проблеми з використанням підходу, заснованого на CRUD, чи інвестування в багату модель домену разом із згаданими тактичними моделями.
Якщо CRUD має більше сенсу, наприклад, якщо відсутня складна бізнес-логіка, і більша частина логіки пов'язана з перетворенням, передачею та збереженням даних, що реалізують модель домену, це може бути невимушеним надлишком. Це не означає, що роботи не буде багато, а просто не найбільше зусиль щодо впровадження дають ділові правила. Але в цьому випадку не існує такого поняття, як анемічна модель домену , просто тому, що доменної моделі взагалі немає . Ви, швидше за все, побачите такі речі, як DTO (Об'єкти передачі даних) або DAO(Об'єкти доступу до даних) та класи обслуговування, які будуть працювати з даними. І відповідні операції значною мірою пов'язані з перетворенням даних з одного подання в інше та переміщенням даних із дуже малою або майже відсутністю ділової логіки.
Якщо ви визначили, що існує багато складної бізнес-логіки, яка з часом також зміниться, ніж інвестування в модель домену - з мого досвіду - хороша ідея. Причина полягає в тому, що простіше представити бізнес-перспективу за допомогою коду та полегшити розуміння відповідних операцій, що відображають діловий домен та його правила. Це не означає, що в кожному випадку використання повинні існувати класи доменних моделей. Наприклад, якщо немає стану, який мутується та зберігається, також можуть бути лише служби домену, які містять логіку домену, реалізовані більше як чисті функції.
Але якщо існує також стан, який мутується і зберігається, що також має мету та значення в діловій сфері, держава та поведінка, що змінює цей стан, слід інкапсулювати . Зважаючи на це, ніхто не може обійти бізнес-правила настільки легко, що призводить до недійсних станів та серйозних збоїв. Так звані анемічні моделі доменів часто є джерелом таких проблем. . Це часто трапляється, якщо ви бачите код, де різні компоненти працюють в одному і тому ж «анемічному» класі доменної моделі, перевіряючи якусь частину його стану та змінюючи якусь частину його стану, не дбаючи про загальні інваріанти цього господарюючого суб’єкта. Не потрібно називати це анти-шаблоном, але важливо це розумітиВи втрачаєте багато переваг багатих моделей доменіву підході, заснованому на DDD, разом із згаданими проблемами. При використанні моделі домену, де поведінка та її дані розміщені в одному класі, також може бути безліч різних "клієнтів", що викликають операції цього класу, але їм не потрібно дбати про те, щоб ділові інваріанти суб'єкта господарювання дотримувались як клас доменної моделі завжди подбає про це, а також може повідомити "клієнта" про недійсні операції або навіть викинути винятки як мережу безпеки.
Так нижня лінія , я думаю , що це важливо не плутати структури даних , як класи (такі як DTOS або DAO , ) з анемією , класів моделі домену . У ретельно та навмисно підібраному підході, що базується на CRUD, немає переваг у спробі використовувати модель домену, оскільки існує занадто менш складна бізнес-логіка.
Під анемічною моделлю домену я б посилався на код, з якого я бачу, що існує багато складної бізнес-логіки та бізнес-правил, які розподілені між різними компонентами, які, швидше за все, повинні бути близькими до даних, які ця логіка змінює.
Є також ще один урок, який я вивчив по дорозі: якщо ви намагаєтесь використовувати ту саму ділову мову (яку також називають повсюдною мовою ) у своєму коді, яку стейкхолдери використовують у своєму щоденному робочому житті, ви вже здобуваєте стільки переваг щодо розуміння діловий домен та покращення читабельності коду, що настільки вам допоможе, незалежно від того, використовуєте ви підхід на основі CRUD або модель домену.
Щоб розширити відповідь Майкла, я вважав би (досить) зрозумілим, куди повинен йти цей код: у виділеному Посереднику, який обробляє взаємодію між Орденом та Інвентарем.
З мого POV головне в домені полягає в тому, що він ПОВИНЕН утримувати просту поведінку тестування, isInThisState()
методи і т. Д. З мого досвіду вони також розпорошені по сервісі сльозами (sic :)) у більшості компаній, або копіюються, і нескінченно переписуються. Все це порушує стандартні правила згуртованості.
На мій погляд, підхід повинен полягати в тому, щоб прагнути до СД, що має настільки велику кількість бізнес-поведінки, наскільки це практично, а решту розміщувати у чітко відведених місцях (тобто не в послугах)
Моя команда особисто надає перевагу ADM. у нас є набір бізнес-об’єктів, що представляють певні частини нашого домену. Ми використовуємо сервіси для збереження цих об’єктів у базі даних. Наші бізнес-об’єкти мають методи, однак ці методи лише маніпулюють його внутрішнім станом.
Перевага для нас від використання ADM порівняно з RDM можна побачити в тому, як ми зберігаємо об'єкти в db. Розробники, що працюють над нашими застарілими кодовими системами, можуть використовувати наші бізнес-об'єкти (з нової системи) і продовжувати використовувати свій поточний рівень доступу до даних, щоб зберегти ці об'єкти на базі даних. Використання RDM змусило б розробників нашої застарілої системи вводити об’єкти сховища в нашу бізнес-модель ... що не відповідало б їх поточному рівню доступу до даних.
Модель анемією домену є анти-патерн. Антишаблони не мають плюсів.