Прошло больше года с тех пор, как я опубликовал свой первый широко обсуждаемый пост на Reddit:

Новое изобретение ошибочной концепции ORM.

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

  • Я фактически реализовал предложенную альтернативу на PHP и выпустил ее под лицензией MIT. Результирующий фреймворк все чаще используется в производственных проектах мной и третьими сторонами.
  • Моя первоначальная концепция восходит к 2011 году, и она претерпела множество итераций и уточнений.
  • Верю в прогресс и выбор. Agile Data намного лучше выражает взаимодействие с базой данных на высоком уровне, в то время как реализации ORM лучше… быть знакомыми.
  • Я как автор не могу не быть предвзятым :).

В этой статье я хотел бы посмотреть на:

  1. Почему ORM важен?
  2. Почему не работает ORM?
  3. Чем отличаются подходы Agile Data?
  4. Как гибкие данные подходят для производственного / корпоративного использования?
  5. Примечания к архитектурным решениям в Agile Data (ООП против развязки)

Какие важные функции ORM привносит в ваш проект?

В отличие от разработчиков Java, которые искренне принимают такие шаблоны, как ORM или Event Sourcing, язык PHP используется другой аудиторией разработчиков. Для многих это «выполнение работы». Быстрые и простые решения предпочтительнее больших и корпоративных архитектурных проектов.

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

  1. В проекте, в котором используется ORM, лучше разделение задач: база данных; бизнес-логика; презентация. В командах разные разработчики могут сосредоточиться на загрузке / хранении / кэшировании / оптимизации данных; бизнес-логика / модульные / интеграционные тесты; и презентация (интерфейсы UI и API).
  2. ORM помогает рефакторингу. Иногда устаревшее программное обеспечение могло иметь ужасный дизайн базы данных. Причина часто заключается в том, что поверх программного обеспечения добавляются «новые функции», к которым нельзя прикасаться. После многих лет обслуживания даже переименование столбца таблицы стало бы значительным проектом рефакторинга. Разделение таблицы SQL на две отдельные таблицы или перемещение ее в NoSQL может оказаться невозможным в рамках разумного бюджета. С ORM все это можно легко сделать без большого риска для проекта.
  3. Существование ORM вносит в проект «порядок», который позволяет надстройкам и расширениям добавлять дополнительные функции, такие как функции аудита или мягкого удаления, в режиме plug-and-play.

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

  • быть независимым от базы данных;
  • отдельная логика персистентности;
  • быть систематизированным и расширяемым;
  • разрешить рефакторинг базы данных;
  • упростить использование и помочь предотвратить ошибки.

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

Почему не работает ORM?

Я считаю, что есть 4 основных области, в которых все реализации ORM имеют серьезные проблемы:

  1. ORM отстой при агрегировании или построении данных отчета. Многие проекты, которые должны следовать очень строгой зависимости от ORM, по-прежнему будут использовать хранимые процедуры или необработанные запросы для извлечения агрегированных данных отчета. DQL тоже - лишь частичное решение этой проблемы.
  2. ORM выравнивает основу для баз данных. Если ваша база данных имеет уникальную функцию, такую ​​как возможность выполнять полнотекстовый или географический поиск, объединять таблицы или использовать встроенный язык выражений, ORM не предлагает способов использования этих функций. Есть обходные пути, такие как построители «запросов», которые интегрируются в ORM, но они игнорируют изоляцию логики сохраняемости. Они также не являются частью ORM и не могут быть возвращены в ORM.
  3. Производительность ORM довольно низкая, и не из-за технической реализации, а из-за логического дизайна. Такие проблемы, как (n + 1) и огромные массивы с идентификаторами, ждут, чтобы взорваться под капотом вашего приложения и требуют от вас повозиться, чтобы найти и исправить их. Точно так же ORM НЕ помогает уменьшить количество запросов и объем данных, отправляемых или получаемых из базы данных.
  4. Интеграция ORM на верхнем уровне - это очень плохо. Я не видел фрагмента PHP-кода, который бы хорошо работал с произвольной моделью / объектом ORM. Например, при реализации формы регистрации в веб-приложениях может потребоваться подробное описание всех полей и даже усердная работа по предоставлению данных для этих раскрывающихся списков / обратных вызовов автозаполнения.

Проблемы, которые я перечислил, нельзя просто «исправить». Автор шаблона может выбрать, какие проблемы будут отвлечены от разработчика, а на что разработчик должен обратить пристальное внимание.

По замыслу ORM пытается абстрагировать операции с «базой данных» с помощью такого кода:

function getBasketTotal(User $user) {
    $basket = $user->getBasket();
    $total = 0;
    foreach ($basket->getItems() as $item) {
        $total += $item->cost;
    }
    return $total;
};

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

Для сравнения, Agile Data по-разному определяет паттерн, позволяя использовать такой код:

function getBasketTotal(User $user) {
    return $user
        ->ref('basket')
        ->ref('Items')
        ->action('sum', 'cost')
        ->getOne();
}

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

Особое упоминание конструкторам запросов:

$user->getBasket()->sum('cost');

Хотя его код выглядит похожим, он определен с помощью «Конструктора запросов», который, например, подразумевает, что «стоимость» является физическим полем внутри таблицы. Agile Data не требует, чтобы разработчик знал особенности поля «стоимость», которое можно определить с помощью «соединения» или «выражения».

Как Agile Data использует разные шаблоны, чтобы избежать проблем с ORM?

