Что такое dto в программировании

Чистим пхпшный код с помощью DTO

Это моя первая статья, так что ловить камни приготовился.

Возможно, такой подход в PHP сложился исторически, из-за отсутствия строгой типизации и такого себе ООП. Ведь как по мне, то только с 7 версии можно было более-менее реализовать типизацию+ООП, используя strict_types и type hinting.

Также вызов подобных методов может сопровождаться описанием массива, который мы будем передавать. Или вовсе передается какой-то массив с мусором, а метод просто берет нужные ему ключи. Например, сервис по созданию пользователя:

Меня такой подход не устраивает, поэтому я уже долгое время придерживаюсь DTO’шек. Мы создаем нужный нам DTO объект, который требует метод, и его уже передаем. И тогда внутри метода мы с полной уверенностью можем работать с валидными данными. Мы точно знаем, что у него есть нужные типизированные параметры, нет ничего лишнего, а конечный метод у нас становится гораздо чище.

Но тогда нам же придется инициализировать нужную DTO, а это дополнительные прелюдии перед вызовом метода, и жирнеющий контроллер. Нехорошо. Ведь контроллер, как правило, должен только получить данные, которые прилетели к нему в запросе, привести их к внутренней структуре и отдать нужному методу или сервису.

Собственно, так и появился мой пакет.

Использование ClassTransformer

С его помощью я автоматически привожу данные к нужному мне классу. Рассмотрим более подробно все также на примере создание пользователя. К нам приходит запрос, и данные из него мы должны отправить в метод. В моем случае на Laravel проекте это выглядит вот так:

В запросе к нам приходит массив параметров: name, phone и email. Пакет просто смотрит есть ли такие параметры у класса, и, если есть, сохраняет значение. В противном случае просто отсеивает их. На входе transform можно передавать не только массив, это может быть другой object, из которого также будут разобраны нужные параметры.

Но наименования аргументов могут отличаться. Тогда, в созданной нами DTO, мы можем спокойно описать свою реализацию приведения:

Существуют объекты гораздо сложнее, с параметрами определенного класса, либо массивом объектов. Что же с ними? Все просто, указываем параметру в PHPDoc путь к классу и все. В случае массива нужно указать, каких именно объектов этот массив:

Указывая параметру класс, вы можете строить более сложные цепочки связей. Пакет рекурсивно будет проходить по всем типизированным параметрам и приведет их к нужному формату.

Путь к классу обязательно должен быть полным, т.к. иначе гораздо сложнее реализовать нахождение класса для инициализации. Или это вообще будет alias и нужно будет парсить файл, а это уже будет оверхед для такого сервиса.

Метод сервиса работает с конкретным набором данным

Знаем все параметры, которые есть у объекта

Можно задать типизацию каждому параметру

Вызов метода становится проще, за счет удаления приведения вручную

В IDE работают все подсказки.

Аналоги

Он пытает решить ту же проблему используя DTO, но их подход заставляет ваш класс наследоваться от их DTO, и самим вызывать инициализацию, передавая в конструктор набор данных. При такой реализации я не вижу особо профита использования, ведь я так же могу сам прописать new DTO() и заполнить параметры.

Roadmap

Реализовать метод afterTransform, который будет вызываться после инициализации DTO. Это позволит более гибко кастомизировать приведение к классу. В данный момент, если входные ключи отличаются от внутренних DTO, нужно самому описывать метод transform. И если у нас из 20 параметров только у одного отличается ключ, нам придется описать приведение всех 20. А с методом afterTransform мы сможем кастомизировать приведение только нужного нам параметра, а все остальные обработает пакет.

Источник

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.

Заключение

Вышесказанное в статье можно суммировать следующим образом:

Источник

Структуры данных в PHP. Data Transfer Object. Предметно-ориентированный Laravel

В основе каждого проекта лежит работа с данными. Почти каждая задача приложения может быть обобщена следующим образом: предоставлять, интерпретировать и манипулировать данными любым способом, который хочет бизнес.

