Позволяют ли ORM создавать модели богатых доменов?

21

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

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

Некоторые из проблем, которые я нашел:

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

Мои вопросы:

  • кто-нибудь был в подобной ситуации и не согласен с подходом к процедуре хранения? что ты сделал?
  • Есть ли реальная выгода от использования хранимых процедур? кроме глупой точки «никто не может выпустить дроп-стол».
  • Есть ли способ создать богатый домен с помощью хранимых процедур? Я знаю, что есть возможность использовать AOP для внедрения DAO / Repositories в сущности, чтобы иметь возможность перемещаться по графу объектов. Мне не нравится этот вариант, так как он очень близок к вуду.

Вывод

Во-первых, спасибо всем за ваши ответы. Я пришел к выводу, что ORM не позволяют создавать модели Rich Domain (как упоминали некоторые люди), но это упрощает объем (часто повторяющихся) работ. Ниже приведено более подробное объяснение заключения, но оно не основано на каких-либо достоверных данных.

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

Если мы берем базу данных приложения в качестве внешней системы, то нам нужно создать абстракцию, которая позволяет нам отображать сущности модели домена в базу данных ( как упомянул НимЧимпски , используя средство отображения данных). Очевидное различие заключается в том, что теперь нам необходимо вручную сопоставить для каждой сущности модели базу данных (либо унаследованную схему, либо хранимые процедуры), при этом возникает дополнительная проблема, заключающаяся в том, что, поскольку эти два объекта не синхронизированы, одна сущность домена может отображаться частично к объекту базы данных (например, класс UserCredentials, который содержит только имя пользователя и пароль, сопоставлен с таблицей Users, имеющей другие столбцы), или один объект модели домена может отображаться на более чем один объект базы данных (например, если существует однозначный одно отображение на таблицу, но мы хотим, чтобы все данные были только в одном классе).

В приложении с несколькими сущностями объем дополнительной работы может быть небольшим, если нет необходимости перемещать сущности, но он увеличивается, когда возникает условная необходимость пересечь сущности (и, таким образом, мы можем захотеть реализовать своего рода «ленивый»). загрузка '). По мере того, как в приложении растет число сущностей, эта работа только увеличивается (и у меня есть чувство, что оно увеличивается нелинейно). Здесь я предполагаю, что мы не пытаемся заново изобрести ORM.

Одно из преимуществ обработки БД как внешней системы заключается в том, что мы можем кодировать ситуации, в которых нам нужны 2 разные версии приложения, в которых каждое приложение имеет свое сопоставление. Это становится более интересным в сценарии непрерывных поставок в производство ... но я думаю, что это также возможно с ORM в меньшей степени.

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


Небольшое обновление (6/6/2012)

Хранимые процедуры (по крайней мере, в Oracle) не позволяют делать что-либо, например, непрерывную доставку с нулевым временем простоя, поскольку любое изменение структуры таблиц приведет к аннулированию процедур и триггеров. Поэтому во время обновления БД приложение также будет недоступно. Oracle предлагает решение для этого, называемого Переопределение на основе редакции , но несколько администраторов баз данных, которых я спрашивал об этой функции, отметили, что она плохо реализована и не поместит ее в рабочую БД.

Аугусто
источник
Ну, очевидно, вы могли бы делать то, что делает Hibernate, и использовать наследование для генерации динамического прокси-объекта, который позволяет вам получать граф объекта. Это очень хакерски с SP: D
Макс
Так что я бы в итоге переосмыслил половину hibernate, без 10+ лет опыта, который есть у команды hibernate :).
Августо
1
Любой администратор базы данных должен предотвращать удаление определенных таблиц определенными пользователями. Неважно, как вы пытаетесь это сделать.
JeffO
1
Вы можете взглянуть на Mybatis - он может предоставить вам необходимую функцию. Это меньше ORM, чем структура отображения. Вы можете написать SQL так, как вам нравится, и сообщить Mybatis, куда поместить его в вашу объектную модель. Он будет обрабатывать большие графы объектов с несколькими запросами, что походит на вашу ситуацию (множество тонких хранимых процедур).
Майкл К
1
@ Августо: Я был в подобной ситуации, не из-за использования SP, но из-за использования проприетарной структуры отображения, которая не поддерживала объектные отношения. Мы потратили дни на написание кода, который можно написать за несколько минут, используя правильный ORM. Я никогда не решал эту проблему.
Кевин Клайн

Ответы:

16

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