Вся система Agile Data работает вместе, чтобы предложить отличные возможности абстракции базы данных, но, когда меня спрашивают об основных отличиях, я предлагаю этот список:

  1. Agile Data представляет объект Rich Field. Это позволяет полям модели быть интеллектуальными и выполнять сопоставление с выражениями, преобразование типов, сопоставление соединений, выборочную загрузку полей и поддержку значений грязных полей. Вдобавок к этому каждое поле содержит метаинформацию, позволяющую разработчикам создавать общие процедуры, которые работают с любой моделью, независимо от сохраняемости или определенных полей.
  2. Класс модели в Agile Data сильно отличается. Стандартный объект сущности ORM отображается в единую запись. Объект модели отображается в набор записей. Это изначально позволяет в дальнейшем использовать ссылки «многие ко многим», обновления нескольких записей, агрегацию и выражения через ссылки.
  3. Действия - хотя объекты похожи на объекты «Конструктора запросов», их можно повторно передать обратно в ORM. Это позволяет, например, автоматически создавать подзапросы для записей INSERT для поиска идентификатора по непервичному ключу.
  4. Простой и понятный дизайн Agile Data не зависит от совместимости или устаревших решений и может быть легко освоен тем, кто плохо знаком с программированием. Что еще более важно, многие студенты, изучающие Agile Data, делают это, даже не зная языка SQL.

В качестве примера предположим, что мы хотим определить связь между «Контакт» и таблицей «Страна». Синтаксис очень прост:

$contact->hasOne('country_id', new Country())
     ->withTitle()->addField('country_code', 'code');

Под капотом происходит многое:

  • Контакт сейчас имеет поля country и country_code, которые будут автоматически извлечены из таблицы Country без дополнительных запросов или кеширования.
  • Картирование полей происходит
  • Поле country_code можно указать при создании нового контакта вместо country_id. Это не приведет к дополнительным запросам.
  • Дополнительные логические правила (например, мягкое удаление) будут применяться к стране прозрачно.

В конце концов, разработчику не нужно добавлять какие-либо «хаки», чтобы это работало:

$contact->insert(['name'=>'John', 'country_code'=>'UK']);

Эта прозрачная реализация возможна благодаря классу Smart Field и сопоставлению DataSet в модели.

Использование гибких данных в тяжелых производственных приложениях

Agile Data 1.0 был выпущен в 2016 году. Впоследствии в версии 1.1 была добавлена ​​строгая поддержка приведения типов, и с тех пор в Agile Data не было внесено никаких серьезных изменений. Все новые дополнения и улучшения обратно совместимы и в большинстве случаев не требуют рефакторинга.

Вместо этого я сосредоточился на кодировании надстроек:

  1. Аудит - https://github.com/atk4/audit. В отличие от аналогичных надстроек для ORM, моя реализация легко интегрируется со всем проектом и включает поддержку Event Sourcing, функции UNDO и REDO, которые могут применяться к любой операции с базой данных, даже если она вызвала вложенные операции. . Аудит полностью прозрачен, работает с любой настойчивостью и может быть изменен или расширен. Доступно по лицензии MIT.
  2. Отчет - https://github.com/atk4/report не имеет аналогичной реализации для ORM. Это ваш механизм агрегирования модели предметной области. Выполните группировку, определите функции агрегирования, используйте UNION для объединения нескольких DataSet в новый DataSet. В результате данные вашего отчета могут быть сгенерированы без использования языка SQL или полу-SQL, а просто путем выражения действий агрегирования. Также важно, что модуль отчет выполняет логику расчета на сервере, а не в PHP. Доступно по лицензии MIT.
  3. Диаграмма - https://github.com/atk4/chart. Это показывает, как стороннюю библиотеку диаграмм JavaScript можно интегрировать с произвольными данными. Объединение этого с надстройкой отчетов дает вам очень удобное решение для ваших панелей мониторинга. Доступно по лицензии MIT.
  4. Для интерактивности - Agile UI поставляется с множеством виджетов, таких как CRUD, Table или Form, которые могут работать с произвольной моделью и полностью не зависят от базы данных. Также доступно по лицензии MIT.

В работе находятся и другие надстройки, например, Я писал в своей предыдущей статье о« шифровании поля пароля ».

А как насчет развязки?

Фреймворки следуют разным методам, и на то есть причина. Если вы разработчик на Symfony, то вы проектируете и создаете в основном все через интерфейсы. Agile Toolkit опирается на более фундаментальные шаблоны объектно-ориентированного программирования.

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

Я часто слышу, как разработчики высказывают опасения, что такая конструкция сделает их программное обеспечение «зависимым от Agile Data». Некоторые другие разработчики заявили, что хотят использовать «постоянство», но не хотят, чтобы их сущности наследовали класс Model.

Хотя я мог бы вдаваться в подробности, плюсы и минусы, все сводится к вопросу выбора. Есть много блестящих разработчиков PHP, которые продолжают добавлять новые развязанные расширения для Doctrine, но внутренняя развязка Agile Data просто не приносит пользы. В конце концов, я не хотел жертвовать опытом и комфортом существующих разработчиков, чтобы выиграть спор с теми, кто в любом случае не собирается использовать Agile Data.

Библиотека Agile Data имеет всего 2 зависимости и легко подключается к любому фреймворку / проекту.

Я всегда открыт для предложений и Agile Data. Мой выбор лицензии MIT гарантирует, что фреймворк останется доступным и будет расти. Команда, которую я собрал и обучил, предлагает коммерческие консультации по Agile Data.

Я надеюсь, что моя работа будет полезна разработчикам PHP и продолжит расти и развиваться в еще лучший продукт.