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

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

Для внедрения ORM в вашем проекте существуют противопоказания. Проконсультируйтесь со здравым смыслом перед применением.

Пьянящее чувство полезности

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

Мы, айтишники, просто волшебники. Мы можем это сделать. Особенно, когда горят глаза и чешутся руки. Это же так заманчиво - потратить день на разработку, чтобы потом десятки людей экономили своё время каждый день. Каждый божий день. Мы будем давно уже делать другие полезные вещи, а те самые люди всё ещё будут экономить своё время. И радоваться.

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

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

Ну почти хорошо. Вот если бы ещё это и это поправить. Вот тут автоматизировать. Вот тут ещё в экселе работают. Бизнес, почуяв запах крови оптимизации, захочет больше. Ещё больше. Ещё!

Всё. С этого момента вы официально белки в колесе. Или хомячки, кому какая аналогия больше нравится. Бизнес будет от вас требовать всё больше и больше. Вы сделаете сложное - захотят нереальное. Сделаете нереальное - захотят невозможное. У бизнеса нет кнопки "стоп", у вашего приложения нет предела возможностей. Есть вы, есть бизнес, есть приложение.

Сложность

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

Тем временем бизнес входит во вкус. Бизнес чувствует раж. Он знает, что команда может сделать сложное и хочет ещё сложнее. А у вашей команды с каждой итерацией колеса сложность возрастает. Уже нет эффекта "низкой базы" и каждое новое нововведение даётся если не с кровью, то с потом. В этом фундаментальная проблема взаимодействия бизнеса и команды разработки.

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

Вы уже сидите на игле дофамина. Вам жизненно необходимо увеличивать дозу полезности бизнеса, потому что он тоже сидит на той же игле. А тут эта херня - сложность. Которая всё портит.

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

Данные

Первое, что приходит в голову при разработке системы: как хранить данные. "База данных" ответ. Не, ну серьёзно, а что ещё? Какая? Ну просто база данных, любая. Чтоб данные хранила. Ну не в экселе же хранить, серьёзно. Берём {vendorname}sql.

Итак, вы выбрали реляционную базу данных. Всмысле, вы не выбирали именно реляционную, вы вообще не думали про слово "реляционная", вам оно не было нужно. Но выбрали.

Так, для тех, кто выбрал NoSql небольшая приписка: вы ещё пожалеете. Вы ещё переделаете свою базу под RDBMS, а потом, когда уже всё переделаете, поймёте, для чего нужна была NoSQL. И переделаете часть на монгу. Или что там модно.

У вас реляционная база данных и объектно-ориентированный язык для работы с этими данными. Чувствуете, какое назревает противоречие?

Сейчас для котят на пальцах: объектно-ориентированные данные хранят объекты внутри объектов. Реляционные ссылаются.

Я вот раньше думал и пропагандировал мысль, что ORM именно это и значит - мостик между реляционной моделью и объектно-ориентированной. Как-бы не так. То есть да. Ну почти. Дело в том, что это не решаемая для общего случая проблема. А в частных случаях фетчить данные и раскладывать по полям - ну наверное вы это умеете. Иначе, что вы тут вообще делаете?

Сделать привязку один раз руками не проблема. Проблема потом поддерживать соответствие. А ещё более суровая проблема - быть уверенным, что соответствие поддерживается.

ORM

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

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

ORM нужен, чтобы ты не работал больше с полями. Серьёзно, не надо. Хватит уже. Поля в базе - это поля в базе, а вы всей командой работаете бизнес-логикой, которая не ограничивается одним полем. Нет, конечно, уровнем объектов тоже не ограничивается. Но давайте хотя бы с полями покончим, уже меньше будет проблем, ок?

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

Хватит. Правда. Вы не сможете развивать сколько-нибудь сложную систему на основе встроенных в базу данных языков. Как бы вы ни старались, вы всё равно будете работать с данными, а не с доменными сущностями. Вы всегда будете на ступень-две ниже тех, кто работает с объектами.

Фундаментальная проблема pl/sql-type языков - они write only. Да, это приходит только с возрастанием сложности, простые конструкции не вызывают подозрений. Тут та же ситуация, что с лягушкой. Как со всеми нами. Сложность вырастает, она мешает, но внутри базы данных сложность становится проблемой быстрее.

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

Если вы пишете логику в базе данных с применением языка общего назначения, то ответьте на вопрос: а как вы это объединять будете? Вот хотя бы себе, но честно.

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

Перестаньте руками писать запросы. Это быстрее в моменте, но когда ваша система станет чуть сложней, вам эти запросы придётся править. Каждый раз править. На каждое добавленное поле придётся дорабатывать код фетча.

