З усіма цими послугами, як я не можу бути анемічним?


90

Де ми проведемо межу між делегуванням та інкапсуляцією бізнес-логіки? Мені здається, що чим більше ми делегуємо, тим анемічнішими ми стаємо. Однак делегація також сприяє повторному використанню та принципу DRY. Отже, що доречно делегувати і що має залишитися в наших моделях домену?

Візьміть такі приклади як приклади:

Авторизація . Чи повинен об'єкт домену нести відповідальність за підтримку правил контролю доступу (наприклад, властивості CanEdit) або повинен бути делегований іншому компоненту / службі, що відповідає виключно за управління доступом, наприклад, IAuthorizationService.CanEdit (об'єкт)? Або це повинно бути поєднання двох? Можливо, в об’єкті домену є властивість CanEdit, яка делегує внутрішній сервіс IAuthorizationService для виконання фактичної роботи?

Перевірка . Те саме обговорення, що й вище, стосується валідації. Хто дотримується правил і хто відповідає за їх оцінку? З одного боку, стан об'єкта повинен належати цьому об'єкту, а дійсність - це стан, але ми не хочемо переписувати код, який використовується для оцінки правил для кожного об’єкта домену. У цьому випадку ми могли б використовувати спадщину ...

Створення об'єкта . Фабричний клас проти заводських методів порівняно з "нововведенням" екземпляра. Якщо ми використовуємо окремий заводський клас, ми можемо виділити та інкапсулювати логіку створення, але за рахунок відкриття стану нашого об'єкта на заводі. Цим можна керувати, якщо наш доменний шар знаходиться в окремій збірці, відкривши внутрішній конструктор, який використовується заводом, але це стає проблемою, якщо існує кілька моделей створення. І якщо вся фабрика робить виклик правильного конструктора, який сенс мати фабрику?

Фабричні методи класу усувають проблему з відкриттям внутрішнього стану об'єкта, але, оскільки вони є статичними, ми не в змозі розірвати залежності через інжекцію заводського інтерфейсу, як ми можемо з окремим заводським класом.

Наполегливість . Можна стверджувати, що якщо наш доменний об’єкт буде піддавати CanEdit під час делегування відповідальності за здійснення перевірки авторизації іншій стороні (IAuthorizationService), чому б не встановити метод "Зберегти" на нашому об'єкті домену, який робить те саме? Це дозволило б оцінити внутрішній стан об'єкта, щоб визначити, чи може операція виконуватися без порушення інкапсуляції. Звичайно, це вимагає, щоб ми ввели екземпляр сховища в наш об’єкт домену, який трохи пахне для мене, тож ми замість цього піднімаємо подію домену та дозволяємо обробнику виконувати операцію збереження?

Бачите, куди я йду з цим?

У Rockford Lhotka є велика дискусія щодо його причин проходження маршруту «Клас заряджання» для його CSLA-рамки, і я маю трохи історії з цим фреймом, і я бачу його уявлення про бізнес-об’єкти, що паралельно співпадають доменні об’єкти багато в чому. Але намагаючись стати більш прихильним до хороших ідеалів DDD, мені цікаво, коли співпраці стає занадто багато.

Якщо я закінчую IAuthorizationService, IValidator, IFactory та IRepository для мого сукупного кореня, що залишається? Чи достатньо методу Publish, який змінює стан об'єкта з проекту на опублікований, досить, щоб вважати клас об'єктом домену, який не є анемічним?

Твої думки?


Чудове запитання. Я не маю для вас відповіді, оскільки я майже завжди закінчуюся абсолютно анемічно в дизайні з точно тієї ж причини - використовуючи / споживаючи / опромінюючи послуги з / у багатьох різних контекстах чи різних програмах.
hromanko

Велике запитання, чи хотілося б побачити дядька, мартінфовелера, ерічеванс та ін., Щоб задзвонити це. Тепер про мене піти і подумати довго
Martijn Verburg

Я вважаю, що постійно розвиваюся до анемічної моделі; і тому я борюся з цією самою річчю. Чудове запитання!
L-Four

