Data Access Objects
Data Access Objects (DAO, объекты доступа к данным) — это шаблон проектирования, используемый в разработке программного обеспечения для абстрагирования и инкапсуляции операций доступа к хранилищу данных, такому как база данных, файловая система или внешний сервис. Основная цель DAO — отделить бизнес-логику приложения от деталей реализации уровня хранения данных, обеспечивая унифицированный интерфейс для выполнения операций создания, чтения, обновления и удаления (CRUD) без привязки к конкретной технологии базы данных или API.
История и происхождение
Концепция DAO получила широкое распространение в конце 1990-х — начале 2000-х годов в контексте разработки корпоративных приложений на Java (J2EE), где данный шаблон был систематизирован в каталоге «шаблонов проектирования Core J2EE» группы Sun Microsystems. Идея опиралась на более общий принцип «разделения ответственности» (Separation of Concerns), в рамках которого доступ к данным выделяется в отдельный слой. Зарождение подхода связано с необходимостью упростить тестирование и повторное использование кода: разработчики могли писать бизнес-логику, не зная, как именно будут сохраняться объекты, а замену базы данных (например, с MySQL на PostgreSQL или на XML-файлы) можно было осуществить заменой реализации DAO без изменения остальных частей приложения.
Основные принципы
DAO базируется на нескольких ключевых идеях:
- Абстракция источника данных. DAO скрывает от вызывающего кода все технические детали: SQL-запросы, работу с курсорами, транзакции, обработку исключений низкого уровня. Например, запрос «найти пользователя по идентификатору» может выполняться по-разному в реляционной базе, NoSQL-хранилище или REST-сервисе, но для кода, вызывающего DAO, это единый вызов метода
findById().
- Инкапсуляция CRUD. Каждый DAO обычно предоставляет стандартный набор методов:
create(entity),findById(id),findAll(),update(entity),delete(entity)илиdeleteById(id). Реализация может быть дополнена специфическими методами поиска (например,findByName(String name)).
- Интерфейс и реализация. Типичная структура включает интерфейс (или абстрактный класс), описывающий контракт, и один или несколько конкретных классов, реализующих этот интерфейс для разных хранилищ. Это позволяет легко подменять реализации (например, в тестах использовать in-memory DAO, а в production — DAO на основе PostgreSQL).
- Работа с объектами предметной области (Domain Objects). DAO обычно оперирует объектами, представляющими бизнес-сущности (например,
User,Order,Product). Он отвечает за преобразование данных из формата хранилища (строки таблицы, JSON-документы) в объекты приложения и обратно.
Различия с другими шаблонами
В литературе и на практике шаблон DAO часто сравнивается с Repository. Основное различие состоит в уровне абстракции:
- DAO фокусируется на работе с одним типом источника данных и часто имитирует операции на уровне таблицы (один DAO — одна таблица или коллекция). Он может возвращать сырые данные, близкие к структуре хранилища.
- Repository работает на более высоком уровне, скрывая не только источник данных, но и логику формирования запросов. Repository может возвращать агрегаты бизнес-сущностей, объединяя данные из нескольких источников, и часто использует DAO внутри себя.
DAO также следует отличать от Active Record (популярного в Ruby on Rails, ASP.NET MVC с Entity Framework, Yii2), где объект предметной области сам содержит методы для сохранения и загрузки данных, нарушая принцип единственной ответственности.
Структура и примеры реализации
### Типичная структура классов
На примере Java:
- Класс предметной области —
Userс полямиid,name,email. - Интерфейс DAO —
UserDaoс методамиvoid save(User user),User findById(int id),List<User> findAll(),void update(User user),void delete(User user). - Конкретная реализация —
UserDaoJdbcImpl, использующая JDBC и SQL-запросы. - Фабрика или DI-контейнер, предоставляющий экземпляр реализации вызывающему коду.
### Пример на Java с JDBC
``java public interface UserDao { User findById(int id); List<User> findAll(); void save(User user); void update(User user); void delete(int id); } ``
Реализация для MySQL может содержать: ``java public class UserDaoJdbcImpl implements UserDao { private Connection connection = ... // получение через DataSource @Override public User findById(int id) { String sql = "SELECT FROM users WHERE id = ?"; try (PreparedStatement stmt = connection.prepareStatement(sql)) { stmt.setInt(1, id); ResultSet rs = stmt.executeQuery(); if (rs.next()) { return new User(rs.getInt("id"), rs.getString("name"), rs.getString("email")); } } catch (SQLException e) { / обработка */ } return null; } // прочие методы... } ``
### Пример на PHP (PDO)
``php class UserDao { private $pdo; public function __construct(PDO $pdo) { $this->pdo = $pdo; } public function findById($id) { $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = ?'); $stmt->execute([$id]); return $stmt->fetchObject('User'); } } ``
Преимущества и недостатки
### Преимущества
- Снижение связанности. Бизнес-логика не зависит от конкретной базы данных или технологии доступа.
- Упрощение тестирования. В unit-тестах бизнес-логики можно подставить тестовый DAO, который не обращается к реальной БД, что ускоряет выполнение тестов.
- Удобство рефакторинга. Смена поставщика базы данных (например, с MySQL на PostgreSQL) требует замены только реализации DAO.
- Переиспользование кода. DAO могут быть легко переиспользованы в разных частях приложения.
- Централизация управления доступа к данным. Обработка соединений, транзакций, кэширования и протоколирования может быть сконцентрирована в одном слое.
### Недостатки
- Избыточность кода. Для каждой сущности приходится писать отдельный DAO, что увеличивает объём шаблонного кода, особенно в приложениях с таблицами.
- Сложность поддержки для сложных запросов. При динамических запросах (с переменным набором фильтров, сортировок) DAO может стать громоздким, могут потребоваться дополнительные паттерны (Specification, Query Object).
- Потенциальное дублирование SQL-логики. Если запросы для разных сущностей похожи, их всё равно приходится реализовывать в каждом DAO отдельно.
- Усложнение архитектуры. В простых приложениях или скриптах введение DAO может быть излишним и привести к избыточному коду.
Применение
DAO широко применяется в многослойных архитектурах веб-приложений (Java Spring, PHP Laravel/Symfony, Python Django/Flask с ORM, C# ASP.NET). В современных фреймворках роль DAO часто берут на себя ORM (Hibernate, Doctrine, Entity Framework Core, SQLAlchemy), которые предоставляют готовый слой доступа к данным, однако разработчики могут дополнительно писать пользовательские DAO-классы для операций, не покрываемых стандартными методами.
В микросервисной архитектуре DAO часто используются на уровне каждого микросервиса, где они являются мостом между бизнес-логикой и базой данных этого сервиса.
Связь с другими паттернами
- Factory Pattern. DAO часто создаются с помощью фабричного метода или абстрактной фабрики для выбора нужной реализации.
- Singleton. Для сокращения количества соединений в приложении может использоваться единственный экземпляр DAO.
- Service Layer. В классической трехуровневой архитектуре слой сервисов использует DAO для получения данных, а бизнес-правила реализуются на уровне сервисов.
- Composite. Иногда для работы с иерархиями данных применяют составные DAO.
Интересные факты
- В языке Java разработчики компании Sun официально описали DAO как один из ключевых паттернов для J2EE, что способствовало его популяризации.
- В современных средах, поддерживающих аспектно-ориентированное программирование (AOP), например Spring AOP, реализация DAO может быть сгенерирована автоматически на основе репозитория или @Repository-аннотации, уменьшая количество ручного кода.
- Один из первых проектов, популяризовавших DAO для широкой аудитории PHP-разработчиков, — фреймворк Zend Framework 1 (2006 год), где DAO были представлены как один из компонентов уровня модели.
Источники
- Gamma E., Helm R., Johnson R., Vlissides J. Design Patterns: Elements of Reusable Object-Oriented Software (1994) — в книге описан общий принцип разделения доступа к данным.
- Alur D., Crupi J., Malks D. Core J2EE Patterns: Best Practices and Design Strategies (2001) — первая систематизация DAO как отдельного паттерна.
- Fowler M. Patterns of Enterprise Application Architecture (2002) — описание и критика подхода DAO в контексте архитектуры приложений.
- Документация фреймворков Spring Framework (Spring Data JPA,elf4j), Doctrine (PHP), Entity Framework (C#) — практические реализации DAO/Repository.
- Richardson C. Введение в шаблон проектирования Data Access Object — технические статьи на IBM developerWorks (2006).
- Материалы сообщества Stack Overflow, статьи на Baeldung и Okkez.ru по тематике DAO и его отличий от Repository.
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →