UPD Продолжение

sql

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

Прежде всего, мы используем библиотеку HugSQL. Работает она любопытным образом. Вы пишете обычный .sql файл с запросами. Перед каждым запросом размещаете структурированный комментарий. В нем указываете имя функции и дополнительные директивы. Затем библиотека парсит файл и динамически создает в модуле функции с аналогичными названиями.

Пример:

-- :name create-user! :<!
-- :doc creates a new user record
INSERT INTO users
(first_name, last_name, email, username, password, phone)
VALUES (:first_name, :last_name, :email, :username, :password, :phone)
RETURNING *

(let [user (db/create-user! {:username "ivan"
                             :email "test@test.com"
                             :password "123"
                             :phone "223-322"
                             :first_name "test"
                             :last_name "test"})])

Разберу минусы данного подхода. Во-первых, приходится писать больше кода. В Джанге или Рельсах достаточно вызвать у объекта метод .save(). В зависимости от состояния объекта будет выполнен запрос вставки или обновления. В нашем случае нужно писать два отдельных запроса.

Во-вторых, приходется потратить время на повторное изучение SQL и конкретной базы данных, в нашем случе – Постгреса. Потому что все ORM-системы превращают базу в хранилище, то есть черный ящик. Есть модели, пользуйся, а внутрь не лезь. И вообще, объекты предлагают больше синтаксического сахара.

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

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

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

Мы активно используем расширения Постгреса и специальные типы данных. JSON, геолокация, генерация токенов и случайных величин. Не приходится писать это в коде. Например, если из двух разных мест выполнить вставку в эту таблицу, будет сгенерирован случайый токен на уровне БД. А если делать это в коде, придется копипастить.

В логах Постргреса я вижу написанные в своем стиле запросы. ORM, как правило, генерит адский поток, который невозможно прочесть. Если ORM-запрос содержит что-то посложней чтения или вставки, например, group by, having, join, union, приходится его дебажить. Потому что далеко не факт, что результат будет таким, как вы хотите. Например, Алхимия в Питоне не может правильно сделать union, хоть убей. А как указать, что тебе нужен именно левый джоин, а не внутренний?

Не приходится перенастраивать мозги с одной ORM на другую. Помню, одновременно работал в двух проектах, в одном Джанга, в другом Алхимия. Так замучился запоминать особенности каждой. А чистый скуль не врет.

Выражаясь терминами Рича Хики, ORM это легко (easy), а SQL – просто (simple). Это не синонимы. Легко значит – быстро, а просто – прозрачно. Здесь я не буду умничать, а просто советую послушать доклад.