Вы, наверное, и сами это заметили: в начале проекта вы начинаете не с создания контроллеров, а с создания моделей, как их называет Laravel. Крупные проекты выигрывают от создания ERDs и других видов диаграмм для концептуализации того, какие данные будут обрабатываться приложением. Только когда это ясно, вы можете начать создавать точки входа, которые работают с вашими данными.

В этой статье мы рассмотрим, как работать с данными в структурированном виде, чтобы все разработчики в вашей команде могли написать приложение для обработки этих данных предсказуемым и безопасным способом.

Возможно, вы сейчас думаете о моделях, но это не совсем так. Сначала нам нужно сделать еще несколько шагов назад.

Теория типов

Чтобы понять как использовать объекты передачи данных (DTO) вам нужно будет иметь некоторые базовые знания о типизации.

Давайте уточним несколько терминов, которыми я объясню, как я буду использовать типизацию в этой статье.

Сила типизации – сильная или слабая типизация – определяет, может ли переменная изменить свой тип после того, как она была определена.

PHP – это слабо типизированный язык:

PHP имеет слабую типизацию. Будучи языком, который в основном работает с HTTP-запросом, практически все является строкой.

Даже при наличии включенной строгой типизации система типов PHP слаба. Подсказки типа обеспечивают только тип переменной в этот момент времени, без гарантии относительно любого будущего значения, которое может иметь эта переменная.

Как я уже говорил, PHP имеет слабую типизацию, поскольку все входные данные, с которыми он имеет дело, начинаются как строка. Однако у сильной типизации есть интересное свойство: данные приходят с гарантиями качества данных. Если переменная имеет тип, который не изменяется, то целый ряд неожиданных действий просто больше не может произойти.

Видите ли, математически доказуемо, что если строго типизированная программа компилируется, невозможно, чтобы у этой программы был целый ряд ошибок, которые могли бы существовать в слабо типизированных языках. Другими словами, сильная типизация дает программисту лучшую гарантию того, что код действительно ведет себя так, как он должен.

В качестве примечания: это не означает, что строго типизированный язык не может иметь ошибок! Вы прекрасно можете написать глючную реализацию. Но когда строго типизированная программа успешно компилируется, вы уверены, что определенный набор ошибок не может возникнуть в этой программе.

Статические и динамические типы

Есть еще одна концепция, которую мы должны рассмотреть: статические и динамические типы. И именно здесь все начинает становиться интересным.

Опять же, это одна из сильных сторон PHP: просто написал скрипт, обновил страницу и все работает. Это большая разница по сравнению с языком, который должен быть скомпилирован, прежде чем его можно будет запустить.

Очевидно, что существуют механизмы кэширования, которые оптимизируют это, поэтому приведенное выше утверждение является чрезмерным упрощением. Однако этого достаточно, чтобы понять суть дальше.

Опять же, есть и обратная сторона: поскольку PHP проверяет свои типы только во время выполнения, проверка типов программы может завершиться неудачей при запуске. Это означает, что у вас может быть более четкая ошибка для отладки, но все равно программа потерпела крах.

Эта проверка типов во время выполнения делает PHP динамически типизированным языком. Потому как статически типизированный язык программирования перед выполнением кода выполнит все проверки всех типов в программе при компиляции.

Эти библиотеки могут предложить довольно много информации о вашем коде, без необходимости запускать или модульно тестировать его, IDE, такая как PhpStorm, также имеет множество встроенных статических проверок.

Имея в виду всю эту справочную информацию, пришло время вернуться к ядру нашего приложения: данным.

Структурирование неструктурированных данных

Приходилось ли вам когда-нибудь работать с “массивом данных”, который на самом деле был больше, чем просто список? Вы использовали ключи массива в качестве полей? И вы чувствовали боль от того, что не знали точно, что было в этом массиве? Не будучи уверенным, действительно ли данные в нем такие, какими вы их ожидаете увидеть, или какие поля доступны?