Це також мій досвід роботи з DDD. Ми робимо це там, де я працюю, і ми завжди закінчуємося анемічними. Я раніше (і досі насправді) використовую Csla. Нашому архітектору не подобається, що ми анемічні, але він не зміг дати мені хорошої відповіді на те, що повинен робити об’єкт, якщо всі речі, які ви вказуєте, не можуть бути виконані в межах об'єкта. Зрештою, намагання бути пуристським DDD, здається, створює більше роботи, ніж варто. Я особисто думаю, що Csla та DDD уживаються (вони в принципах здаються однаковими), якщо ви готові залишити догму DDD позаду.
Енді

Ось приклад деяких методик, що застосовуються при моделюванні домену з поведінкової (не орієнтованої на даних) точки зору: medium.com/@wrong.about/…
Zapadlo

Відповіді:


66

Більшість плутанини, здається, стосується функціоналу, який взагалі не повинен існувати в доменній моделі:

  • Наполегливість ніколи не повинна бути в доменній моделі. Ніколи. Ось чому ви покладаєтесь на абстрактні типи, наприклад, IRepositoryякщо частина моделі коли-небудь потребує чогось, як отримати іншу частину моделі, і використовуйте ін'єкцію залежності або якусь подібну техніку, щоб підключити реалізацію. Тож удари, що з запису.

  • Авторизація, як правило, не є частиною вашої доменної моделі, за винятком випадків, коли вона фактично є частиною домену, наприклад, якщо ви пишете програмне забезпечення безпеки. Механіка того, кому дозволено виконувати те, що в додатку, як правило, обробляється на "межі" рівня бізнесу / домену, загальнодоступних частин, з якими фактично дозволяється спілкуватися з інтерфейсом користувача та Інтеграції - Контролером в MVC, Сервісами або сама система обміну повідомленнями в SOA ... ви отримуєте зображення.

  • Фабрики (і я вважаю, ви тут маєте на увазі абстрактні фабрики) не зовсім погано мати доменну модель, але вони майже завжди непотрібні. Зазвичай у вас є лише завод, коли внутрішня механіка створення об'єктів може змінитися. Але у вас є лише одна реалізація доменної моделі, а це означає, що завжди буде лише одна фабрика, яка завжди викликає ті самі конструктори та інший код ініціалізації.

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

Тож як тільки ви дернете все це, це просто залишає перевірку. Це єдиний, який такий складний.

Перевірка є частиною вашої доменної моделі, але вона також є частиною всіх інших компонентів програми. Ваш інтерфейс користувача та база даних матимуть власні, подібні, але різні правила перевірки, засновані на подібній, але різній концептуальній моделі. Насправді не вказано, чи потрібно об’єктам мати Validateметод, але навіть якщо вони є, вони зазвичай делегують його класу валідатора (не інтерфейс - перевірка не є абстрактною в доменній моделі, вона є основоположною).

Майте на увазі, що валідатор технічно все ще є частиною моделі; його не потрібно прикріплювати до сукупного кореня, оскільки він не містить жодних даних або стану. Доменні моделі - це концептуальні речі, як правило, фізично перекладені на збірку або набір збірок. Не наголошуйте на проблемі "анемії", якщо код делегації знаходиться в дуже близькій до об'єктної моделі формі; це все ще рахується.

Що все це на самому справі зводиться до того, що якщо ви збираєтеся зробити DDD, ви повинні розуміти , що домен є . Якщо ви все ще говорите про такі речі, як наполегливість та авторизація, то ви не в цьому шляху. Домен представляє стан роботи системи - фізичні та концептуальні об'єкти та атрибути. Все, що не має безпосереднього відношення до самих об'єктів та відносин, не належить до доменної моделі, періоду.

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

"Чи може ця функціональність колись змінюватися з чисто технічних причин?" Іншими словами, чи не через якісь помітні зміни у реальному бізнесі чи домені?

Якщо відповідь "так", вона не належить до доменної моделі. Це не частина домену.

Є дуже хороший шанс, що коли-небудь ви зміните свою стійкість та інфраструктуру авторизації. Тому вони не є частиною домену, вони є частиною програми. Це стосується також алгоритмів, таких як сортування та пошук; вам не слід запускати та вводити реалізацію двійкового коду пошуку у вашу модель домену, оскільки ваш домен стосується лише абстрактної концепції пошуку, а не того, як це працює.

Якщо після того, як ви позбавили всіх речей, які не мають значення, виявите, що модель домену справді анемічна , то це повинно слугувати досить хорошим свідченням того, що DDD - це просто неправильна парадигма для вашого проекту.

Деякі домени дійсно є анемією. Програми соціальних закладок насправді не мають великого «домену», про який можна говорити; усі ваші об'єкти - це лише дані, не функціональні. З іншого боку, система Sales and CRM має досить важкий домен; коли ви завантажуєте Rateсуб'єкт господарювання, то є обґрунтовані очікування, що ви можете реально робити речі з такою ставкою, як-от застосувати його до кількості замовлення та запропонувати йому з'ясувати об'ємні знижки та промо-коди та все, що цікаво.

Об'єкти домену , які просто містять дані , як правило , дійсно означають , що у вас є анемія моделі предметної області, але це не обов'язково означає , що ви створили поганий дизайн - це може просто означати , що сам домен анемія , і що ви повинні використовувати інша методологія.


2
Крім того, @SonOfPirate, це не зовсім маловірозно, що ви хочете колись змінити всю свою модель безпеки; Захист на основі ролі часто застаріває на користь безпеки, що базується на претензіях або правах, або, можливо, ви навіть хочете захистити полевий рівень. Уявіть, що намагаєтесь переробити всю модель домену, якщо це сталося.
Aaronaught

1
@SonOfPirate: Це майже звучить так, ніби ви ще трохи застрягли в старій моделі "ділового рівня", в якій був "середній рівень", який в основному був тонким шпоном над рівнем даних, реалізуючи різні бізнес-правила, а також зазвичай правила безпеки . Це не той доменний шар. Домен - це те, від чого залежить все інше , він представляє об'єкти та відносини реального світу, якими система призначена для управління.
Aaronaught

1
@ LaurentBourgault-Roy: Вибач, я не вірю тобі. Кожна компанія могла сказати це про кожну заявку; В кінці кінців, зміна бази даних є важко. Це не робить його частиною вашого домену, а бізнес-логіка, яка поєднується з наполегливим шаром, просто означає погану абстракцію. Модель домену орієнтована на поведінку, саме те, що наполегливість не є . Це не тема, з якої люди можуть вигадувати власні визначення; це досить чітко прописано в DDD. Вам часто не потрібна модель домену для CRUD або додатків для звітування, але ви також не повинні стверджувати, що у вас вона є, коли її немає.
Aaronaught

1
Авторизація абсолютно належить до доменного шару. Хто вирішує, які дозволи існують? Бізнес робить. Хто вирішує, хто що може робити? Бізнес робить. Ми лише кілька тижнів тому мали запит на зміну авторизації, необхідної для редагування певного об'єкта в нашій системі. Якщо шаблон базувався на головному шаблоні, тоді для редагування (замінення значень головного) шаблону були потрібні більш високі привілеї, ніж зазвичай. Де ця логіка належить, якщо не в домені?
Енді

1
Іншим видом авторизації може бути ліміт облікових записів клієнтів. Звичайні люди з обслуговування клієнтів можуть підняти його до певного моменту, але, можливо, більш високі межі потребують схвалення керівництва. Це логіка авторизації.
Енді

6

Авторизація. Якщо об'єкт домену несе відповідальність за підтримку правил контролю доступу

Ні. Авторизація - це турбота про себе. Команди, які не були б дійсними через відсутність дозволів, повинні бути відхилені перед доменом якомога раніше - а це означає, що часто ми навіть хочемо перевірити авторизацію потенційної команди, щоб створити інтерфейс (щоб не навіть показати користувачеві можливість редагування).

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

