Pull to refresh
Хекслет
Школа программирования

Забейте на ORM

Reading time 5 min
Views 17K
Original author: Piotr Solnica
Привет, Хабр!

Мы в Хекслете учим людей программировать, но стараемся хитрить: например, под видом простого, на первый взгляд, курса по PHP, рассказываем людям про абстракции, рекурсии, функции первого класса, замыкании, свертку и вообще начинаем «Основы программирования» с МИТ'шного СИКПа, а не с классов и формочек. В этом и других курсах, а также в наших регулярных вебинарах рассказываем о функциональном программировании, о проблемах современных подходов и о главном зле: состоянии. В нашем чате постоянно поднимаются крупные дискуссии, в которых выясняется, что изменяемое состояние в разы повышает сложность в системе.

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

* * *

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

Но что это вообще означает в Ruby? Запретить изменять любые объекты? Будет слишком медленно, так что — нет. Иммутабельно-ориентированный дизайн означает, что вы избегаете те интерфейсы, которые могут изменять объекты. Да, много методов в Руби изменяют состояние, но когда вы разрабатываете интерфейсы объектов, вы можете создавать их таким способом, что объекты не будут изменяться.

Использование неизменяемых объектов стало для меня шокирующим открытием. Одна из вещей, которую я понял после того открытия, это почему object-relational mapping (ORM) — плохая идея, и из-за нее у нас так много ненужной сложности.

Я был пользователем различных ORM около 10 лет, что включает в себя ~2 года в рабочей группе проекта Data Mapper и ещё ~2 года в попытках построить новую версию, которая должна была преодолеть все препятствия модели Active Record. Я увидел их снаружи, я увидел их изнутри, и больше не хочу иметь дело с ORM. С меня хватит.

Я забил на ORM.

Сложность


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

Object-relational mapping — не что иное, как слой дополнительной сложности, которая существует только потому что мы хотим, изменять объекты и хранить их персистентно в БД.

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

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

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

Потеря соответствия


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

У вас есть два варианта:

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


И оба варианта ужасны.

Первый особенно страшный, и многие люди в сообществе Ruby знают почему. Потому что маппинг 1:1 плотно связывает уровень приложений с базой данных. Мы используем Active Record ORM'ы достаточно долго, чтобы понимать, насколько это усложняет наши приложения.

Что насчёт второго варианта, известного как шаблон Data Mapper? В Ruby такого нет, но еще есть люди, которые пытаются построить его. Плюс уже существуют несколько проектов, которые пытаются реализовать этот шаблон.

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

Хуже всего то, что мы так и не сможем решить её. Можно лишь улучшать все по возможности, учитывая ограничения конкретного языка программирования. И в конце концов люди точно перейдут на написание SQL-запросов руками.

Базы данных не существует?


На самом деле, она существует. И это одна из самых мощных частей вашего стека. Теперь задумайтесь на секунду вот о чём:

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



Понимаю, всё не так, как вы привыкли. Обычно мы говорим о замечательных штуках вроде «грамотной абстракции, где наша база данных становится деталью реализации» или «будет проще менять базы данных» или «объекты отделены от базы данных, так систему построить проще» и так далее.

Но к чему на самом деле всё сводится? «Я хочу изменять объекты и хранить их персистентно».

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

Два волшебных слова: функции и типы данных.

Пользователя не существует


User'а не существует, Order'а не существует

Не существует ActiveProductShippingContainerHolyMolyManager'а.

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

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

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

А что с данными? Данные означают сложность, поэтому мы скрываем это за объектами, верно? Вот только это никогда не работает так, как в рекламе книжке.

Винегрет из данных с беспорядочным и непредсказуемым поведением — вот что такое ORM.

Но как так? Почему?

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

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

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

Не существует User'а, но, скорее всего, есть SignupUser. Нет Order, но вы определённо столкнётесь с PlaceOrder. А когда увидите классы, заканчивающиеся на Manager — просто убегайте.

Какой у нас есть выбор?


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

«Мне нужен ORM, потому что я использую объектно-ориентированный язык»

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

Это избавит от кучи ненужных абстракций, которые появляются в типичных ORM.

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

Объектно-ориентированные языки, которые поддерживают такое, проживут дольше.

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

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

Откажитесь от ORM. Примите неизменяемо-ориентированный дизайн. Он работает лучше.

Перевод: Наталия Басс
Tags:
Hubs:
-1
Comments 68
Comments Comments 68

Articles

Information

Website
ru.hexlet.io
Registered
Founded
Employees
51–100 employees
Location
Финляндия
Representative
S__vet