Давайте представим себе, о чем я говорю: работа с запросами Laravel. Рассмотрим этот пример как базовую операцию CRUD для обновления существующего клиента:

Прежде чем искать решения этой проблемы, вот что вы можете сделать, чтобы понять что находится в этой свалке:

Теперь представьте себе на минуту, что вы работаете с командой из нескольких разработчиков над этим проектом, и что ваш коллега написал этот фрагмент кода пять месяцев назад: я могу гарантировать вам, что вы не будете знать, с какими данными вы работаете, не делая никаких громоздких вещей, перечисленных выше.

Оказывается, что строго типизированные системы в сочетании со статическим анализом могут быть большим подспорьем в понимании того, с чем именно мы имеем дело. Такие языки, как Rust, например, решают эту проблему просто:

Структура – это то, что нам нужно! К сожалению, PHP не имеет структур. Но у него есть массивы и объекты. Объектов может быть достаточно для решения нашей проблемы:

Типизированные свойства доступны только с PHP 7.4. В зависимости от того, когда вы читаете эту статью, вы можете еще не использовать их — у меня есть решение для вас позже в этой статье, продолжайте читать.

Для тех, кто может использовать PHP 7.4 или выше, вы можете сделать что-то вроде этого:

Статический анализатор, встроенный в вашу IDE, всегда сможет сообщить нам, с какими данными мы имеем дело.

Этот шаблон упаковки неструктурированных данных в типы, чтобы мы могли использовать наши данные надежным способом, называется “объекты передачи данных” или DTO (Data Transfer Object). Этот шаблон я настоятельно рекомендую вам использовать в ваших проектах большего размера. Для средних и маленьких проектов данный шаблон будет избыточен.

Обсуждая эту статью с коллегами, друзьями или в сообществе Laravel, вы можете столкнуться с людьми, которые не разделяют одно и то же видение сильной типизации. На самом деле есть много людей, которые предпочитают принимать динамическую и слабую типизацию PHP. И тут определенно есть что сказать в свое оправдание.

По моему опыту, однако, есть больше преимуществ в строго типизированном подходе при работе с командой из нескольких разработчиков над проектом в течение серьезного количества времени. Вы должны использовать любую возможность, чтобы уменьшить когнитивную нагрузку. Вы не хотите, чтобы разработчикам приходилось начинать отладку своего кода каждый раз, когда они хотят знать, что именно находится в переменной. Информация должна быть прямо под рукой, чтобы разработчики могли сосредоточиться на том, что важно: создании приложения.

Конечно, использование DTO имеет свою цену: существуют не только накладные расходы на определение этих классов; Вам также нужно сопоставить, например, запрос данных с DTO.

Преимущества использования DTO определенно перевешивают. Независимо от того, сколько времени вы потеряете, написав этот код, вы компенсируете его в долгосрочной перспективе.

Однако вопрос о построении DTO из “внешних” данных все еще нуждается в ответе.

Фабрики DTO

Как мы строим DTO? Я поделюсь с вами двумя возможностями, а также объясню, какая из них имеет мое личное предпочтение.

Первый-самый правильный: использование специальной фабрики.

Наличие отдельной фабрики сохраняет ваш код чистым на протяжении всего проекта. Эта фабрика должна жить в прикладном слое.

Что плохого в таком подходе? Ну, во-первых: он добавляет специфичную для приложения логику в домен. DTO, который живет в домене, теперь должен знать о CustomerRequest классе, который живет в прикладном слое.

Очевидно, что смешивание кода конкретного приложения в рамках домена-не самая лучшая идея. Однако у него есть мое предпочтение. На то есть две причины.

Во-вторых, и это более важная причина; я предпочитаю этот подход, потому что одно из собственных ограничений PHP: он не поддерживает именованные параметры.

Видите ли, вы не хотите, чтобы ваши DTO в конечном итоге имели конструктор с индивидуальным параметром для каждого свойства: это не масштабируется и очень сбивает с толку при работе со свойствами nullable или default-value. Вот почему я предпочитаю подход передачи массива в DTO, и пусть он строит себя на основе данных в этом массиве.