Одна складна частина, з якою можна зіткнутися, - це контекстна авторизація, де команда може бути або не може бути дозволена не лише на основі ролей користувача, але і ділових даних / правил.

Перевірка. Те саме обговорення, що й вище, стосується валідації.

Я б також сказав "ні", не в домені (в основному). Перевірка відбувається в різних контекстах, і правила валідації часто відрізняються між контекстами. Рідко буває просте, абсолютне почуття дійсного чи недійсного при розгляді даних, капсульованих сукупністю.

Крім того, як і авторизація, ми використовуємо логіку перевірки на різних рівнях - в інтерфейсі користувача, в сервісі або команді, що працює з обробкою команд тощо. Знову ж, простіше використовувати DRY з валідацією, якщо це окремий компонент. З практичної точки зору, перевірка (особливо при використанні фреймворків) вимагає викриття даних, які в іншому випадку повинні бути капсульовані, і це часто вимагає приєднання спеціальних атрибутів до полів та властивостей. Я дуже вважаю за краще, щоб вони були в інших класах, ніж мої доменні моделі.

Я б скоріше дублював деякі властивості в декількох подібних класах, ніж намагався примусити вимоги до рамки перевірки в мої сутності. Це неминуче призводить до безладу класів сутності.

Створення об'єкта. Фабричний клас проти заводських методів порівняно з "нововведенням" екземпляра.

Я використовую один шар непрямості. У моїх останніх проектах це команда + обробник для створення чогось, тобто CreateNewAccountCommand. Альтернативою може бути завжди використання фабрики (хоча це може бути незручно, якщо решта операцій сутностей піддаються класу обслуговування, який є окремим від заводського класу).

В цілому, проте, я намагаюся бути більш гнучким у виборі дизайну для створення об’єктів. newлегко і звично, але не завжди достатньо. Я думаю, що тут важливо використовувати судження і дозволяти різним частинам системи використовувати різні стратегії за потребою.

Наполегливість. ... чому б не мати метод "Зберегти" на нашому об'єкті домену

Це рідко гарна ідея; Я думаю, що існує достатньо спільного досвіду для цього.

Якщо я закінчую IAuthorizationService, IValidator, IFactory та IRepository для мого сукупного кореня, що залишається? Чи достатньо методу Publish, який змінює стан об'єкта з Чернетки на Опублікований, достатньо, щоб вважати клас об'єктом не анемічного домену ???

Можливо, модель домену не є правильним вибором для цієї частини вашої програми.


2
"Одна складна частина, з якою можна зіткнутися, - це контекстна авторизація, де команда може бути або не може бути дозволена не тільки на основі ролей користувача, але і ділових даних / правил." - А як ви до цього підходите? Більше, ніж ні, принаймні для мене наші правила авторизації - це поєднання ролі та поточного стану.
SonOfPirate

@SonOfPirate: обробники подій, які слухають події домену та таблиці оновлень, які є дуже специфічними для потреб запитів, що перевіряють авторизацію. Логіка, яка розглядає стан і визначає, чи роль чи особа авторизована чи не живе в оброблювачі подій (тому таблиці майже завжди є простим "так / ні", що змушує автентичну перевірку простий пошук). Крім того, як тільки будь-яка з цієї логіки використовується в більш ніж одному місці, вона переробляється з обробника і переходить у спільну службу.
квентін-старін

1
Взагалі за останні кілька років я все більше й більше віддалявся від спроб консолідувати все у домені чи бізнес-об’єкті. Мій досвід здається, що зробити речі більш деталізованими та менш сполученими - це довгострокова виграш. Тому, хоча з одного погляду цей метод розміщує бізнес-логіку поза доменом (до певної міри), він також підтримує спритні модифікації пізніше. Вся справа в тому, щоб досягти рівноваги.
квентін-зірин

4
Посилання на саму відповідь тут: Я вважаю, що зразок стану є дуже корисним при роботі з валідаціями, які залежать як від дозволів, так і від правил бізнесу. Створіть абстрактний стан, який буде введений у модель домену та викриває методи, які приймають об’єкт домену як параметр та перевіряють дії, що стосуються домену. Таким чином, якщо ваші правила дозволу змінюються (як це майже завжди робиться), вам ніколи не доведеться возитися з моделлю для його розміщення, оскільки реалізація стану живе у вашому компоненті безпеки.
Aaronaught

