Какую бизнес-логику должна реализовать база данных?

108

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

Какой из этих подходов в целом лучше?

Плюсы реализации бизнес-логики в БД, о которых я могу думать:

  • Централизация бизнес-логики;
  • Независимость от типа приложения, языка программирования, ОС и т. Д .;
  • Базы данных менее подвержены миграции технологий или большим рефакторингам (AFAIK);
  • Никаких переделок по переносу технологий приложений (например: .NET на Java, Perl на Python и т. Д.).

Минусы:

  • SQL менее продуктивен и более сложен для программирования бизнес-логики из-за отсутствия библиотек и языковых конструкций, предлагаемых большинством прикладных языков;
  • Более сложное (если вообще возможно) повторное использование кода через библиотеки;
  • Менее продуктивные IDE.

Примечание: базы данных, о которых я говорю, являются реляционными, популярными базами данных, такими как SQL Server, Oracle, MySql и т. Д.

Спасибо!

Рафаэль
источник
3
Вы можете найти ответ на этот вопрос полезным.
Blrfl
7
Этот аргумент уже обсуждался исчерпывающе . Что еще мы могли бы значимо добавить к разговору здесь?
Роберт Харви
2
@gnat: Даже не близко.
Роберт Харви
7
Учтите, что база данных будет далеко ( далеко ) пережить ваше приложение. База данных может даже пережить язык, на котором вы пишете свое приложение. Как правило, сами данные являются бизнесом, и база данных должна быть в состоянии защитить целостность содержащихся в ней данных. В этом ключе каждое ограничение внешнего ключа, честно говоря, является реализацией бизнес-правила. Если вы не избавитесь от всех реляционных ограничений в вашей реляционной базе данных, вы действительно не сможете полностью извлечь бизнес-логику из базы данных.
Крейг

Ответы:

83

Бизнес логика не входит в базу данных

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

Базы данных делают несколько вещей действительно хорошо:

  1. Они хранят и получают данные
  2. Они устанавливают и обеспечивают связь между различными объектами данных
  3. Они предоставляют средства для запроса данных для ответов
  4. Они обеспечивают оптимизацию производительности.
  5. Они обеспечивают контроль доступа

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

И, конечно, могут быть вещи, которые выполняются в базе данных для производительности и по другим причинам:

  1. Закрытие отчетного периода
  2. Хруст номера
  3. Ночные пакетные процессы
  4. Отказоустойчивость

Естественно, ничего не выгравировано на камне. Хранимые процедуры подходят для широкого круга задач просто потому, что они находятся на сервере базы данных и имеют определенные преимущества и преимущества.

Хранимые процедуры везде

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

Но чем вы рискуете?

  1. Блокировка поставщика
  2. Потребность в разработчиках со специальными наборами навыков
  3. Спартанские инструменты программирования, в целом
  4. Чрезвычайно жесткая программная связь
  5. Нет разделения интересов

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

Итак, что является типичной практикой?

Я бы сказал, что типичным современным подходом является использование Object-Relational Mapper (например, Entity Framework) для создания классов, моделирующих ваши таблицы. Затем вы можете обращаться к своей базе данных через репозиторий, который возвращает коллекции объектов, и эта ситуация хорошо знакома любому компетентному разработчику программного обеспечения. ORM динамически генерирует SQL, соответствующий вашей модели данных и запрашиваемой информации, который затем обрабатывает сервер базы данных для возврата результатов запроса.

Насколько хорошо это работает? Очень хорошо и гораздо быстрее, чем написание хранимых процедур и представлений. Обычно это покрывает около 80% ваших требований к доступу к данным, в основном CRUD. Что покрывает остальные 20%? Вы уже догадались: хранимые процедуры, которые все основные ORM поддерживают напрямую.

Можете ли вы написать генератор кода, который делает то же самое, что и ORM, но с хранимыми процедурами? Что вы можете. Но ORM, как правило, независимы от поставщиков, понятны всем и лучше поддерживаются.

Роберт Харви
источник
3
Спасибо за отличный ответ, @ Роберт Харви. Но я думал о аргументе «блокировки поставщика»: не используете ли конкретную технологию (скажем, стек .NET или Java) для создания приложения, а также блокировки поставщика? Или есть ли преимущества привязки вендоров, ориентированных на приложения, по сравнению с базой данных?
Рафаэль
3
@ RobertHarvey, но та часть логики приложения, которая есть в .NET, все еще привязана к .NET. То же самое касается PHP и Java.
Pacerier
2
@Pacerier: от vendor-lockin я имею в виду поставщика базы данных. На практике база данных (и стек программирования) редко заменяются.
Роберт Харви
2
@ Кай: Ну, вы не можете иметь оба пути. Либо вы используете заглушки и насмешки и живете с тем фактом, что тест является искусственным, либо вы пишете реалистичный тест и живете с небольшой задержкой. Я сомневаюсь, что ваш компромисс составляет 10 минут против 30 секунд.
Роберт Харви
3
Возможно, поздно, но я придерживаюсь мнения, что хранимые процедуры, реализующие бизнес-логику, относятся к уровню бизнес-логики, а не к уровню данных. Это своего рода отдельный язык без необходимости ORM.
Паралифе
16

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

Я оспариваю ваши плюсы и минусы.

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

select <whatever>
from group g
where fn_invoker_has_access_to_group(g.group_id)

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

Распространенным решением вышеупомянутой проблемы является извлечение логики из функции и объединение ее в запрос. Теперь вы нарушили инкапсуляцию и дублировали логику.

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

declare some_cursor
while some_cursor has rows
    exec some_other_proc
end

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

В целом, я считаю, что базы данных плохи в:

  1. вычисление
  2. Итерация (они оптимизированы для операций над множествами)
  3. Балансировки нагрузки
  4. анализ

Базы данных хороши в:

  1. Блокировка и разблокировка
  2. Ведение данных и их взаимосвязи
  3. Обеспечение целостности

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

Однако вы правы в том, что он отделяет вашу бизнес-логику от языка программирования вашего приложения, но я не понимаю, почему это является преимуществом. Если у вас есть приложение на Java, значит, у вас есть приложение на Java. Преобразование набора Java-кода в хранимые процедуры не меняет того факта, что у вас есть Java-приложение.

Я предпочитаю, чтобы код базы данных был ориентирован на постоянство. Как вы создаете новый виджет? Вы должны вставить в 3 таблицы, и они должны быть в транзакции. Это принадлежит хранимой процедуре.

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

Brandon
источник
8
В SQL-сервере в цикле нужно вызывать только плохо написанные sps, вы можете отправлять ему наборы данных в параметре и выполнять процесс на основе множеств.
HLGEM
2
SQL Server будет генерировать неоптимальный план запроса всякий раз, когда в предложении WHERE есть UDF.
Джим Г.
7
Похоже, что ваша проблема с производительностью связана не с логикой в ​​базе данных, а с приложением. Она просто плохо написана и спроектирована. Эта проблема будет следовать за вами в мире ORM точно так же. ORM могут быть настоящей головной болью вне CRUD-операций. Если ваша система перегружена данными, указывайте тип системы, пожалуйста, соблюдайте осторожность.
Сам Йи
Это правда. Большинство наших проблем с производительностью просто из-за плохо написанного кода и чрезмерно сложной архитектуры. Но я все еще считаю, что мы добавили неправильный тип работы в наши базы данных. Как можно больше кода в базу данных заставляет нас делать то, что база данных не очень хороша.
Брэндон
1
Этот пример даже является аргументом для размещения основных частей бизнес-логики в БД: чтобы избежать итеративного подхода (циклы кода или курсора вместо выражений на основе множеств), таких как чума. Программисты склонны обрабатывать наборы объектов итеративным образом (цикл, обход), что, вероятно, приводит к ненужным нагрузкам или к проблеме SELECT N + 1 для многих циклических переходов одного запроса. Используя выражения на основе SQL или языка (например, LINQ), они будут вынуждены вместо этого использовать подход, основанный на множестве, когда это возможно.
Эрик Харт
10

Я работал в 2 разных компаниях, которые имели разные взгляды на эту тему.

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

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

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

Жан-Франсуа Кот
источник
3
Хранимые процедуры тестируются на единицу. Смотрите здесь для некоторых методов.
Роберт Харви
4
afaik, модульный тест никогда не использует базу данных или файл. Технически, «модульное тестирование» хранимой процедуры не является модульным тестированием, и оно будет чертовски медленным. Набор модульных тестов должен быть запущен в считанные секунды (или, может быть, минуты с очень большими приложениями) в любое время во время разработки.
Жан-Франсуа Кот
1
ОП говорил о «бизнес-логике», и бизнес-логика должна быть проверена модулем. Помещая его в хранимую процедуру, вы смешиваете его с запросом к базе данных, что замедляет весь процесс. Как я уже сказал, вы можете использовать хранимую процедуру (это не преступление), но это стирает грань между бизнес-логикой и уровнем базы данных, что плохо. Используйте это с осторожностью :)
Жан-Франсуа Кот
1
Если вы создаете БД и необходимые объекты, sp, test, а затем разбираете его, это модульный тест. Он проверяет единицу работы.
Тони Хопкинсон
2
Разве выигрыш в производительности с хранимыми процедурами не развенчан?
JeffO
9

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

Как вы заметили, T-SQL (или его эквивалент в других популярных РСУБД) - не самое лучшее место для кодирования сложной бизнес-логики.

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

Дэн Пичельман
источник
2
Использование базы данных в качестве хранилища «ключ / значение» с «завышенной ценой» является совершенно допустимым методом, что подтверждают легионы практиков NoSQL.
Роберт Харви
1
@RobertHarvey Вы, очевидно, правы, но почему-то моя интуиция продолжает настаивать на том, что должно быть более простое / дешевое / быстрое решение, чем база данных, если все, что вам нужно, это хранилище ключей / значений. Мне нужно больше узнать о NoSQL.
Дан Пичельман,
2
Я не вижу использования хранимых процедур в качестве лекарства для плохо спроектированной базы данных.
JeffO
2
@RobertHarvey, я буквально перечитал «хранилище ключей / значений по завышенной цене». Платить лицензию Oracle или SQL Server за что-то подобное, когда есть такие опции, как MongoDB, доступные бесплатно, кажется напрасной тратой денег.
Рафаэль
@Raphael Или вы можете использовать PostgreSQL De
Demi
9

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

http://en.wikipedia.org/wiki/Set_operations_(SQL)

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

Хотя это не жесткие и быстрые правила, это хорошая отправная точка.

Джон Рейнор
источник
6

Там нет ни одного правильного ответа на это. Это зависит от того, для чего вы используете базу данных. В корпоративном приложении вам нужна логика в базе данных через внешние ключи, ограничения, триггеры и т. Д., Потому что это единственное место, где все возможные приложения совместно используют код. Кроме того, размещение требуемой логики в коде обычно означает, что база данных непоследовательна, а данные низкого качества. Это может показаться тривиальным для разработчика приложений, который разбирается только в том, как работает GUI, но я уверяю вас, что люди, пытающиеся использовать данные в отчетах о соответствии, считают это очень раздражающим и дорогостоящим, когда они получают штрафы в размере миллиарда долларов за то, что данные не не следуйте правилам правильно.

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

HLGEM
источник
3

Через несколько лет вопрос все еще важен ...

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

Избегайте мусорных БД

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

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

Выражения (на основе набора) вместо процедурного кода

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

Использовать механизмы целостности данных БД

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

Хранимые процедуры?

В настоящее время хранимые процедуры редко нужны, поскольку базы данных сохраняют скомпилированные планы выполнения для SQL и повторно используют их, когда повторяется один и тот же запрос, только с разными параметрами. Таким образом, аргумент прекомпиляции для SP больше не действителен. Можно хранить или автоматически генерировать запросы SQL в приложении или ORM, которые большую часть времени находят предварительно скомпилированные планы запросов. SQL является языком выражений, если вы явно не используете процедурные элементы. Таким образом, в лучшем случае вы используете выражения кода, которые можно перевести на SQL.

Хотя сторона приложения, включая сгенерированный ORM, SQL больше не находится внутри базы данных, в отличие от хранимых процедур, я все равно считаю это кодом базы данных. Потому что он все еще требует знания SQL и базы данных (кроме самого простого CRUD) и, при правильном применении, работает значительно иначе, чем процедурный код, обычно создаваемый с помощью языков программирования, таких как C # или Java.

Эрик Харт
источник
2

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

Другой конец спектра - чистая 3-уровневая архитектура. Или, может быть, многоуровневый в веб-ориентированном бизнесе. Вы, вероятно, услышите другую историю здесь. Администратор базы данных, если таковой имеется, будет просто помощником, который выполняет некоторые административные задачи.

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

Здесь часто так много нетехнических факторов, связанных с окружающей средой, и нет общего ответа на этот вопрос.

Мартин Маат
источник
2

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

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

В этом примере бизнес-логика может проверить баланс перед запросом переноса или просто вызвать сохраненный процесс для переноса и сообщить об ошибке. ИМХО, бизнес-логика в этом примере должна предварительно проверять наличие достаточных средств и наличие целевого счета и только после этого вызывать перевод средств. Если между начальными шагами и сохраненным вызовом процедуры произойдет другой дебет, только тогда будет возвращена ошибка.

CyberFonic
источник
Хороший пример и объяснение.