Поскольку именованные параметры не поддерживаются, статический анализ также недоступен, а это означает, что вы находитесь в неведении о том, какие данные необходимы при построении DTO. Я предпочитаю держать это “пребывание в темноте” в классе DTO, чтобы его можно было использовать без лишних мыслей извне.

Если бы PHP поддерживал что-то вроде именованных параметров, я бы использовал такой шаблон:

До тех пор, пока PHP не поддержит это, я бы предпочел прагматическое решение теоретически правильному. Но это зависит от тебя. Не стесняйтесь выбирать то, что подходит вашей команде лучше всего.

Альтернатива типизированным свойствам

Как я уже упоминал ранее, существует альтернатива использованию типизированных свойств для поддержки DTO: docblocks. Пакет DTO от Spatie, который выше применяли, также поддерживает их.

Однако по умолчанию docblocks не дают никаких гарантий, что данные относятся к тому типу, о котором они говорят. К счастью, PHP имеет свой API рефлексии, и с ним возможно гораздо больше.

Решение, предоставляемое этим пакетом, можно рассматривать как расширение системы типизации PHP. Если вы не можете использовать PHP 7.4 и хотите немного больше уверенности в том, что ваши типы docblock действительно соблюдаются, этот пакет вам поможет.

Поскольку данные лежат в основе почти каждого проекта, это один из самых важных строительных блоков. Объекты передачи данных предлагают вам способ работы с данными структурированным, безопасным и предсказуемым способом.

Источник

Какой смысл использовать DTO (объекты передачи данных)?

Какой смысл использовать DTO и является ли это устаревшей концепцией? Я использую POJO в слое представления для передачи и сохранения данных. Могут ли эти POJO рассматриваться как альтернатива DTO?

DTO является шаблоном и не зависит от реализации (POJO / POCO). DTO говорит, что, поскольку каждый вызов любого удаленного интерфейса стоит дорого, ответ на каждый вызов должен принести как можно больше данных. Таким образом, если требуется несколько запросов для доставки данных для конкретной задачи, данные, которые должны быть доставлены, могут быть объединены в DTO, так что только один запрос может принести все необходимые данные. Каталог шаблонов архитектуры корпоративных приложений содержит более подробную информацию.

DTO как концепция (объекты, целью которых является сбор данных, возвращаемых клиенту сервером), безусловно, не устарела.

Однако причина, по которой это понятие устарело, а не просто неправильно, состоит в том, что некоторые (в основном более старые) фреймворки / технологии требуют этого, поскольку их модели предметной области и представления не являются POJOS и вместо этого напрямую связаны с фреймворком.

Хотя DTO не является устаревшим шаблоном, он часто применяется без необходимости, что может сделать его устаревшим.

Например, скажем, у вас есть JSF ManagedBean. Общий вопрос заключается в том, должен ли бин содержать ссылку на сущность JPA напрямую или он должен поддерживать ссылку на некоторый промежуточный объект, который впоследствии преобразуется в сущность. Я слышал, что этот промежуточный объект называется DTO, но если ваши ManagedBeans и сущности работают в одной и той же JVM, то использование шаблона DTO дает мало преимуществ.

Рассмотрим аннотации Bean Validation. Ваши сущности JPA, вероятно, снабжены валидациями @NotNull и @Size. Если вы используете DTO, вам нужно будет повторить эти проверки в DTO, чтобы клиентам, использующим ваш удаленный интерфейс, не нужно было отправлять сообщение, чтобы узнать, что они не прошли базовую проверку. Представьте себе всю эту дополнительную работу по копированию аннотаций Bean Validation между вашим DTO и Entity, но если ваш View и Entities работают в одной и той же JVM, нет необходимости выполнять эту дополнительную работу: просто используйте Entities.

Ссылка IAmTheDude на Каталог шаблонов архитектуры корпоративных приложений предоставляет краткое объяснение DTO, и вот еще несколько ссылок, которые я нашел освещающими:

