RESTful API стимулируют анемичные доменные модели?

34

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

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

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

Находятся ли REST и DDD в напряжении на каком-то уровне? (Или я что-то здесь неправильно понимаю? Возможно, мы делаем что-то еще не так?) Возможно ли построить сильную модель предметной области в сервис-ориентированной архитектуре, избегая при этом вызовов HTTP в стиле RPC?

Kazark
источник
1
POST был намеренно разработан, чтобы быть «намеренно расплывчатым»; Результат POST зависит от реализации. Что мешает вам делать то, что делают Twitter и другие разработчики API, и определять каждый метод POST в части API, не относящейся к CRUD, в соответствии с вашими конкретными требованиями?
Роберт Харви
@RobertHarvey Мы создали POST как творение. Глядя на стандарт снова, возможно, это слишком упрощенно. Например, думаете ли вы, что POST для этого IsPostalAddressValid(...)будет соответствовать "Предоставлению блока данных, такого как результат отправки формы, процессу обработки данных"?
Казарк
Это потому, что нет глагола CREATE (и глагола UPDATE, в этом отношении). Я утверждаю, что эти глаголы отсутствуют в стандарте (по какой-либо причине), поэтому вы должны выбрать POST для «всего остального». Ваш «процесс обработки данных» в данном случае - это процесс, который проверяет почтовый адрес и возвращает значение, соответствующее результату этого анализа.
Роберт Харви
1
@RobertHarvey: Я считаю, что POST и PUT / PATCH - это просто глагол CREATE и UPDATE, который вы хотели. Он называется только по-разному, поэтому глагол по-прежнему имеет смысл даже в не-RESTful дизайне.
Ли Райан
@LieRyan: Я дам тебе это. Я просто думаю, что CRUD подразумевает анемичные модели данных по определению. Вы можете вести себя по-своему, если, скажем, вы находитесь в M MVC, но, конечно, не в гетерогенных системах. Для всего остального, кроме CRUD, вам нужен POST.
Роберт Харви

Ответы:

38

Первый закон распределенных систем Мартина Фаулера: «Не распространяй свои объекты!» Удаленные интерфейсы должны быть грубыми, а внутренние интерфейсы - точными. Зачастую модель богатых доменов применяется только в ограниченном контексте .

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

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

simoraman
источник
1
У Фаулера много хороших мыслей, но давайте не будем забывать, какой катастрофой были исходные спецификации и реализации EJB. Только после этого они выяснили, что низкоуровневые вызовы методов для каждой второстепенной операции, такой как getName (), были кошмаром сети / загрузки. Грубые интерфейсы стали подходом, а вместе с ним и концепцией, что целые графы / сообщения сущностей были отправлены и получены в контексте глагол + существительное.
Даррелл Тиг
9

POST был намеренно разработан, чтобы быть «намеренно расплывчатым»; Результат POST зависит от реализации. Что мешает вам делать то, что делают Twitter и другие разработчики API, и определять каждый метод POST в части API, не относящейся к CRUD, в соответствии с вашими конкретными требованиями? POST - это заглавный глагол. Используйте его, когда ни один из других глаголов не подходит для операции, которую вы хотите выполнить.

Другими словами, ваш вопрос может быть в равной степени сформулирован так: «Стимулируют ли« умные »объекты дизайн в стиле RPC?» Даже Мартин Фаулер (который придумал термин «Модель анемичной области») признает, что голые DTO имеют некоторые преимущества:

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

Что касается модели зрелости Ричардсона , вы можете перейти на уровень 3, не беспокоясь о «моделях анемичной области». Помните, что вы никогда не собираетесь передавать поведение в браузер (если только вы не планируете вводить Javascript через свои модели).

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

Смотрите также
мне нужно больше глаголов

Роберт Харви
источник
Я думаю, что это определенно касается стороны вопроса RFC2616. Как насчет того, что мы пытаемся ориентироваться на ресурсы, то есть, по крайней мере, пытаться достичь уровня 2 в модели зрелости Ричардсона для REST?
Казарк
1
Я прочитал martinfowler.com/articles/richardsonMaturityModel.html . Вы можете добраться до уровня 3, не беспокоясь о «Анемичных Доменных Моделях». Помните, что вы никогда не собираетесь передавать поведение в браузер (если только вы не планируете вводить Javascript через свои модели).
Роберт Харви
4

REST API - это всего лишь один тип уровня представления. Это не имеет ничего общего с моделью предметной области.

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

Вы сопоставляете модель своего домена с REST API так же, как вы сопоставляете модель своего домена с RDBMS через ORM - должен существовать этот слой отображения.

Домен ← ORM →
Домен СУБД ← Отображение REST → API REST

Эльнур Абдуррахимов
источник
3

ИМХО, я не думаю, что они склонны поощрять модели анемичных доменов (ADM), но они требуют, чтобы вы потратили некоторое время и все обдумали.

Прежде всего, я думаю, что основной характеристикой ADM является то, что они почти не ведут себя в них. Это не значит, что система не имеет поведения, просто она обычно находится в каком-то классе обслуживания (см. Http://vimeo.com/43598193 ).

И, конечно, если поведение не существует в ADM, то что же? Ответ, конечно же, данные. И как это соотносится с REST API? Предположительно, данные сопоставляются с содержимым ресурса, а поведение - с глаголами HTTP.

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

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

RibaldEddie
источник
3

Эта статья довольно связана с предметом, и я верю, что отвечает на ваш вопрос.

Основная концепция, которая, я думаю, хорошо отвечает на ваш вопрос, резюмируется в следующем абзаце из упомянутой статьи:

«Очень важно различать ресурсы в REST API и доменные объекты в доменно-ориентированном дизайне. Доменно-ориентированный проект применяется к аспектам реализации (включая реализацию API), в то время как ресурсы в REST API определяют структуру и контракт API. Ресурс API выбор не должен зависеть от деталей реализации базового домена. "

Majix
источник
1

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

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

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

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

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

Делает ли это Personсущность анемичной? Я так не думаю.

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

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

Даррелл Тиг
источник
Хорошие моменты - это действительно приносит некоторую свежую перспективу, которой другие ответы еще не имели.
Казарк
0

Ваша проблема в том, что вы пытаетесь втиснуть свою модель в базовый набор глаголов, максимально используя POST?

В этом нет необходимости - я знаю, что для большинства людей REST означает POST, GET, PUT и DELETE, но http rfc говорит:

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

И системы, такие как SMTP, используют тот же стиль методов, основанных на глаголах, но с совершенно другим набором.

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

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

gbjbaanb
источник