DTO vs POCO vs Value Object
Определения DTO, POCO и Value Object
Вначале небольшая ремарка по поводу Value Object. В C# существует похожая концепция, называемая Value Type. Это всего лишь деталь имплементации того, как объекты хранятся в памяти и мы не будем касаться этого. Value Object, о котором пойдет речь, — понятие из среды DDD (Domain-Driven Design).
Ок, давайте начнем. Вы возможно заметили, что такие понятия как DTO, Value Object и POCO часто используются как синонимы. Но действительно ли они означают одно и то же?
DTO — это класс, содержащий данные без какой-либо логики для работы с ними. DTO обычно используются для передачи данных между различными приложениями, либо между слоями внутри одного приложения. Их можно рассматривать как хранилище информации, единственная цель которого — передать эту информацию получателю.
С другой стороны, Value Object — это полноценный член вашей доменной модели. Он подчиняется тем же правилам, что и сущности (Entities). Единственное отличие между Value Object и Entity в том, что у Value Object-а нет собственной идентичности. Это означает, что два Value Object-а с одинаковыми свойствами могут считаться идентичными, в то время как две сущности отличаются друг от друга даже в случае если их свойства полностью совпадают.
Value Object-ы могут содержать логику и обычно они не используются для передачи информации между приложениями.
POJO был представлен Мартином Фаулером в качестве альтернативы для JavaBeans и других «тяжелых» enterprise-конструкций, которые были популярны в ранних 2000-х.
Основной целью POJO было показать, что домен приложения может быть успешно смоделирован без использования JavaBeans. Более того, JavaBeans вообще не должны быть использованы для этой цели.
Другой хороший пример анти-POCO подхода — Entity Framework до версии 4.0. Каждый класс, сгенерированный EF, наследовал от EntityObject, что привносило в домен логику, специфичную для EF. Начиная с версии 4, Entity Framework добавил возможность работать с POCO моделью — возможность использовать классы, которые не наследуются от EntityObject.
Таким образом, понятие POCO означает использование настолько простых классов насколько возможно для моделирования предметной области. Это понятие помогает придерживаться принципов YAGNI, KISS и остальных best practices. POCO классы могут содержать логику.
Корреляция между понятиями
Есть ли связи между этими тремя понятиями? В первую очередь, DTO и Value Object отражают разные концепции и не могут использоваться взаимозаменяемо. С другой стороны, POCO — это надмножество для DTO и Value Object:
Другими словами, Value Object и DTO не наследуют никаким сторонним компонентам и таким образом являются POCO. В то же время, POCO — это более широкое понятие: это может быть Value Object, Entity, DTO или любой другой класс в том случае если он не наследует компонентам, не относящимся напрямую к решаемой вами проблеме.
Вот свойства каждого из них:
Заметьте, что POCO-класс может и иметь, и не иметь собственной идентичности, т.к. он может быть как Value Object, так и Entity. Также, POCO может содержать, а может и не содержать логику внутри себя. Это зависит от того, является ли POCO DTO.
Заключение
Вышесказанное в статье можно суммировать следующим образом:
Забудьте о DAO, используйте Repository
Недавно задумался о том, чем отличаются паттерны, позволяющие абстрагироваться от работы с хранилищем данных. Много раз поверхностно читал описания и различные реализации DAO и Repository, даже применял их в своих проектах, видимо, до конца не понимая концептуальных отличий. Решил разобраться, закопался в Google и нашел статью, которая для меня разъяснила все. Подумал, что неплохо было бы перевести ее на русский. Оригинал для англочитающих здесь. Остальным интересующимся добро пожаловать под кат.
Data Access Object (DAO) — широко распространенный паттерн для сохранения объектов бизнес-области в базе данных. В самом широком смысле, DAO — это класс, содержащий CRUD методы для конкретной сущности.
Предположим, что у нас имеется сущность Account, представленная следующим классом:
Создадим интерфейс DAO для данной сущности:
Паттерн Repository
Лучшим решением будет использование паттерна Repository. Эрик Эванс дал точное описание в своей книге: «Repository представляет собой все объекты определенного типа в виде концептуального множества. Его поведение похоже на поведение коллекции, за исключением более развитых возможностей для построения запросов».
Вернемся назад и спроектируем AccountRepository в соответствии с данным определением:
Методы add и update выглядят идентично методам AccountDAO. Метод remove отличается от метода удаления, определенного в DAO тем, что принимает Account в качестве параметра вместо userName (идентификатора аккаунта). Представление репозитория как коллекции меняет его восприятие. Вы избегаете раскрытия типа идентификатора аккаунта репозиторию. Это сделает вашу жизнь легче в том случае, если вы захотите использовать long для идентрификации аккаунтов.
Если вы задумываетесь о контрактах методов add/remove/update, просто подумайте об абстрации коллекции. Если вы задумаетесь о добавлении еще одного метода update для репозитория, подумайте, имеет ли смысл добавлять еще один метод update для коллекции.
Однако, метод query является особенным. Я бы не ожидал увидеть такой метод в классе коллекции. Что он делает?
Репозиторий отличается от коллекции, если рассматривать возможности для построения запросов. Имея коллекцию объектов в памяти, довольно просто перебрать все ее элементы и найти интересующий нас экземпляр. Репозиторий работает с большим набором объектов, чаще всего, находящихся вне оперативной памяти в момент выполнения запроса. Нецелесообразно загружать все аккаунты в память, если нам необходим один конкретный пользователь. Вместо этого, мы передаем репозиторию критерий, с помощью которого он сможет найти один или несколько объектов. Репозиторий может сгенерировать SQL запрос в том случае, если он использует базу данных в качестве бекэнда, или он может найти необходимый объект перебором, если используется коллекция в памяти.
Одна из часто используемых реализаций критерия — паттерн Specification (далее спецификация). Спецификация — это простой предикат, который принимает объект бизнес-области и возвращает boolean:
Итак, мы можем создавать реализации для каждого способа выполнения запросов к AccountRepository.
Обычная спецификация хорошо работает для репозитория в памяти, но не может быть использована с базой данных из-за неэффективности.
Для AccountRepository, работающего с SQL базой данных, спецификации необходимо реализовать интерфейс SqlSpecification:
Репозиторий, использующий базу данных в качестве бекэнда, может использовать данный интерфейс для получения параметров SQL запроса. Если бы в качестве бекэнда для репозитория использовался Hibernate, мы бы использовали интерфейс HibernateSpecification, который генерирует Criteria.
SQL- и Hibernate-репозитории не используется метод specified. Тем не менее, мы находим наличие реализации данного метода во всех классах преимуществом, т.к. таким образом мы сможем использовать заглушку для AccountRepository в тестовых целях а также в кеширующей реализации репозитория перед тем, как запрос будет направлен непосредственно к бекэнду.
Мы даже можем сделать еще один шаг и использовать композицию Spicification с ConjunctionSpecification и DisjunctionSpecification для выполнения более сложных запросов. Нам кажется, что данный вопрос выходит за рамки статьи. Заинтересованный читатель может найти подробности и примеры в книге Эванса.
Что такое dao и dto
Описать простыми словами термины, встречающиеся при разработке web-приложений:
из браузера
web (controller, view, ui) layer
service layer
dao (repository) layer
domain (model, entity) layer
Plain Old Java Object, старый добрый Java-объект — это объект, состоящий чаще всего из набора полей, их геттеров/сеттеров и без дополнительной нагрузки в виде:
Extend prespecified classes, as in
Implement prespecified interfaces, as in
Contain prespecified annotations, as in (хотя статья на сайте Спринга допускает наличие аннотаций для POJO-объектов )
Термин POJO возник в качестве ответной реакции на появление платформы J2EE и ее широко распространившееся внедрение в приложениях, из-за чего, в частности, усложнился весь процесс их разработки. Мартин Фаулер с коллегами придумали данный термин для описания класса, свободного от «немого» кода, который требовался лишь для корректной работы среды выполнения
POJO был представлен в качестве альтернативы для Enterprise JavaBeans (EJB) и других «тяжелых» enterprise-конструкций, которые были популярны в 2000-х годах (об этом можно почитать в статье Сергея Немчинского)
Пример:
Таким образом, основной посыл POJO — упрощение классов-сущностей насколько, насколько это возможно для моделирования предметной области
Резюмируя все вышесказанное, можно сказать, что POJO — этот класс, который ничего не делает и имеет только состояние
не путать с Enterprise JavaBeans
это класс в языке Java, написанный по определённым правилам:
Пример:
«Сущности» — это классы, моделирующие объекты предметной области
По своей структуре они приближены к таблицам базы данных (типы и названия столбцов таблицы == типам и названиям полей в классе, взаимодействующему с конкретной таблицей)
Хранятся в domain слое
Сущности почти всегда изменяемы (мутабельны)
«Объект-значение» ⎼ термин из среды DDD ⎼ это объект без специальных методов, имеющий набор свойств (полей) примитивных типов данных или тоже Value object
Объекты данного рода проверяются на равенство исходя не из физической одинаковости (одинаковости ссылок на них), а из значений свойств
Примером VO является любой класс, который реализует равенство через равенство содержащихся в нем данных
В отличие от Entity не обладает идентичностью. На практике это означает, что объекты-значения не имеют поля-идентификатора: если два экземпляра одного объекта-значения обладают одинаковым набором атрибутов, то они равны
Объекты-значения должны быть неизменяемы (immutable). Это значит, что если требуется изменить такой объект, то для этого придется создать его новый экземпляр, вместо того чтобы изменять существующий
Объект-значение всегда должен принадлежать одной или нескольким сущностям, он не может жить собственной жизнью
Чтобы распознать объект-значение, мысленно замените его на Integer
Объекты-значения не должны иметь собственной таблицы в базе данных
Data Transfer Object, объект переноса данных ⎼ это паттерн, который предполагает использование отдельных классов для передачи данных (объектов без поведения) между слоями, c целью уменьшения количества запросов к базе данных
DTO можно рассматривать как хранилище информации, единственная цель которого — передать данные получателю
Примером DTO является любой класс, который содержит только поля и методы по извлечению этих данных
Data Access Object, объект доступа к данным — абстрактный интерфейс к какому-либо типу базы данных или иному механизму хранения
Описание проблемы
Способ доступа к данным бывает разным и зависит от источника данных. Способ доступа к базе данных зависит от типа хранилища (реляционные базы данных, объектно-ориентированные базы данных, однородные или «плоские» файлы и т.д.). Унифицированный API для доступа к этим несовместимым системам отсутствует. А использование конкретного способа доступа создает зависимость между кодом приложения и кодом доступа к данным
Такая зависимость кода может сделать миграцию приложения от одного типа источника данных к другому трудной и громоздкой
Для доступа к данным хотелось бы использовать какой-либо универсальный способ (паттерн), позволяющий скрыть процесс их получения
Для решения вышеперечисленной проблемы обычно используют паттерн DAO
Описание паттерна
Данный паттерн абстрагирует и инкапсулирует доступ к источнику данных, а также управляет соединением с ним для получения и записи данных
В самом широком смысле, DAO — это класс, содержащий CRUD-методы для конкретной сущности
DAO полностью скрывает от клиента детали реализации взаимодействия с хранилищем данных. Поскольку при изменениях источника данных представляемый DAO интерфейс не изменяется, этот паттерн дает возможность DAO принимать различные схемы хранилищ без влияния на клиента или бизнес-компоненты. По существу, DAO выполняет функцию адаптера между компонентом и источником данных
Шаблон DAO используется для связи программы, написанной на Java с реляционными базами данных через интерфейс JDBC
JDBC API позволяет в приложениях использовать SQL-команды, являющиеся стандартным средством доступа к таблицам
Также DAO — это промежуточный слой, который скрывает от клиента реализацию взаимодействия с разными хранилищами данных, способы и механизмы хранения, предлагая при этом единые требования по функционалу в виде интерфейса
Паттерн DAO позволяет:
Проблемы DAO
Большая часть людей представляет DAO некими вратами к базам данных, беспрепятственно добавляя в него множество методов для доступа к базе. Поэтому нередко можно увидеть слишком раздутое DAO c большим количеством методов на все случаи жизни
Чтобы уйти от этого, предлагается использовать паттерн Repository
«Хранилище» ⎼ паттерн, выполняющий роль коллекции объектов из domain layer в оперативной памяти
Хранилище позволяет добавлять или удалять объекты, как будто мы работаем с обычными коллекциями
Тот факт, что объект из domain layer на самом деле не находятся в хранилище, полностью скрыт от клиентских программ. Для них ⎼ это выглядит, как коллекция в памяти
Данный слой используется в Spring Data JPA
REpresentational State Transfer, передача состояния представления
REST определяет набор операций, которые, например User может выполнять над Meals
Дополнительная информация:
В чем разница между DAL, DTO и DAO в трехуровневом архитектурном стиле, включая MVC
Недавно я изучал ORM (объектно-реляционное сопоставление) и трехуровневый стиль архитектуры (презентация, бизнес и сохранение данных ). Если я правильно понимаю, я могу разделить уровень сохранения данных на уровень DTO и DAO.
Я хотел бы понять, как следующие части работают вместе в слое сохранения данных.
Вдобавок я узнал, что
Буду рад, если кто-нибудь расскажет мне правду о том, как это работает вместе.
Пожалуйста, не закрывайте этот вопрос, потому что много разных выражений, я видел это везде, эти вещи связаны друг с другом в основном в больших приложениях, и я не могу представить, как это работает.
2 ответа
Объекты передачи данных. Обычно они используются для передачи данных от контроллера к клиенту (JS). Термин также используется для POCO / POJO теми немногими, которые фактически содержат данные, полученные из базы данных.
Уровень доступа к данным абстрагирует ваши действия с базой данных с помощью DAO / Repository / POCO и т. Д. ORM помогают вам создать DAL, но его также можно реализовать без их использования.
Как указано выше, модели потребляют большую часть вашей бизнес-логики. В N-уровневом приложении, если бизнес-логика полностью разделена с целью повторного использования между приложениями / платформами, то модели в MVC называются анемичными моделями. Если BI не нужно повторно использовать в этом масштабе в вашем приложении, вы можете использовать модель для его удержания. Никакой путаницы, правда?
Буду рад, если кто-нибудь расскажет мне правду о том, как это работает вместе.
Все шаблоны MV * определяют только идею / концепцию; они не определяют реализацию. Паттерны MV * в основном сосредоточены на отделении представления от BI. Просто сконцентрируйся на этом.
Обратитесь к этому ответу для получения подробной информации о различных объектах, содержащих данные.
Вы можете сначала провести различие между шаблоном MVC и трехуровневой архитектурой. Подводить итоги:
3-х уровневая архитектура:
Теперь для вышеупомянутой трехуровневой архитектуры шаблон MVC имеет место на уровне представления (для веб-приложения):
Жизненный цикл типичного HTTP-запроса:
36 уровень. Ответы на вопросы к собеседованию по теме уровня
MVC — это такой паттерн проектирования приложение, при котором приложение разделяется на три отдельные части: модель (model), представление (view) и контроллер (controller). Модель предоставляет данные и реагируется на команды контроллера, изменяя своё состояние. Представление отвечает за отображение данных модели пользователю, реагируя на изменения модели. А контроллер интерпретирует действия пользователя, оповещая модель о необходимости изменений. Таким образом каждый из компонентов этой схемы слабо связан с другими компонентами, за счёт чего достигается гибкость программы. Чаще всего вся бизнес-логика размещается в модели, хотя иногда она содержится и в контроллере. В первом случае модель называют тонкой, в последнем — толстой.
POJO переводится как «объект Java в старом стиле». Их противопоставляют EJB-объектами. Последние следуют специальной конвенции и обычно жёстко привязаны к какому-то конкретному enterprise-фреймворку (например, у них должен быть публичный конструктор без параметров, должен быть геттеры и сеттеры для полей, они должны быть сериализуемыми и т.д.). POJO — это, соответственно, обычный класс, не наследующий ни от каких специальных классов и не реализующий никаких специальных библиотек. Обычно POJO ничего особенного не делает, и содержит только состояние.
Entity Bean — это бин, цель которого хранить некоторые данные. В логику такого бина встроен механизм сохранения себя и своих полей в базу данных. Такой объект может быть уничтожен, а потом воссоздан из базы заново. Но кроме хранения данных у него нет никакой логики. А бин в свою очередь — это особый класс, которые должен выполнять следующие правила:
Очередь (Queue) — это структура данных, работающая по принципу «Первый вошёл — первый вышел». То есть элементы в очередь добавляются с одного конца, а извлекаются — с другого. Deque — это двусторонняя очередь. В этой очереди элементы можно добавлять как в начало, так и в конец, а также брать элементы тоже можно и из начала, и из конца очереди. Соответственно есть методы, которые позволяю положить элемент (это методы add(e) и offer(e)), и есть методы, позволяющие извлечь элемент из очереди (это такие методы, как remove() и poll()). Кроме того, есть методы, которые позволяют просто получить элемент из очереди без его удаления оттуда (это методы element() и peek()). В интерфейсе Deque дополнительно есть методы для добавления элементов в начало и конец очереди, извлечения элементов из начала или конца, а также получения элементов из начала или конца очереди (без их удаления из очереди).
Среди простых реализаций можно отметить ArrayDeque, LinkedList и PriorityQueue. Также существуют много классов в Concurrent Collections, которые реализуют эти два интерфейса (оба сразу или только один из них).