1
Залишаючись на темі дозволу, дозвольте накинути на стіл відчутний приклад, щоб побачити, як ви (обидва) поводилися з цим. На моєму доменному об’єкті я опублікував операцію Publish, яка вимагає, щоб користувач був у ролі Видавця І що об’єкт перебуває у певному стані. Я хочу приховати або відключити "Кнопку" Опублікувати в інтерфейсі користувача. Як би ви це зробили?
SonOfPirate

4

Гаразд, тут іде для мене. Я попередньо виправдаю це, сказавши, що:

  • Передчасна оптимізація (а це включає дизайн) часто може спричинити проблеми.

  • IANMF (я не Мартін Фаулер);)

  • Брудний маленький секрет полягає в тому, що для проектів невеликих розмірів (навіть, мабуть, середніх розмірів) важлива буде послідовність вашого підходу.

Авторизація

Для мене автентифікація та авторизація завжди є актуальною проблемою. У моєму щасливому маленькому світі Java, який делегується на безпеку Spring або рамку Apache Shiro.

Перевірка Для мене валідація є частиною об'єкта, як я вважаю, що визначає, що таке об'єкт.

наприклад, об'єкт "Автомобіль" має 4 колеса (гаразд, є деякі дивні винятки, але поки що ігноруємо дивний 3-колісний автомобіль). Автомобіль просто не дійсний, якщо в ньому немає 4 (в моєму світі), тож перевірка є частиною того, що є визначенням Автомобіля. Це не означає, що ви не можете мати класи перевірки помічників.

У своєму щасливому світі Java я використовую рамки перевірки Bean і використовую прості примітки на більшості моїх полів Bean. Тоді легко перевірити об'єкт незалежно від того, в якому шарі ви знаходитесь.

Створення об'єкта

Я дивлюся фабричні класи з обережністю. Занадто часто я бачу xyxFactoryFactoryклас;)

Я схильний просто створювати newоб’єкт за потребою, поки не зіткнувся з випадком, коли введення залежностей є гарантованим (і оскільки я намагаюся дотримуватися підходу TDD, це виникає частіше, ніж ні).

У моєму щасливому світі Java, де все частіше Гейс, але Весна все ще є Королем.

Наполегливість

Отже, це дебати, які тривають у колах і кругових роз'їздах, і я завжди про це думаю.

Деякі кажуть, що якщо дивитися на об'єкт «чисто», наполегливість не є основною властивістю, це лише зовнішня турбота.

Інші вважають, що ваші доменні об’єкти неявно реалізують інтерфейс "зберігається" (так, я знаю, я тут розтягуюся). Таким чином, це нормально мати різні save, і deleteт.д. методи на них. Це сприймається як прагматичний підхід, і багато технологій ORM (JPA в моєму щасливому світі Java) таким чином займаються об'єктами.

З поважної проблеми безпеки я переконуюсь, що права редагування / видалення / додавання / будь-яких дозволів встановлені правильно на службі, яка викликає метод збереження / оновлення / видалення на об'єкті. Якщо я справді параноїк, я можу навіть встановити дозволи на сам об’єкт домену.

HTH!


2

Джиммі Нілссон торкається цієї теми у своїй книзі про DDD. Він почав з анемічної моделі, пішов на неанемічні моделі за пізнішим проектом і, нарешті, зупинився на анемічних моделях. Його міркування полягали в тому, що анемічні моделі можуть бути повторно використані в декількох сервісах з різною логікою бізнесу.

Компроміс - це відсутність здатності до виявлення. Методи, які можна використовувати для роботи з вашими анемічними моделями, поширюються в наборі служб, розташованих в іншому місці.


Звучить специфічна вимога - повторне використання структури даних (наголос "дані") - це призводить до того, що загальна частина зводиться до звичайних DTO.
Віктор Сергієнко

