← Назад к вопросам

Какие паттерны применяются в ORM? Расскажите об Active Record и Data Mapper.?

2.0 Middle🔥 172 комментариев
#Архитектура и паттерны

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Паттерны проектирования в ORM

ORM (Object-Relational Mapping) — это технология, которая связывает объектно-ориентированную модель приложения с реляционной базой данных. Два ключевых паттерна, используемых в ORM — Active Record и Data Mapper — предлагают принципиально разные подходы к этой задаче. Их выбор существенно влияет на архитектуру приложения, уровень связности и тестируемость кода.


Active Record (Активная Запись)

Active Record — это паттерн, в котором объект модели инкапсулирует не только данные (поля), но и поведение для работы с базой данных (CRUD-операции). Модель «знает», как сохранить себя, загрузить или удалить. Это приводит к высокой связности модели и слоя данных.

Ключевые характеристики Active Record:

  • Объект = запись в таблице: Каждый экземпляр класса соответствует одной строке в таблице БД.
  • Наследование от базового класса: Модели наследуют методы save(), find(), delete() от общего класса ActiveRecord.
  • Простота и скорость разработки: Позволяет быстро создавать функциональность, так как вся логика работы с БД сосредоточена в одном месте (в модели).
  • Прямой доступ к БД из бизнес-логики: Модель может напрямую выполнять запросы.

Пример на PHP (условный синтаксис, напоминающий популярные реализации):

// Модель наследует базовый класс ActiveRecord
class User extends ActiveRecord
{
    // Свойства обычно соответствуют колонкам таблицы `users`
    public $id;
    public $name;
    public $email;

    // Бизнес-логика может быть прямо в модели
    public function sendWelcomeEmail() {
        // ... логика отправки email
    }
}

// Использование
$user = new User();
$user->name = "Анна";
$user->email = "anna@example.com";
$user->save(); // Метод save() унаследован, он вставит запись в БД

// Поиск также осуществляется через модель
$foundUser = User::find(1); // Статический метод для поиска по ID
echo $foundUser->name;
$foundUser->delete();

Популярные ORM, использующие Active Record: Laravel Eloquent, Yii2 AR, Ruby on Rails ActiveRecord.

Преимущества:

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

Недостатки:

  • Нарушение принципа единой ответственности (SRP): Модель отвечает и за бизнес-логику, и за персистентность.
  • Сложность тестирования: Для юнит-тестирования бизнес-логики требуется заглушка (mock) базы данных, так как модель тесно с ней связана.
  • Связность (Coupling): Бизнес-логика становится зависимой от конкретной СУБД и схемы таблиц.
  • Сложность при росте приложения: Модели «раздуваются», становится трудно поддерживать сложные запросы и отношения.

Data Mapper (Преобразователь Данных)

Data Mapper — это паттерн, который создает прослойку между объектами домена (бизнес-логики) и базой данных. Его главная цель — полное разделение ответственности. Объекты домена ничего не знают о том, как они сохраняются, а маппер занимается только преобразованием данных между этими двумя мирами.

Ключевые характеристики Data Mapper:

  • Разделение: Доменные модели — это простые PHP-классы (POPO — Plain Old PHP Objects) без какого-либо кода для работы с БД. Data Mapper — это отдельный класс, который знает, как перенести данные из объекта в таблицу и обратно.
  • Гибкость и чистота доменной модели: Модели содержат только состояние и поведение предметной области, что делает их независимыми и легко тестируемыми.
  • Более сложная архитектура: Требует написания или настройки отдельных классов-мапперов для каждой сущности (или использования метаданных).

Пример на PHP (упрощенно):

// Доменная модель — обычный класс
class User
{
    private $id;
    private $name;
    private $email;

    public function __construct($name, $email) {
        $this->name = $name;
        $this->email = $email;
    }

    // Геттеры, сеттеры и чистая бизнес-логика
    public function getName() { return $this->name; }
    public function changeEmail($newEmail) {
        $this->email = $newEmail;
    }
}

// Data Mapper для модели User
class UserMapper
{
    private $pdo; // Объект подключения к БД

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }

    public function save(User $user) {
        // Логика вставки или обновления в таблице `users`
        $sql = "INSERT INTO users (name, email) VALUES (?, ?)";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([$user->getName(), $user->getEmail()]);
        // Установка сгенерированного ID в объект
        $user->setId($this->pdo->lastInsertId());
    }

    public function find($id): ?User {
        // Логика выборки и создания объекта User из данных БД
        $sql = "SELECT * FROM users WHERE id = ?";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([$id]);
        $data = $stmt->fetch();

        if (!$data) return null;

        $user = new User($data['name'], $data['email']);
        $user->setId($data['id']);
        return $user;
    }
}

// Использование
$mapper = new UserMapper($pdoConnection);

// Работа с доменным объектом
$user = new User("Иван", "ivan@example.com");
$user->changeEmail("ivan.new@example.com");

// Сохранение через маппер
$mapper->save($user);

// Загрузка через маппер
$loadedUser = $mapper->find(1);

Популярные ORM, использующие Data Mapper: Doctrine (PHP), Hibernate (Java), SQLAlchemy (Python).

Преимущества:

  • Чистая архитектура: Соблюдение SRP, доменная модель независима от инфраструктуры.
  • Идеальная тестируемость: Доменную логику можно тестировать юнит-тестами без моков БД, используя обычные заглушки (stubs) для маппера.
  • Гибкость: Легче работать со сложными наследованиями, агрегатами и кастомными запросами. Можно использовать разные источники данных.
  • Масштабируемость: Лучше подходит для больших и сложных enterprise-приложений.

Недостатки:

  • Более высокая сложность: Требует больше начальной настройки и понимания архитектурных принципов.
  • Больше кода: Необходимо писать или конфигурировать отдельные классы-мапперы (хотя многие ORM автоматизируют этот процесс через метаданные).
  • Менее очевиден для новичков.

Итоговое сравнение

КритерийActive RecordData Mapper
СвязностьВысокая (модель и БД тесно связаны)Низкая (модель и БД разделены)
СложностьНизкая, проще для стартаВысокая, требует больше архитектурных решений
ТестируемостьСложнее (нужны моки БД)Легче (можно тестировать модель изолированно)
ГибкостьОграничена структурой таблицВысокая, модели не зависят от схемы БД
Идеально дляБыстрого прототипирования, небольших и средних CRUD-приложенийКрупных, сложных приложений с насыщенной бизнес-логикой, где важна чистота архитектуры

Выбор между Active Record и Data Mapper — это компромисс между скоростью разработки/простотой и чистотой архитектуры/гибкостью. Для типичного веб-сайта или блога часто выбирают Active Record (например, с Laravel). Для сложной финансовой системы или платформы с микросервисной архитектурой предпочтительнее будет Data Mapper (например, с Doctrine).