Каковы недостатки шаблона ActiveRecord?

30

Мне любопытно, каковы недостатки использования шаблона ActiveRecord для доступа к данным / бизнес-объектам. Единственное, о чем я могу подумать, - это то, что он нарушает Принцип Единой Ответственности, но модель AR достаточно распространена, и сама эта причина не кажется «достаточно хорошей», чтобы оправдать ее неиспользование (конечно, мой представление может быть искажено, поскольку часто ни один код, с которым я работаю, не следует ни одному из принципов SOLID).

Лично я не фанат ActiveRecord (за исключением написания приложения на Ruby on Rails, где AR чувствует себя «естественным»), потому что он чувствует, что класс делает слишком много, а доступ к данным не должен зависеть от самого класса обрабатывать. Я предпочитаю использовать репозитории, которые возвращают бизнес-объекты. Большая часть кода, с которым я работаю, имеет тенденцию использовать вариант ActiveRecord в форме (я не знаю, почему метод является логическим):

public class Foo
{
    // properties...

    public Foo(int fooID)
    {
        this.fooID = fooID;
    }

    public bool Load()
    {
        // DB stuff here...
        // map DataReader to properties...

        bool returnCode = false;
        if (dr.HasRows)
            returnCode = true;

        return returnCode;
    }
}

или иногда более "традиционный" способ иметь public static Foo FindFooByID(int fooID)метод для поиска и что-то вроде public void Save()для сохранения / обновления.

Я понимаю, что ActiveRecord, как правило, намного проще в реализации и использовании, но кажется, что он слишком сложен для сложных приложений, и вы могли бы иметь более надежную архитектуру, инкапсулировав логику доступа к данным в репозитории (не говоря уже о том, что ее проще заменить стратегии доступа к данным, например, возможно, вы используете Stored Procs + DataSets и хотите переключиться на LINQ или что-то еще)

Итак, каковы другие недостатки этого шаблона, которые следует учитывать при принятии решения, является ли ActiveRecord лучшим кандидатом на работу?

Уэйн Молина
источник

Ответы:

28

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

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


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

  • легко иметь подключения к базе данных доступны везде. Это обычно приводит к неприятному жесткому кодированию или некоторому статическому соединению, которое поражается отовсюду.
  • Ваши объекты имеют тенденцию выглядеть больше как SQL, чем объекты.
  • Трудно сделать что-либо в приложении отключено, потому что база данных так укоренилась.

В конце концов, множество других плохих решений.

Уайетт Барнетт
источник
2
Можете ли вы рассказать о «других плохих дизайнерских решениях»?
Кевин Клайн
2
Спасибо. Я не нашел, что эти проблемы были проблемой в разработке Ruby on Rails. Все еще возможно проверить поведение и настойчивость отдельно. ИМО, отделяющая постоянство от поведения, имеет мало практической ценности.
Кевин Клайн
@kevin: эти вещи не являются недостатком таких рубиновых функций, как миксин и утка. Со статическими языками, такими как C #, который ОП использовал в своем вопросе, разделить их немного сложнее.
Уайет Барнетт
@Wayne: для меня классы - это просто блоки для размещения методов - я могу отделить бизнес-логику от постоянства, поместив их в отдельные классы, или я могу просто концептуально отделить их, убедившись, что бизнес-методы не делают I / О. На языках с плохой поддержкой делегирования (например, Java) это экономит огромное количество кода. OTOH, я просто вхожу в Hibernate, поэтому я могу быть совершенно не прав.
Кевин Клайн
4
Я бы добавил две вещи; 1. Связь с механизмом персистентности делает код трудным, если не невозможным, для правильного модульного тестирования. 2. Active Record склеивает ваш код с отношениями в централизованном механизме персистентности, что делает действительно трудным разделить ваш монолит, если вы когда-нибудь решите.
Истепанюк
15

Самый большой недостаток активной записи заключается в том, что ваш домен обычно тесно связан с определенным механизмом сохранения. Если для этого механизма требуется глобальное изменение, возможно, от сохранения на основе файлов к сохранению на основе БД или между средами доступа к данным, КАЖДЫЙ класс, реализующий этот шаблон, может измениться. В зависимости от языка, структуры и дизайна, даже чего-то такого простого, как изменение места нахождения БД или того, кто «владеет», может потребоваться пройти через каждый объект для обновления методов доступа к данным (это редко встречается в большинстве языков, которые обеспечивают легкий доступ настроить файлы со строками подключения).

