Открыть сервис

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:

  1. Класс предметной областиUser с полями id, name, email.
  2. Интерфейс DAOUserDao с методами void save(User user), User findById(int id), List<User> findAll(), void update(User user), void delete(User user).
  3. Конкретная реализацияUserDaoJdbcImpl, использующая JDBC и SQL-запросы.
  4. Фабрика или 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 →