Мартейн Вербург
источник
Мартейн, я согласен, что приложение должно быть смоделировано с использованием принципов DDD, но проблема, с которой я сталкиваюсь (и, пожалуйста, скажите мне, если есть решение !!), заключается в том, что некоторые хранимые процессы возвращают очень мало информации для создания экземпляра сущности DDD. Пожалуйста, смотрите этот комментарий, где я объяснил немного больше информации, которую возвращают хранимые процедуры. Я мог бы обойти это, вызвав более одного сохраненного процесса, и, например, восстановив все данные пользователя, а затем вызвав другой, чтобы получить всю информацию об учетной записи, но это выглядит неправильно :).
Августо
1
@ Августо Ну ... вы разработчик приложения, поэтому вам нужно решить, имеет ли смысл существование определенного объекта с определенными полями, для которых установлено значение NULL. Если это имеет смысл (например, для определенной задачи), пусть так и будет. Если нет, попросите автора SP предоставить больше данных, чтобы вы могли создавать свои объекты.
Яцек Прусия
И добавление к комментарию Яцека - на самом деле вполне приемлемо вызывать 2+ хранимых прока, снова подумайте о них как о двух удаленных сервисах, которые вам нужно вызвать для создания модели вашего домена, ничего страшного в этом нет :-).
Мартейн Вербург
@ Martijn: По моему опыту, тонкий слой не достаточно. Код отображения может быть значительно длиннее базовой бизнес-логики.
Кевин Клайн
@Kevin cline - Хороший вопрос, поставили «с надеждой» в ответе :-)
Martijn Verburg
5

Есть ли реальная выгода от использования хранимых процедур?

В финансовом мире (и в местах, где требуется соблюдение закона Сарбейнса-Оксли ), вы должны иметь возможность проводить аудит систем, чтобы убедиться, что они делают то, что должны. В этих случаях гораздо проще обеспечить соответствие, когда весь доступ к данным осуществляется через хранимые процедуры. А когда весь специальный SQL удален, скрыть все гораздо сложнее. В качестве примера того, почему это было бы «хорошо», я отсылаю вас к классической статье Кена Томпсона « Размышления о доверии» .

Tangurena
источник
да миллион раз да! Вы также должны убедиться, что пользователи не могут делать ничего, чего они не должны делать, в том числе отсутствие прямых прав на таблицы и хранимые процедуры помогает в этом.
HLGEM
1
Я работаю в публичной компании, и мы согласны с SOX. Это может быть моим плохим знанием аудита, но я не вижу разницы между выполнением аудита на уровне БД (через хранимые процедуры) или на уровне приложения. Каждое приложение должно иметь свою собственную схему БД, и эта схема доступна только из приложения, а не может использоваться несколькими приложениями.
Августо
неработающая ссылка ...
Alex R
@AlexR, фиксированная ссылка
Tangurena
2

Хранимые процедуры намного более эффективны, чем и клиентский SQL-код. Они предварительно компилируют SQL в БД, что также позволяет ему выполнять некоторые оптимизации.

Архитектурно, SP возвратит минимальные данные, необходимые для задачи, что хорошо, поскольку означает, что передается меньше данных. Если у вас есть такая архитектура, вам нужно думать о БД как о сервисе (думать о нем как о веб-сервисе, а каждый SP - это метод для вызова). Работать с ним таким образом не должно быть проблемой, в то время как ORM поможет вам работать с удаленными данными, как если бы они были локальными, и, таким образом, обманом заставит вас столкнуться с проблемами производительности, если вы не будете осторожны.

Я был в ситуациях, когда мы полностью использовали SP, БД предоставила API данных, и мы использовали его. Это конкретное приложение было очень масштабным и работало на удивление хорошо. Я не скажу ничего плохого о СП после этого!

Есть еще одно преимущество: администраторы баз данных напишут все ваши запросы SQL для вас и с радостью будут обрабатывать всю реляционную иерархию в БД, так что вам не нужно это делать.