Конечно, вы всегда можете автоматизировать процесс. Вы можете сделать кодогенератор или синхронизатор схемы БД, распарсив класс данных в синтаксисе своего языка и замапив типы данных. Но это же и будет ORM. Простой, кривой, ещё маленький. Может быть оно так и надо. Может быть таков путь.

Пора внедрять ORM в вашу систему

Комментарии (99)


  1. usego
    30.04.2024 11:47
    +12

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


    1. Kerman Автор
      30.04.2024 11:47

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

      С другой стороны, ORM умеет генерить селекты c where и некоторые умеют в left join. У EF правда есть linq to entities, где можно совращать гусей, но тут синтаксис максимально близок к sql. Если честно, совсем не понимаю, что в этих запросах может быть сложного.


      1. usego
        30.04.2024 11:47
        +3

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


        1. Kerman Автор
          30.04.2024 11:47
          +3

          Если вам сложно не нагородить десятиэтажных конструкций, может вам не надо в айти? Айти всё-таки про огромное количество вариантов, как сделать одно и то же. Здесь нет точного рецепта, как правильнее. Тут самому приходится выбирать пути, ошибаться и пробовать снова. И ещё нести ответственность за выбранные решения.


          1. usego
            30.04.2024 11:47
            +4

            Категоричность умиляет. Обычно по ней можно делать вывод, где человек на графике нормального распределения. В айти сейчас столько людей, которым не надо было в айти, что с этим фактом надо считаться при выборе технологий. И именно поэтому ORM - крайне опасен. Всякого навидался в реальных проектах. Из некоторых выпиливали nHibernate под корень, так как перестал иметь смысл, поддержку и стал rocket science'ом для молодёжи со всеми вытекающими. А с EF городили такого, что пришлось просто запретить его в проектах сложнее домашней странички.


        1. headliner1985
          30.04.2024 11:47
          +1

          Нужно всего-то включить печать запросов в локлальном и дев окружении


          1. usego
            30.04.2024 11:47
            +3

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


            1. headliner1985
              30.04.2024 11:47

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


              1. weirded
                30.04.2024 11:47
                +2

                Explain прикладывать надо, а не SQL :)


                1. mayorovp
                  30.04.2024 11:47

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


      1. olivera507224
        30.04.2024 11:47
        +6

        совсем не понимаю, что в этих запросах может быть сложного

        Вот эта ремарка вызывает ощущение, будто автор очень мало и очень поверхностно поработал как с ORM, так и с SQL. Хотя сама статья, если честно, вызвала прямо противоположные чувства.

        Можно сколько угодно спорить о том, какой подход более "трушный" - замаппить данные самостоятельно или использовать ORM. Но вот сомневаться в сложности как одного, так и второго метода - как минимум подозрительно.

        Самое первое, что приходит мне на ум, когда я слышу о том, что писать запросы (на чистом SQL или через ORM - не важно) это просто - это попросить автора этих слов разобрать порядок выполнение какого-нибудь более-менее сложного запроса. Хотя бы в пределах одной СУБД. В каком порядке будет выполняться запрос? Какие индексы будут задействоваться? А будут ли они задействоваться? А как сделать так, чтобы они задействовались? А что произойдёт, если данных будет очень много? А можно ли переписать запрос более эффективно?

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


        1. Kerman Автор
          30.04.2024 11:47

          В каком порядке будет выполняться запрос?

          Язык SQL декларативный. В том порядке, в котором захочет СУБД. Проще её саму спросить. Индексы БД не обязана использовать, но обычно использует. Если данных много - крутите кэши под ваш сценарий. Может у вас хэви инсерт, а для селекта можно подождать - тогда кэши не спасут.

          Сомневайтесь, это полезно. Проясню ремарку: сегодняшние orm, пока в них не внедрили нейросети, очень сильно ограничены по функционалу. В сгенерированных запросах нет ничего сложного. Нет латерал джойнов, в юнионы по-моему ни одна не умеет. Даже group by с having умеют единицы. Про CTE вообще молчу. Человек намного изощрённее любой ORM. Естественная нейросеть способна нагородить куда более ужасную и изощрённую конструкцию, чем самый продвинутый фреймворк


          1. haaji
            30.04.2024 11:47
            +4

            Нет латерал джойнов, в юнионы по-моему ни одна не умеет. Даже group by с having умеют единицы. Про CTE вообще молчу

            Это ерунда полная, sqlalchemy в питоне умеет все это и даже больше, а все, что умеет hibernate джавовский даже представить сложно.


          1. usego
            30.04.2024 11:47
            +1

            Опытный разработчик строит запрос под схему данных и индексы, заранее разделяя запрос на подзапросы, что бы он был эффективен. И вот ваш второй абзац и поясняет, почему ORM не silver bullet и применим только в определённых условиях.


  1. vazir
    30.04.2024 11:47
    +14

    Сначала вы будете внедрять ОРМ, потом, по прошествии времени не знать как бодаться с тормозами вашей разработки. Зато все при деле. Мое единственно правильное мнение что орм это зло. Или вы полностью понимаете как вы храните ваши данные, или вы замуровываете себе бочку с порохом в фундамент


    1. Kerman Автор
      30.04.2024 11:47
      +3

      Простите, а когда это "потом" начнётся? Один проект у меня уже 13 лет на ORM, пока всё хорошо. Или имеется в виду, что падает скорость разработки? Тогда тем более непонятно. ORM как раз нужен, чтобы снизив сложность, её повысить. И это работает.


      1. vazir
        30.04.2024 11:47

        Имеется в виду что использующие ОРМ часто мало понимают как будут храниться данные. Если данные сложносвязанные, оптимально это сделать сложнее или вообще невозможно чем не используя ОРМ. Вообще не трогаю случаи когда куча логики вообще внутри БД в виде триггеров или хранимок. А на начальном этапе, возможно скорость разработки будет выше, для тех кто с БД не очень дружит. А если дружит то ОРМ только усложняет все.


        1. michael_v89
          30.04.2024 11:47
          +1

          Имеется в виду что использующие ОРМ часто мало понимают как будут храниться данные.

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

          Если данные сложносвязанные

          То возможно база спроектирована неправильно.

          Вообще не трогаю случаи когда куча логики вообще внутри БД в виде триггеров или хранимок.

          Ну так ORM нужна в основном для случаев, когда логика находится в приложении, чтобы было удобнее ее писать и поддерживать.


      1. vazir
        30.04.2024 11:47
        +3

        И да, я пишу много логики в БД. И нет не хватит. Почему? Потому что есть как бы такое правило, что чем ближе к данным находится логика, тем она быстрее. Хранимка получает данные условно напрямую, избегая задержек передачи по сети. Триггером можно заботится о консистетности и так далее. База - давно не тупое хранилище.


        1. Kerman Автор
          30.04.2024 11:47

          В теории всё так. But.

          In theory, theory and practice are the same. In practice, they are not.

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

          p.s. триггеры, юники, ключи и прочие констрейнты работают не только для встроенного языка


        1. posledam
          30.04.2024 11:47

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


    1. posledam
      30.04.2024 11:47
      +2

      ORM не может быть злом. Это отражение плоских данных в структуру объектов ЯП. Либо это делать руками, что по сути ни что иное, как обезьяний труд. Либо всё таки положиться на компилятор/ЯП. Проблемы начинаются в отказе от SQL для написания запросов. Простые запросы и правда нет смысла фигачить руками, а что по-сложнее уже нет смысла генерировать, так как SQL самодостаточен.


      1. evgenyk
        30.04.2024 11:47
        +1

        Ну, есть еще одна альтернатива. Не отображать данные в структуру объектов. А сохранять данные в виде, скажем многомрных массивов и их обрабатывать. Ведь вовсе не обязательно всегда и везде работать испольуя только ООП. Оно не всегда хорошо работает.


        1. evgenyk
          30.04.2024 11:47
          +1

          Почему-то минусуют. А ведь есть такие подходы к архитектуре, как Data-driven design, stream processing и data pipelining.


  1. moooV
    30.04.2024 11:47
    +20

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

    ORM makes simple things easier, but hard things impossible.

    Каждый раз когда начинали что-то с ORM или внедряли в итоге плевали и от него уходили руками писать многостраничные sql портянки с вложенными запросами и тоннами джоинов.


    1. atues
      30.04.2024 11:47
      +2

      О, да-да, натюрлих! Пока запросы типа "найти запись по id" или пока глубина джойнов не больше 2-х, то ORM худо бедно попрет. Но в проектах, где БД содержит 1000+ криво спроектированных таблиц да еще без FK или с такими FK, что хрен расплетешь - что на что ссылается (я говорю об унаследованных системах с сотнями миллиардов и больше записей), то медленно (а, может, и не медленно) приходит трындец. Вот всю бизнес-логику фигачить на pl/sql - этого не стоит делать точно. Лучше БД оставить хранение данных, а логику выносить в приложение.


      1. Spiritschaser
        30.04.2024 11:47
        +1

        криво спроектированных таблиц ... с такими FK, что хрен расплетешь

        Как вариант, грамотно спроектировать к ним 1000+ view для использования с ORM.


        1. atues
          30.04.2024 11:47

          Ну, как вариант да. Но напомнило анекдот:

          "Объявление в борделе.

          Секс - 100 баксов

          Наблюдение за сексом - 200 баксов

          Наблюдение за наблюдающим за сексом - 500 баксов"


    1. Kerman Автор
      30.04.2024 11:47

      Это одно из самых распространённых заблуждений об ORM, которые я встречал. Делать сложные запросы - это не задача ORM. ORM в первую очередь - маппер. ORM и SQL вообще ортогональны в этом вопросе.

      Тут так: если хотите читаемый и поддерживаемый код: пишете на орм. Хотите любить гусей - пишете на чистом sql. Как-то одно другому мешает? На практике оказывается, что сложные запросы нужны, мягко скажем, не в критичном количестве. А ежедневная рутина делается проще на орм.

      Некоторые ORM вообще могут работать с raw sql. Просто позволяют писать запросы, и после делают маппинг на объекты.


      1. xztv
        30.04.2024 11:47
        +1

        А как тогда все же писать сложные тяжелые запросы с несколькими уровнями вложенности, кучей джойнов и т.д.? Это получается такая же игра с синтаксисом и оптимизациями, только на уровне ORM-либы, а не SQL-запроса.

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


        1. Kerman Автор
          30.04.2024 11:47

          И кто вам запретит писать сложные тяжёлые запросы? У вас кто-то насильно отбирает SQL?


      1. usego
        30.04.2024 11:47

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


        1. headliner1985
          30.04.2024 11:47

          Почитайте про развитие архитектуры сайта hh ru, где люди перешли с точностью до наоборот от самописных sql на orm по примерно тем же причинам. И не надо только про производительность, у них миллионы запросов в секунду.


          1. usego
            30.04.2024 11:47
            +1

            Обычно хайлоад не насилует базу напрямую.


        1. michael_v89
          30.04.2024 11:47
          +2

          Ну вот такой жизненный цикл разработки многие проекты и проходят.

          Ни разу за все время работы не встречал проект, который перешел на логику в базе. А негативных отзывов о логике в базе встречал достаточно.


          1. usego
            30.04.2024 11:47

            Логика без ORM ещё не означает логику в базе.


            1. michael_v89
              30.04.2024 11:47
              +1

              Может и не означает, но вы сказали "многие проекты переносят логику на сторону базы".


    1. kozlov_de
      30.04.2024 11:47
      +1

      sql портянки с вложенными запросами и тоннами джоинов?

      Возможно, это признак плохо спроектированной БД

      А если нельзя перепроектировать БД, то можно добавить вьюшки в БД

      А, тут и без меня уже всё написали.

      И да, пока всё просто не усложняйте

      Keep it simple, stupid


      1. mayorovp
        30.04.2024 11:47

        Как будто вьюшка в БД с вложенными запросами и тоннами джоинов будет работать быстрее, а писаться - проще...


  1. drSmit5252
    30.04.2024 11:47
    +1

    примеры бы очень не помешали, насколько проще станет работать


    1. Kerman Автор
      30.04.2024 11:47
      +1

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

      Я уже однажды приводил пример:

      Orders.Where(o => o.IsCompleted && o.CreatedDate.Year > 2020).OrderBy(o.ID)

      Трансформируется в:

      SELECT * FROM db.Orders a WHERE a.IsCompleted = 1 AND YEAR(a.CreatedDate) > 2020 ORDER BY a.ID

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


    1. michael_v89
      30.04.2024 11:47
      +2

      $query = Product::find()->alias('p');
      $query->joinWith('category c');
      
      if ($filter->managerEmail) {
        $query->joinWith('manager m');
        $query->andWhere(['m.email' => $filter->managerEmail]);
      }
      
      $subQuery = ProductImage::find()->where('product_id = p.id');
      if ($filter->hasImages === true) {
        $query->andWhere(['exists', $subQuery]);
      } elseif ($filter->hasImages === false) {
        $query->andWhere(['not exists', $subQuery]);
      }
      
      return new ActiveDataProvider([
        'query' => $query,
        'sort' => ['attributes' =>
          ['p.created_at', 'p.name', 'c.name', 'm.email']
        ],
      ]);
      

      Параметр sort задает разрешенные поля для сортировки, конкретная сортировка задается в URL, ActiveDataProvider автоматически применяет сортировку к запросу. С SQL этот код будет выглядеть гораздо длиннее и запутаннее.

      В целом маппинг на сущности нужен не столько для построения SQL-запросов, сколько для задания типизации в коде, например в аргументах функций. И соответственно для использования возможностей IDE - подсказки возможных свойств, поиск мест использования класса или свойства, и т.д.