Как правильно делать DTO?
От: vsb Казахстан  
Дата: 07.04.18 15:31
Оценка:
Под DTO я понимаю классы (структуры) с данными, без какой-либо существенной логики. Например такие классы часто принимает/возвращает слой доступа к БД или с ними оперируют REST-контролеры, веб-сервисы и тд.

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

Какие вижу минусы: во-первых часто есть несколько запросов, которые возвращают разное число данных. Скажем одно дело это вытащить сущность по ID, тут можно и поджойнить другие сущности, другое дело это делать сложный селект, возвращающий тысячу сущностей, любой джойн тут неприятен, да и просто лишнее поле тянуть не хочется, если оно не нужно. Т.е. запросы по сути возвращают разные типы, но логически у них куча общих полей и делать разные классы на каждый запрос это странно, одних имён не напасёшься на такие классы, к тому же нередко код работает со всеми этими классами и в типизированном языке придётся выдумывать какой-то ад с интерфейсами. Проще просто сделать один мегакласс со всеми полями ну и каждый запрос заполняет некоторое их подмножество. В итоге каждое поле может принимать значение undefined, которое в той же Java напрямую не выражается. Если сильно не мудрствуя использовать null, то легко допустить баг, когда код думает, что это поле извлечено из базы, а оно не извлечено, система типов не помогает и хорошо, есть будет NPE, а то и просто ошибка в логике. JavaScript теоретически тут лучше, каждое значение может быть undefined, хотя на практике с такими значениями может быть непросто работать правильно.

Второй вариант это сделать для каждого поля значения undefined, причём такой undefined, что при любой попытке его использовать вылетит ошибка. Либо сделать класс-холдер, либо специальным образом генерировать DTO-классы. Мне такой подход нравится больше всего, но его тяжело использовать: наивный подход ведёт к большому перерасходу памяти, излишней нагрузке на GC и дополнительным переходам по ссылкам, по крайней мере в Java, а грамотный подход требует специальной библиотеки, которую не так тривиально написать.

Попутно есть ещё одна проблема. Часто при работе с БД прикручивают кеширование. С другой стороны если у нас есть класс с "сеттерами", то можно получить закешированное значение, вызвать на него сеттер (нечаянно) и забыть про это. В следующий раз у нас это же закешированное значение уже будет с изменённым полем. То бишь хочется часто отдавать иммутабельные объекты. С другой стороны объект того же класса рядом должен быть мутабельным, получили значение, поменяли, сохранили в БД. Делать все объекты иммутабельными, на мой взгляд, это перебор. Поэтому хочется помимо неинициализованности хранить ещё для каждого поля его иммутабельность. Ну и тоже эффективно, конечно же, а не плодить врапперы.

Ну и для ORM-подобного функционала хочется отслеживания изменений, то бишь чтобы update сохранил только изменённые поля.

В итоге получается какой-то не совсем простой DTO-объект.

Как эти проблемы решаются? Особенно буду благодарен информации про Java, но и для других языков интересно. В той же Java пока нигде не видел нормальных решений, обычно никак не решаются эти проблемы, посему чешутся руки написать свой "фреймворк" с мегаоптимальной генерацией байткода.
Отредактировано 07.04.2018 15:35 vsb . Предыдущая версия . Еще …
Отредактировано 07.04.2018 15:35 vsb . Предыдущая версия .
Отредактировано 07.04.2018 15:33 vsb . Предыдущая версия .
Отредактировано 07.04.2018 15:31 vsb . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.