Проблемы проектирования ActiveRecord для большого приложения

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

Например, php-код:

class User extends Model {}

Я привожу красноречивый пример Laravel, но это верно для большинства php-фреймворков.

Затем вы добавляете отношения в класс:

public function pictures()
{
    return $this->hasMany('App\Picture');
}

Затем вы добавляете такие методы:

public function deleteComments()
{
    // delete comments code here
}

Мой первый вопрос: является ли это хорошей архитектурой дизайна, потому что через годы, когда проект станет большим, у вас будет много взаимосвязей (изображения, комментарии, сообщения, подписки и т. д., Связанные с пользователем). Класс может превратиться в 10 тысяч строк кода или около того.

В этом случае класс станет очень большим и трудным в обслуживании. Кроме того, принцип единой ответственности может быть нарушен из-за того, что у вас слишком много методов в одном классе.

Также, если я хочу использовать этот класс в другом приложении, я не могу просто потому, что мне придется вытаскивать изображения, комментарии и т. д. Во втором приложении (веб-сайте).

Если я сделаю другие классы, такие как «UserPictures», «UserPictureDeleter», код станет более сложным.

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
6
0
689
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Laravel и некоторые другие фреймворки предоставляют концепцию Активная запись в своих базовых классах модели. Основная идея Active Record - это представление строки таблицы как объекта, включающего данные строки и методы работы с базами данных.

Использование шаблона Active Record полностью оправдано в небольших простых приложениях, поскольку этот шаблон дает возможность быстро разработать ваше приложение. Но если в вашем приложении много кода и сложная бизнес-логика, Active Record создаст много проблем в архитектуре вашего приложения. Active Record может вызвать следующие проблемы:

  • нарушение принципа единой ответственности, приводящее к раздутию кода. Active Record всегда нарушает этот принцип, потому что всегда несет две обязанности: реализует бизнес-логику и методы работы с базой данных.
  • нарушение принципа низкой связи (GRASP), затрудняющее повторное использование кода
  • создание квалифицированной абстракции затруднено при использовании паттерна Active Record

Решением этих проблем является использование абстракций ООП вместо абстракций строк таблицы. Например, вы можете использовать Модель домена и Домен-ориентированный дизайн. Этот подход лучше, чем шаблон Active Record для больших приложений.

К сожалению, эти концепции слишком объемны для объяснения в этом посте, но вы можете прочитать «Domain Driven Design» Эрика Эванса. Это хорошая книга о дизайне приложений. Кроме того, вы можете найти множество статей об этих концепциях в Google. Например Построение модели предметной области, Реализация доменно-ориентированного дизайна в PHP (Laravel)

Спасибо, хороший ответ, но с моделью предметной области не означает ли это, что у меня будет больше повторного использования кода? Вместо «присоединений» активных записей, например, я буду использовать пользовательские записи во всех моделях предметной области (пользователь, изображения и т. д.). Можете ли вы предоставить очень простой способ с кодом (php, java, что угодно), просто чтобы понять. Спасибо

user2693928 19.11.2018 15:38

Я добавил в пост несколько статей с примерами

Maksym Fedorov 20.11.2018 08:11

Я думаю, что обсуждение активных записей и доктрин - это всего лишь вопрос личного выбора. Утверждать, что Active Record предназначен для небольших / простых приложений, просто неправильно! Вы будете удивлены «масштабом» компаний, использующих Active Record. Если вы не Facebook или Google, это даже не имеет значения. И если да, то вы, вероятно, собираетесь разработать свой собственный фреймворк.

Paras 25.11.2018 20:35

Любой выбор узоров или выбор любых технологий - это всегда личный выбор. Но это не лишает проблем паттерна Active Record. Размер компании не показывает ситуации, в которых применяется шаблон

Maksym Fedorov 26.11.2018 07:48

Итак, вы признаете, что ваш комментарий об ActiveRecord, предназначенном для небольших приложений, неверен? Паттерн Доктрина также имеет недостатки / проблемы. Это вопрос выбора, но у обоих есть свои плюсы и минусы. То, что Doctrine делает тысячами строк кода, ActiveRecord достигает сотнями.

Paras 27.11.2018 09:05

Есть причина, по которой популярные фреймворки, такие как Laravel и Rails, приняли ActiveRecord, а не Doctrine.

Paras 27.11.2018 09:12

Где я признал, что мое мнение об ActiveRecord неверно? Вы можете попробовать разработать большое приложение со сложной бизнес-логикой с помощью Active Record, но у вас будет много проблем с поддержкой этого приложения. Где я рассказывал о Доктрине? Я говорил об использовании DDD и модели домена, Doctrine - это не шаблон, Doctine - одна из многих ORM.

Maksym Fedorov 27.11.2018 09:49

Модель - это не одна строка из базы данных, модель не всегда содержит только отношения. Модель должна содержать бизнес-логику. Если вы попытаетесь создать Active Record с множеством взаимосвязей и богатой бизнес-логикой, вы удивитесь, сколько проблем с поддержкой в ​​течение длительного времени у вас будет. Active Record используется для быстрой разработки, но не для создания качественного кода. В этом преимущество Active Record. Laravel предоставляет не только Active Record в своем Eloquent ORM, но вы также можете использовать любой ORM в Laravel.

Maksym Fedorov 27.11.2018 09:49

Doctrine - это ORM, реализующий шаблон DDD. Модель должна содержать только логику базы данных, а не бизнес-логику. Проблемы, о которых вы говорите, возникают из-за того, что вы добавили в модели бизнес-логику, что является плохой практикой. Вы можете использовать любой ORM в любом фреймворке или разработать свой собственный. Laravel и Rails из коробки поддерживают ActiveRecord, а не Doctrine по какой-то причине.