Анемічні моделі дозволяють краще використовувати повторно? Звучить більше як DTO, і я особисто не греблю про повторне використання визначення властивостей. Я набагато краще скористатись поведінкою.
Енді

@Andy - Я погодився би, якщо ваша поведінка знаходиться в доменних службах і вони працюють на анемічних об'єктах (нормально DTO, якщо ви хочете), чи не збільшує це повторне використання такої поведінки? Просто грає захисник диявола.
jpierson

@jpierson Я виявив, що поведінка зазвичай специфічна для конкретного випадку використання. Якщо є повторне використання, це буде належати до іншого класу, але споживач не буде використовувати ці класи, він буде використовувати ті, специфічні для випадку використання. Тож будь-яке повторне використання "так би мовити", так би мовити. Крім того, спроба повторно використовувати моделі, як правило, ускладнює використання споживачам моделей, тому ви в кінцевому підсумку створюєте перегляд / редагування моделей на рівні інтерфейсу користувача. Часто тоді ви стикаєтесь із порушенням DRY, щоб забезпечити більш багатий досвід користувача (наприклад, DataAnnotations редагуючих моделей).
Енді

1
Я вважаю за краще, щоб доменні модулі, побудовані для випадку використання, повторно використовувалися там, де це має сенс (тобто повторне використання може бути здійснено, не змінюючи поведінку сильно або зовсім). Тож замість анемічної доменної моделі, класу обслуговування та моделі редагування у вас є одна розумна редагована модель домену. Набагато простіше використовувати та підтримувати, я знайшов.
Енді

2

Це питання задавали давно, але воно позначене дизайном, керованим доменом. Я думаю, що саме питання містить фундаментальне нерозуміння всієї практики, а відповіді, включаючи прийняту відповідь, увічнюють фундаментальне непорозуміння.

У архітектурі DDD немає "доменної моделі".

Візьмемо Авторизацію як приклад. Дозвольте попросити вас подумати над питанням: уявіть, що два різні користувачі аутентифікуються у вашій системі. Один користувач має дозвіл змінити певну сутність, а інший - ні. Чому ні?

Я ненавиджу прості і надумані приклади, тому що вони часто плутають більше, ніж освічують. Але зробимо вигляд, що у нас є дві різні сфери. По-перше, це платформа CMS для маркетингового агентства. У цього агентства є багато клієнтів, у яких є вміст в Інтернеті, яким потрібно керувати копіювальники та графіки. Вміст включає публікації в блогах, а також цільові сторінки для різних клієнтів.

Інший домен - управління запасами для взуттєвої компанії. Система управляє товарно-матеріальними запасами, коли вона надходить від виробника у Францію, до торгових центрів у континентальних США, до роздрібних магазинів на місцевих ринках і, нарешті, до замовника, який купує взуття в роздріб.

Якщо ви вважаєте, що правила авторизації однакові для обох компаній, то так, це було б хорошим кандидатом на послугу за межами домену. Але я сумніваюся, що правила авторизації однакові. Навіть концепції, що стоять за користувачами, були б іншими. Звичайно, мова була б іншою. Маркетингове агентство, ймовірно, має такі ролі, як автор пошти та власник активів, в той час як взуттєва компанія, ймовірно, виконує ролі, такі як канцеляр з доставки або менеджер складів, або менеджер магазину.

Ці концепції, ймовірно, мають усі види правил дозволу, пов'язані з ними, які потрібно моделювати в домені. Але це не означає, що всі вони є однією і тією ж моделлю навіть в одній програмі. Бо пам’ятайте, що існують різні обмежені контексти.

Тому, можливо, можна вважати, що не анемічна модель домену в контексті Авторизації відрізняється від контексту маршрутизації відвантажень взуття до магазинів з низьким запасом або відвідувачів сайту на відповідну цільову сторінку залежно від того, яке оголошення вони натиснули.

Якщо ви опинилися з анемічними моделями доменів, можливо, вам просто потрібно витратити більше часу на відображення контексту, перш ніж почати писати код.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.