Чи прагнуть API RESTful заохочувати анемічні моделі домену?


34

Я працюю над проектом, в якому ми намагаємось застосувати як орієнтований на домен дизайн, так і REST до архітектури, орієнтованої на сервіс. Ми не турбуємось про 100% відповідність REST; можливо, було б краще сказати, що ми намагаємося побудувати орієнтовані на ресурси HTTP API (~ рівень 2 моделі зрілості Річардсона REST). Тим не менш, ми намагаємося триматися подалі від використання RPC-стилю HTTP запитів, тобто ми намагаємося реалізувати наші HTTP дієслова в відповідно до RFC2616 , а не використовувати , POSTщоб зробити IsPostalAddressValid(...), наприклад.

Однак акцент на цьому здається за рахунок нашої спроби застосувати дизайн, керований доменом. Тільки з GET, POST, PUT, DELETEі кілька інших рідко використовуваними методами, ми схильні будувати Cruddy послуги, а також послуги Cruddy , як правило, мають анемію моделі предметної області.

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

Чи REST і DDD є напруженими на якомусь рівні? (Або я щось тут не розумію? Чи ми можемо зробити щось інше не так?) Чи можливо побудувати міцну модель домену в архітектурі, орієнтованій на сервіс, уникаючи HTTP-дзвінків у стилі RPC?


1
POST було навмисно розроблено таким, щоб бути «навмисно розпливчастим»; результат POST залежить від впровадження. Що заважає вам робити те, що роблять Twitter та інші дизайнери API, і визначати кожен метод POST у частині, що не має CRUD, вашого API відповідно до ваших власних вимог?
Роберт Харві

@RobertHarvey Ми створили POST як створення. Знову дивлячись на стандарт , можливо, це занадто спрощено. Наприклад, чи вважаєте ви, що POST для того, щоб це зробити IsPostalAddressValid(...), відповідав би "Наданню блоку даних, наприклад результату подання форми, до процесу обробки даних"?
Казарк

Це тому, що немає дієслова CREATE (і для цього немає дієслова UPDATE). Я стверджую, що ці дієслова відсутні з стандартних (з будь-якої причини), тому вам доведеться кооптувати POST для "всього іншого". Ваш "процес обробки даних" в цьому випадку - це процес, який вивчає поштову адресу і повертає значення, що відповідає результату цього аналізу.
Роберт Харві

1
@RobertHarvey: Я вважаю, що POST і PUT / PATCH є просто дієсловом CREATE and UPDATE, якого ви хотіли. Його називають лише по-іншому, так що дієслово все-таки має певний сенс навіть у не-RESTful конструкції.
Лі Лі Раян

@LieRyan: Я вам це дозволю. Я просто думаю, що CRUD передбачає анемічні моделі даних за визначенням. Ви можете продовжувати деяку поведінку, якщо, скажімо, ви перебуваєте в MVC, але, звичайно, не в гетерогенних системах. Для всього іншого, крім CRUD, вам потрібно POST.
Роберт Харві

Відповіді:


38

Перший закон Мартіна Фаулера про розподілені системи: "Не поширюйте свої об'єкти!" Віддалені інтерфейси повинні бути грубозернистими, а внутрішні інтерфейси - дрібнозернистими. Часто модель багатого домену застосовується лише в обмеженому контексті .

API REST розділяє два різних контексти, обидва мають власні внутрішні моделі. Контексти спілкуються через грубозернистий інтерфейс (REST API), використовуючи "анемічні" об'єкти (DTO).

У вашому випадку це здається, що ви намагаєтесь поширити контекст через межу, яка є REST API. Це може призвести до дрібнозернистого віддаленого інтерфейсу або до анемічної моделі. Залежно від вашого проекту це може бути або не бути проблемою.


1
Фоулер має багато хороших думок, але не забуваймо, що було катастрофою оригінальних специфікацій та реалізацій EJB. Лише після цього вони з'ясували, що низький рівень викликів методу для кожної незначної операції, як getName (), був кошмаром мережі / завантаження. Грубозернистий інтерфейс став дорогою, і разом з цим поняття, що цілі графіки / повідомлення сутності були відправлені та отримані в контексті дієслова + іменника.
Даррелл Тейг

9

POST було навмисно розроблено таким, щоб бути «навмисно розпливчастим»; результат POST залежить від впровадження. Що заважає вам робити те, що роблять Twitter та інші дизайнери API, і визначати кожен метод POST у частині, що не має CRUD, вашого API відповідно до ваших власних вимог? POST - дієслово catchall. Використовуйте його, коли жоден з інших дієслів не підходить для операції, яку ви хочете виконати.