Это также обычно требует от вас повторения. Большинство механизмов персистентности имеют много общего кода для подключения к БД и запуска транзакции. СУХОЙ (не повторяйся сам) скажет вам как программисту централизовать такую ​​логику.

Это также делает атомные операции сложными. Если группа объектов должна быть сохранена в режиме «все или ничего» (например, «Счета-фактуры» и «InvoiceLines» и / или «Customer» и / или «GL»), либо один объект должен знать обо всех этих других объектах и ​​контролировать их постоянство ( который расширяет область действия контролирующего объекта; большие взаимосвязанные записи могут легко стать «объектами бога», которые знают все об их зависимостях), или контроль над всей транзакцией должен осуществляться вне домена (и в этом случае, почему вы используете AR?)

Это также «неправильно» с объектно-ориентированной точки зрения. В реальном мире счет-фактура не знает, как подать файл, так почему объект кода счета-фактуры знает, как сохранить себя в БД? Конечно, чрезмерная религиозная приверженность «объектам следует только моделировать то, что могут делать их реальные партнеры», приведет к анемичной модели предметной области (счет-фактура также не знает, как рассчитать свою общую сумму, но разделяет вычисление этой общей суммы на другой объект вообще считается плохой идеей).

Keiths
источник
В Ruby, где постоянство можно определить или абстрагировать за очень простым ключевым словом или командой, это, вероятно, меньшая проблема. В .NET, где требуется больше LoC для настройки различных механизмов персистентности, обычно избыточно иметь соединение SQL для каждого объекта, особенно если в одной атомарной транзакции необходимо сохранить несколько объектов. Подключение к БД выглядит одинаково независимо от того, сохраняете ли вы Счет или Заказчика. Вы должны либо абстрагировать общий материал в базовый класс, общий для всех объектов ActiveRecord в вашей кодовой базе, либо извлечь его в свой собственный класс (репозиторий)
KeithS
Можете ли вы привести примеры использования Ruby ActiveRecord, которые иллюстрируют ваши идеи? Я не испытывал этих проблем, но мое приложение было довольно маленьким. Я мог бы написать миграции в Ruby и развернуть их в разных реализациях БД. Я обнаружил, что ActiveRecord в Ruby был очень полезен для устранения повторений, поэтому я не понимаю, почему вы утверждаете, что использование ActiveRecord будет иметь обратный эффект. У меня не было проблем с сохранением: я просто изменил объектную модель и позволил AR обновить базу данных, чтобы отразить объектную модель.
Кевин Клайн
2

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

Поэтому решение состоит в том, чтобы использовать реализацию ORM для Data Mapper . Это отделяет постоянный слой, и теперь мы больше ориентируемся на бизнес-логику сущностей. Доктрина является Data Mapper ORM.

Но этот подход также имеет некоторую сложность: для запросов теперь вы слишком сильно зависите от Data Mapper, создающего ориентированную на запросы среду. Для того, чтобы упростить ее еще один слой вводится между Domain Model и Mapper данных называется хранилище .

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

Например, для User Entity будет UserRepository , представляющий коллекцию пользовательских объектов того же типа (которые хранятся в таблице users), над которыми вы можете выполнить операцию. Для запроса к таблице пользователей он использует User Data Mapper, но он абстрагируется от модели домена User .

Шаблон репозитория - это тип уровня доступа к данным , другой - различия между объектами доступа к данным. Репозиторий имеет функцию агрегированного корня.

palash140
источник
Шаблоны репозитория и DAO различаются, DAO - это общий доступ к данным, репозиторий предназначен для сохранения коллекции всех объектов одного типа. Т.е. все репозитории должны иметь одинаковый интерфейс (например, массив или список). Другой король методов не принадлежит Repository. DAO - более низкий уровень, хранилище может использовать DAO. Часто программисты (особенно PHP) используют Repository в качестве DAO, но это не правильно.
xmedeko