Paras 27.11.2018 10:35

Хорошо, в этом случае у нас есть: 1. не-oop объекты, которые не работают со своими состояниями 2. нарушение принципа TDA 3. нарушение принципа информационного эксперта (GRASP) 4. все еще нарушение принципа единой ответственности - Active Record - это объект и репозиторий одновременно 5. все еще нарушается принцип низкой связи (GRASP), вся бизнес-логика зависит от структуры базы данных 6. все еще грязные абстракции, которые показывают структуру базы данных

Maksym Fedorov 27.11.2018 11:02

@Paras Я думаю, вам нужно больше исследовать архитектуры приложений в целом, в частности Active Record. Я бы порекомендовал блог Мартина Фаулера или его знаменитую книгу Patterns of Enterprise Application Architecture. Я бы использовал Active Record, скажем, в простом приложении со списком дел, но как приложение Todoist.

Humoyun Ahmad 19.11.2020 02:22

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

Межфазовое программирование

кодирование для интерфейса в PHP

The class could become 10k lines of code or so

Каждая функция отношений представляет собой 2-4 строки кода, поэтому, если у вас нет 2500-5000 отношений, этот класс не будет содержать 10 тысяч строк кода. Если да, значит, у вас уже плохой дизайн базы данных.

Also the Single Responsible Principle maybe is violated because you have too many methods in one class.

В SRP нигде не говорится, что нельзя иметь слишком много методов в одном классе. В нем говорится, что у класса должна быть только одна ответственность / причина для существования.

Also if I want to use the class it in another app I cannot, simply because I'll have to pull also the pictures, comments and etc in the second application(website).

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

Спасибо, я указываю также на другие методы, принадлежащие модели, а не только отношения. Например: deleteUser (), getFullname (), activateUser () и так далее. Класс становится большим, и его трудно поддерживать. Если бы у меня как-то были только базовые функции для User без отношений и этих методов, я мог бы вытащить этот простой класс и использовать его в другом приложении, но это может быть сложно, потому что вам нужно продумать сложную структуру, и даже тогда вы не сможете понять все возможности .

user2693928 26.11.2018 08:28

Я не думаю, что deleteUser, activateUser должны даже существовать в классе, отличном от User. Если у вас есть сообщение, связанное с пользователем, и вы хотите выполнить действие над пользователем сообщения, вы бы предпочли сделать $post->user->activate(), а не $post->activateUser(). Кроме того, для каждой модели будут только ограниченные действия, а не тысячи.

Paras 27.11.2018 09:15

Да, это всего лишь пример, количество действий ограничено, но в большом проекте их могут быть сотни. Некоторые из них трудно сказать, относятся они к классу User или нет. Например: $ user-> disablePayments (), $ user-> blockAccount () и представьте, есть ли у вас сотни подобных методов. Некоторые могут сказать, что они не принадлежат к классу пользователей, но я бы сказал, почему бы и нет? $ user-> blockAccount () мне кажется правильным.

user2693928 27.11.2018 09:27
blockAccount вроде нормально, но сколько таких методов у вас на модели? Не думайте, что они будут исчисляться тысячами для таблицы с горсткой столбцов.
Paras 27.11.2018 09:29

Это да, но есть методы, по которым сложно сказать, должны ли они быть в пользовательской модели или отдельном классе. Например, $ user-> sendActivationEmail (). Я предполагаю, что они могут быть извлечены в разные классы, но в любом случае это усложняется. У вас есть один класс с 500 методами или у вас есть 100 классов с 5 методами.

user2693928 27.11.2018 10:08

Я не знаю, как у вас получается 500 методов в одном классе модели. Кажется непостижимым. Даже если у вас большая таблица с 50 столбцами, 500 методов кажутся чрезмерными. Обычно даже в больших приложениях будет много таблиц (то есть много классов), но это не значит, что у них есть много методов в одном классе. Возможно, некоторые из этих методов применимы к нескольким моделям и должны быть извлечены из признака. Трудно сказать, что происходит, не глядя на класс.

Paras 27.11.2018 10:13

500t были просто примерным счетом, даже если они 20,30, единственная ответственность для меня нарушена. В Laravel, например: у вас есть методы save, get, delete, у вас есть toJson () сериализация, вы можете вызывать события, в основном классы моделей делают все. Не знаю, для меня это плохая практика?

user2693928 27.11.2018 10:25

Каким образом получение, сохранение, удаление, моделирование событий и т. д. Нарушает единую ответственность? Модель отвечает за взаимодействие с базой данных. Все это связано с запросами / событиями базы данных. Это не нарушение.

Paras 27.11.2018 10:29

toJson не связан с базой данных, события - нет, sendActivationMail - нет.

user2693928 27.11.2018 11:36

toJson - это сериализация класса. Может быть для любого класса. События не связаны, но события базы данных / красноречивых действительно связаны с базой данных. SendActivationEmail должен быть частью трейта, а не моделью. Нет нарушения SRP

Paras 27.11.2018 11:51

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

user2693928 27.11.2018 12:08

Рассмотрим следующий говорить от Адама Уотана. Он решает проблему именования, которая приводит к раздуванию классов. Если вы рассмотрите его методологию именования методов и перекладывания ответственности, вы получите классы меньшего размера и более легкий для чтения код.

Кроме того, не позволяйте себе навязывать некоторые правила и «законы», существующие в программировании. Если вам нужен класс, состоящий из 10 тыс. Строк, но он читабельный, тогда действуйте.

Другие вопросы по теме