gbjbaanb
источник
3
gbjbaanb, большинство из того, что вы сказали, верно для старых баз данных. Большинство новых баз данных довольно часто перекомпилируют запросы, чтобы решить, какие новые оптимизации использовать (даже если они хранятся в памяти). Я согласен с тем, что вы сказали об использовании БД в качестве внешней системы, но я также считаю, что это большая работа, так как приложение владеет базой данных, и оба должны быть максимально синхронизированы. Например, с именами таблиц / классов и полей / столбцов. Кроме того, подход, позволяющий администраторам баз данных писать процедуры, пахнет как бункеры разработки, а не наличие междисциплинарной команды.
Августо
7
SP не обязательно всегда более эффективны, и я думаю, что передача SQL администраторам баз данных - плохой путь. Как эксперт в области доменов, разработчик должен знать, какие данные они хотят получить и как их получить
Мартейн Вербург
1
Это хороший ответ, но, по моему опыту, большинству клиентов на самом деле не нужно повышение производительности для управления доступом к данным с помощью хранимых процедур, а не неудобство полного использования инструментов ORM на уровне приложений. Чаще всего я вижу эти архитектурные решения, принимаемые в магазинах программного обеспечения, где им необходимо оправдать раздутые зарплаты программистов хранимых процедур «седой бороды» , у которых нет других навыков.
maple_shaft
1
@ Августо the approach of letting the DBAs write the procedures smells like development silos+100 интернетов к тебе за этот камень правды. Я всегда видел это как случай, когда доступ к данным контролировался с помощью хранимых процедур.
maple_shaft
1
@maple_shaft: почему администраторы БД, которые пишут SP, не считаются частью команды разработчиков? Там, где это работает, они являются специализированными программистами, которые действительно хорошо знают этот аспект системы, гораздо лучше, чем большинство разработчиков общего назначения. Это может быть проблемой, которая привела к популярности ORM. Я имею в виду, что никто не подумал бы дважды о том, чтобы дизайнер делал GUI, так почему же ненавистно для архитектора данных, делающего схему?
gbjbaanb
2

Часто случается, что разработчики неправильно используют свои объекты ORM в качестве моделей своих доменов.

Это неверно и привязывает ваш домен напрямую к вашей схеме БД.

Что должно быть на самом деле, так это раздельные доменные модели, настолько богатые, насколько вам нравится, и использование слоя ORM отдельно.

Это означает, что вам потребуется сопоставление между каждым набором объектов.

Ozz
источник
1
Это хорошая идея, но для небольших проектов она начинает казаться излишней. Этот подход также требует уровня трансляции между уровнем персистентности ORM и моделью предметной области.
maple_shaft
@maple_shaft согласился, и именно это я имел в виду под «картированием» :-)
ozz
@ Ozz, я работал именно так, классы сущностей являются моделью предметной области (и я мог бы добавить с большим успехом). Я согласен, что это связывает модель предметной области со схемой, но это именно то, что я хочу, так как я использую соглашение по конфигурации, и приятным побочным эффектом является то, что если я вижу поле на объекте, мне не нужно задумываться о названии таблицы и столбца, где хранится эта информация.
Августо
@ Августо, я тоже это сделал! и, как говорит maple_shaft, это хорошо для небольших приложений в стиле CRUD, но есть много проблем, которые обнаруживает OP. Одним из примеров может быть ситуация, когда у вас есть таблица сопоставления «многие ко многим», например: StudentClasses, которая отображает студентов в их классы и содержит только StudentID и classID, вам не обязательно отображать это в вашем домене. Это просто быстрый пример моей головы.
Оз
2
@Ozz: Ваш комментарий противоречит самой идее ORM. ORM не «привязывает ваш домен напрямую к вашей схеме БД». ORM сопоставляет ваш домен со схемой БД, без необходимости отдельного уровня DAO. В этом весь смысл ORM. И большинство ORM прекрасно справляются с сопоставлениями «многие ко многим», при этом для таблицы сопоставления не требуется модель предметной области.
Кевин Клайн
1

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

ним чимпский
источник
В настоящее время мы используем средства отображения данных, но проблема в том, что хранимые процедуры возвращают минимальный набор данных, которого иногда недостаточно для заполнения объекта (возможно, нам следует разрешить хранимым процедурам возвращать больше информации). Например, один магазинный магазин может вернуть адрес электронной почты, имя, фамилию пользователя; в то время как другой добавляет идентификатор пользователя и адрес. Поскольку данные разные, мы используем разные объекты для хранения данных, что означает, что у нас разные классы «Пользователь». Я пытаюсь избежать использования наследования здесь, потому что я думаю, что это неправильное использование.
Августо
@Augusto: Интерфейсы?
Kramii восстановит Монику
@ Karmii, я не думаю, что здесь помогают интерфейсы, так как тогда нам нужно будет дублировать логику в разных классах. Или мы могли бы использовать интерфейсы, а затем делегировать обработку вспомогательному классу, но на самом деле это не OO :(.
Augusto
1
@ Августо Я не понимаю проблемы: «хранимые процессы возвращают минимальный набор данных, которого иногда недостаточно для заполнения объекта». Таким образом, вы изменяете sproc или создаете другой, а затем позволяете устройству отображения данных выполнять сопоставление
НимЧимпский