Как не усложнить работу с ActiveRecord
В прошлом посте мы разобрали классический вариант реализации Active Record. Обсудили, когда стоит переходить от Transactional Script к Active Record.
Сегодня обсудим:
- Какую логику помещать в Active Record.
- Стоит ли использовать классический вариант реализации Active Record.
Несколько важных правил, чтобы Active Record не превратился в монстра
📌 Внутри Active Record помещайте только простые инварианты: проверки статусов, ограничения значений, пересчет полей, базовые проверки.
📌 Не включайте сложную бизнес-логику: расчёты цен с кучей правил, сложные жизненные циклы. Для этого уже нужен Domain Model.
📌 Избегайте анемии: не делайте getters/setters публичными без необходимости. Лучше использовать методы вроде order.addItem(...), а не order.getItems().add(...).
📌 Не превращайте сущность в "божественный объект" (God object) — если методов больше 10-15 и большая часть из них не про состояние самого объекта, пора задуматься о вынесении логики.
Логика сохранения самого себя в базу: актуально ли?
В классическом варианте Active Record содержит логику по сохранению и загрузке самого себя из и в базу данных. В настоящий момент использование такого подхода — уже не лучшая идея. Такая реализация нарушает принципы единой ответственности и вносит дополнительные сложности в поддержку и развитие функциональности.
В настоящий момент лучше делегировать логику по сохранению и загрузке объектов репозиторию, а не оставлять ее в Active Record. Но это уже не будет классическим паттерном Active Record, а, скорее всего, что-то среднее между Active Record и Domain Model.
Если мы оставляем работу с БД внутри объекта, мы получаем следующие проблемы:
✘ Смешивание инфраструктуры и бизнес-логики: объект знает и о том, как посчитать скидку, и о том, как сгенерировать SQL-запрос (или обратиться к EntityManager).
✘ Сложность тестирования: вам придется использовать моки базы данных или поднимать Testcontainers даже для проверки простой математики.
✘ Связность (Coupling): доменная логика становится намертво привязана к конкретному ORM-фреймворку.
Реализация Active Record с делегированием логики в репозитории
Прошлый пример в новом подходе будет выглядеть так:
Когда стоит оставить Active Record и не переходить дальше:
- Бизнес-логика остаётся небольшой — несколько проверок + расчёты.
- Поддомен не основной (core), а вспомогательный (supporting) или общий (generic).
- Важны скорость разработки и минимум абстракций.
- Проект небольшой или средний, нет планов на очень сложную доменную модель.
- Уже используете ORM, где Active Record — естественный стиль (Panache, Eloquent и т. п.).
В core-доменах с высокой скоростью изменений и большой ценой ошибки переходите к Domain Model. В остальном Active Record — это разумный компромисс между простотой и выразительностью.
А вы где чаще останавливаетесь — на Transactional Script, Active Record или сразу прыгаете в богатую модель?
Подпишись на мой канал в telegram