Источник

Наглядный пример различия DTO, POCO (POJO) и Value Object

Навеяно статьёй о различиях DTO, POCO и Value Object на Хабрахабре: DTO vs POCO vs Value Object, а также вопросом POCO vs DTO.

Нигде нет конкретных примеров. Приведите, пожалуйста, конкретный пример с небольшим описанием (или также примером), где и как его использовать и для чего.

Отличные ответы. Всем спасибо.

5 ответов 5

Представим некоторый интернет магазин. У этого магазина есть веб-интерфейс и сервер приложений, который обрабатывает логику. Некий пользователь хочет совершить какой-нибудь заказ. Для этого ему нужно выполнить ряд действий: добавить нужные товары в корзину и подтвердить заказ.

Для того, чтобы это сделать, на сервере приложений может существовать класс Order :

Конечно, их можно продублировать на клиенте, но тогда на клиенте будет доступна вся наша логика, строка подключения к БД и т. п., чего мы показывать совершенно не хотим. Поэтому, чтобы просто передать обновленное состояние, мы можем сделать для этого специальный класс:

Получается, что несмотря на то, что некоторые значения у заказа в БД и заказа в памяти сервера приложений будут отличаться, это все равно будет один и тот же объект, так как их идентификаторы совпадают.

В c# есть подходящая для этого возможность создавать значимые типы которые сравниваются по значению и при присваивании целиком копируются.

UPDATE Что касается обновленного вопроса (хоть это действительно отдельный большой вопрос, как заметил Discord):

Когда мы разрабатываем приложение мы работаем с какой-либо предметной областью. Эта предметная область может быть выражена в виде некоторой модели и действий, которые меняют состояние этой модели. Все это может быть представлено в виде набора классов. Такие классы содержат в себе как данные (в виде полей классов) так и действия, которые этими данными манипулируют (в виде методов).

Мы можем разложить данные в одни классы, а методы в другие и это тоже будет работать и будет даже более модульно. Но все равно может нести ряд минусов. Глядя на кучу данных может быть не очевидным то, что вообще с ними может происходить или кому они могут быть нужны. Тоже самое и с кучей методов. Поэтому, чтобы было еще удобнее можно разложить данные по классам как-то сгруппировав их понятным образом. Тоже самое и с методами. Данные заказа, пользователя, товара и т.п. могут стать отдельными классами так же как и классы с соответствующими методами. Это будет еще модульнее и понятнее. Но у любого подхода есть свои плюсы и минусы.

И таких мест в программе, где в зависимости от конкретного типа товара должно быть различное поведение может быть много. Конечно, это все будет работать. Но, как только у нас появляется новый тип товара, или поменяется логика обработки существующего типа товара нам придется изменить код в большом количестве мест везде, где присутствуют такие условия. Это сложнее, чем поменять все в одном месте, дольше и чревато тем, что можно что-то забыть сделать.

В данном вопросе мы говорим о языках, основной парадигмой которых является ООП. А это значит, что существует готовая инфраструктура которая поддерживает основные принципы ООП. Чтобы следовать этой парадигме и получать выгоду от готовой инфраструктуры мы можем поменять наши класс, добавив логику вычисления стоимости в них, меняя ее по необходимости в производных классах:

Каждый производный тип сам сможет определить логику своего поведения. Вся она будет в одном месте, рядом с данными. А какой из конкретных методов вызвать определит уже инфраструктура избавив нас от этой головной боли. В данном примере такой подход будет более удобен, т.к. у нас пропадет необходимость создавать куче if ‘ов по всему коду, что только упростит программу и сделает изменения более простыми.

Источник

Понравилась статья? Поделиться с друзьями:

Не пропустите наши новые статьи:

  • Что такое dsl в программировании
  • Что такое dry программирование
  • что такое dry в программировании
  • Что такое dpc watchdog violation windows 10
  • что такое dolby audio в windows 10

  • Операционные системы и программное обеспечение
    0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest
    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии