В реляционных базах данных принято создавать ограничения для блокировки данных и их связи с другими таблицами. Так, таблица Users
может иметь столбец email
, который помечен как уникальный, потому что у каждого есть свой собственный email, верно? А таблица Subscription
получает ненулевую колонку User
, потому что каждая подписка должна быть связана с пользователем. Да, это тоже звучит разумно.
Но я здесь, чтобы поспорить против ограничений! Для кого-то это может показаться еретическим (или, по крайней мере, очень глупым), но выслушайте меня:
Мы используем ограничения по двум причинам: Для производительности (гораздо быстрее искать в столбце с уникальной индексацией), и для гарантии целостности данных (чтобы мы физически не могли ввести недействительные данные, например, бизнес требует, чтобы подписка была привязана к пользователю).
Вот в чем проблема: мы, программисты, часто оптимизируем слишком рано, и SQL-ограничения на самом деле не выражают бизнес-требования!
Позвольте мне немного разложить это по полочкам:
-
Что касается производительности, то, конечно, существуют обоснованные случаи, когда ограничения ускоряют запросы в несколько раз, но пока мы не увидим эту проблему производительности и ее последующее решение с помощью ограничений, мы рискуем преждевременно провести оптимизацию. Меньшее количество ограничений сделает код более гибким и создаст меньше трудностей при последующих изменениях структуры базы данных.
Все, что я предлагаю здесь, это искать возможности отложить оптимизацию базы данных до тех пор, пока она не станет действительно необходимой, чтобы противостоять нашей склонности к преждевременной оптимизации.
-
Ограничения, обеспечивающие целостность данных, действительно сталкиваются с проблемой неспособности реально отразить все требования бизнеса. Я имею в виду, что теоретически ограничения базы данных могут полностью выразить любое бизнес-требование, потому что, например, диалект PostgreSQL является полным по Тьюрингу, но это было бы безумием! Мы не программируем на SQL, мы программируем на C#, Python, Javascript и т.д. Реальная бизнес-функциональность должна быть реализована в наших языках общего назначения, а не в языке хранения данных. Так какие же ограничения мы должны вводить в базу данных?
Я предлагаю вообще не вводить никаких бизнес-ограничений. Это звучит как безумный лай? Чем больше я работаю с SQL, тем больше меня умиляют полунадуманные «бизнес-ограничения», которые практически дублируют то, с чем уже справляется код. Что нам дает пометка столбца
Subscription
User
как non-nullable, когда код уже гарантирует, чтоSubscription
создается только по отношению кUser
? Это вообще не поможет, потому что ограничения просто не являются фактическим источником истины.«Но подождите», — слышу я протест некоторых, — «это же так просто — добавить non-nullable в колонку
User
«. Да, но это все равно вредит, потому что в основе своей не является источником истины для бизнес-требования.
На этом этапе нам нужно обсудить недостатки ограничений: Чем их больше, тем сложнее тестировать и настраивать систему.
-
Сложнее тестировать, потому что для тестирования
Subscription
нам теперь нужно также создатьUser
и правильно их связать. Даже если тест не имеет никакого отношения к сущностиUser
, ее необходимо создать. А потом представьте себе еще несколько сущностей, которые нужно правильно собрать вместе, чтобы создатьSubscription
, и работа с ними быстро становится очень мучительной! Затем мы можем начать прятать всю эту тестовую сложность в фабриках сущностей, но это просто абстракции поверх сложности, которую мы сами себе создали. Почему бы не избавиться от сложности полностью? -
И еще сложнее адаптироваться, потому что введение новых возможностей в конечном итоге требует тщательной миграции, чтобы ограничения были синхронизированы с кодом: Что если мы действительно можем иметь подписки без пользователя? Что если столбец электронной почты не обязательно должен быть уникальным? Если слой хранения данных накладывает искусственные ограничения, это замедляет быстрое экспериментирование с такими идеями.
Я думаю, что многие из нас создают ограничения, потому что нас учили и/или говорили так делать. Но никто из нас в реальности не реализует 100% бизнес-правил на SQL, так где же провести черту? Ограничения, конечно, хороши, когда они дают реальные, ощутимые преимущества в производительности, но что произойдет, если мы ослабим систему, позволив этим сущностям быть более податливыми, с ними легче работать и тестировать?
В конце концов, только вы знаете свою область, поэтому нет общих ответов на мои утверждения. Однако я пытаюсь понять, что в проектирование реляционных хранилищ прокралась определенная догма, когда считается, что мы должны создавать ограничения, которые звучат совместимо с бизнес-логикой, потому что так принято. Вместо этого я предлагаю добавлять ограничение ТОЛЬКО там и тогда, где и когда это действительно и доказательно необходимо. Таким образом, базам данных будет позволено сосредоточиться только на хранении и извлечении данных, в чем они действительно преуспевают.
Фото Sam Moqadam on Unsplash