Active Record, Repository, DAO
Три enterprise-паттерна слоя доступа к данным. Не из GoF, но критичны для backend. Каждый — другой trade-off между простотой и разделением ответственности.
Active Record — простой, но грязный
Идея: один класс хранит и данные, и логику работы с БД.
class User {
id; name;
static async find(query) { /* SELECT */ } // статический
async save { /* INSERT/UPDATE this */ } // динамический
async delete { /* DELETE this */ }
}
const user = await User.find({ name: 'Marcus' });
user.name = 'Mark';
await user.save;
Проблемы:
- Нарушает SRP/SOLID — класс отвечает и за state, и за persistence
- Нарушает separation of concerns — domain logic перемешан с CRUD
- Невозможно нормально разделить — только префиксами
Когда оправдан: анемичная модель без доменной логики, мелкие CRUD-приложения. Типичен для PHP, Ruby on Rails.
DAO (Data Access Object) — разделение state/persistence
Идея: данные в одном классе (плоский DTO), доступ к БД — в другом (DAO).
// State — только данные
class User {
id; name;
}
// DAO — только работа с БД
class UserDao {
async find(query) { /* ... */ }
async save(user) { /* ... */ }
async delete(user) { /* ... */ }
}
- Уже разделена ответственность
- DAO работает с конкретной таблицей
- Близко к mapper-паттерну
Repository — коллекция, не таблица
Идея: репозиторий — как коллекция доменных объектов. Бизнес-логика не знает, что под капотом БД, файл, или сеть.
class UserRepository {
async findById(id) { /* ... */ }
async findByEmail(email) { /* ... */ }
async save(user) { /* ... */ }
async list({ filter, page }) { /* ... */ }
}
- Репозиторий = коллекция в памяти + кеш + БД
- Семантика — доменная, не таблиц/строк
- Хорошо ложится на DDD (Aggregate Root и его репозиторий)
- Можно подменить на in-memory реализацию для тестов
ORM — тоже про data access, но антипаттерн (по автору)
автор критически относится к ORM:
- ORM скрывает SQL → программист не понимает производительность запросов
- ORM плохо ложится на сложные join'ы и денормализованные view
- Query Builder — золотая середина: ручное управление SQL + автоматизация типизации
Главные тезисы автора
- Active Record нарушает SOLID/GRASP/SoC, но простой — для CRUD без логики оправдан.
- DAO разделяет state и persistence — лучше для нагруженной доменной логики.
- Repository — это коллекция доменных объектов, не таблица.
- Query Builder — альтернатива ORM, ручной контроль над SQL.
- В Ruby/PHP Active Record норма, в Java/.NET DAO/Repository доминируют.
- В JS — все три встречаются, выбор зависит от сложности домена.
- «ORM не нужен» — повторяющийся тезис автора.
🎓 Источники
- 🎓 Паттерны Active Record, Repository, DAO · 2025-12-16
- Active Record нарушает принципы, но прост
- Анемичная модель — повод для AR
- DAO разделяет state и поведение
- Когда оправдан каждый паттерн
- 🎓 ООП, процедурное программирование, Transaction script, ORM · 2023-06-16
- 🎓 DDD в JavaScript и TS — где размещать бизнес-логику · 2025-10-03