Інакше кажучи, ваше питання може бути однаково поставлене, як "Чи" розумні "об'єкти заохочують дизайн в стилі RPC?" Навіть Мартін Фаулер (який придумав термін "анемічна модель домену") визнає, що голі DTO мають певні переваги:

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

Що стосується моделі зрілості Річардсона , ви можете дійти до рівня 3, не зачіпаючи себе про "анемічні моделі доменів". Пам'ятайте, що ви ніколи не збираєтеся переносити поведінку в браузер (якщо ви не плануєте вводити Javascript через свої моделі).

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

Дивіться також
мені потрібно більше дієслів


Я думаю, що це, безумовно, стосується RFC2616 сторони питання. А що з тим, що ми намагаємось орієнтуватися на ресурси, тобто хоча б намагаємось досягти рівня 2 в моделі зрілості Річардсона для REST?
Казарк

1
Я читаю через martinfowler.com/articles/richardsonMaturanceModel.html . Ви можете дістатись до рівня 3, не задумуючись про "анемічні моделі доменів". Пам'ятайте, що ви ніколи не збираєтеся переносити поведінку в браузер (якщо ви не плануєте вводити Javascript через свої моделі).
Роберт Харві

4

API REST - це лише один тип шару презентації. Це не має нічого спільного з доменною моделлю.

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

Ви наносите модель домену на API REST так само, як ви домітуєте свою модель домену в RDBMS через ORM - повинен бути цей шар відображення.

Домен ← ORM → RDBMS
Домен ← Картування REST → API REST


3

IMHO Я не думаю, що вони прагнуть заохочувати анемічні моделі доменів (ADM), але вони вимагають від вас певного часу і продумуйте все.

Перш за все, я думаю, що головна характеристика ADM полягає в тому, що вони мало поведінки в них. Це не означає, що система не має поведінки, лише що вона зазвичай знаходиться в якомусь класі обслуговування (див. Http://vimeo.com/43598193 ).

І звичайно, якщо поведінки не існує в ADM, що робити? Відповідь звичайно - дані. І так, як ця карта відповідає REST API? Імовірно, дані відображають зміст ресурсу, а карти поведінки - HTTP-дієслова.

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

Я думаю, що люди, як правило, стикаються з проблемами, це те, що їм важко бачити, як дієслова HTTP відображаються на поведінку свого домену, коли поведінка виходить за рамки простого CRUD, тобто коли є інші побічні ефекти в інших частинах домену поза ресурс, що змінюється запитом HTTP. Одним із способів вирішити цю проблему є події в домені ( http://www.udidahan.com/2009/06/14/domain-events-salvation/ ).


3

Ця стаття досить пов'язана з темою, і я вважаю, що відповідає на ваше запитання.

Основне поняття, яке, на мою думку, дуже добре відповідає на ваше запитання, узагальнено у наступному параграфі згаданої статті:

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


1

Кілька досить успішних реалізацій, які я бачив / побудував, відповідають на питання про те, як вони змішують метафору дієслова + іменника, використовуючи грубозернисті «бізнес-приємні» методи, що діють на сутність.

Тож замість getName()методу / послуги (приреченого) викрийте getPerson(), передаючи такі речі, як ідентифікатор-тип / ідентифікатор, повертаючи всю Personсутність.

Оскільки поведінку суб'єкта Особи в такому контексті не може бути адекватно передано (і, можливо, вони не повинні бути в контексті, орієнтованому на дані, як це), цілком розумно визначити модель даних (проти об'єкта) для пар запитів / відповідей послуга / с.

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

Я не погоджуюся з авторами, які б сказали, що, наприклад, реалізація дієслова передачі належить до класу Person або пов'язана з службою, орієнтованою на особи. Дійсно, спосіб передачі для a Personта його варіанти (у цьому простому прикладі) було б краще визначено a Carrier, де Personможуть не знати навіть того, які способи передачі доступні або як передача взагалі відбувається (хто знає, як працюють реактивні двигуни все одно).

Це робить Personсуб'єкт анемічним? Я не думаю, що так.

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

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


Хороші моменти - це дійсно приносить певну перспективу, якої інших відповідей ще не було.
Казарк

0

Чи є ваша проблема в тому, що ви намагаєтеся вписати свою модель в основний набір дієслів, використовуючи POST якомога більше?

Це не потрібно - я знаю, що для більшості людей REST означає POST, GET, PUT і DELETE, але http rfc говорить:

Набір загальних методів для HTTP / 1.1 визначено нижче. Хоча цей набір можна розширити, не можна вважати, що додаткові методи мають спільну семантику для окремо розширених клієнтів та серверів.

А такі системи, як SMTP, використовують один і той же стиль методів, заснованих на дієсловах, але з абсолютно іншим набором.

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

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

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