Programming
Java
Industrial Programming
Comments 14
+14
Когда я смотрю на заумные паттерны типа DAO и Repository мне представляется какой-то сферический конь в вакууме. При работе с базой данных основную роль играет не теоретическое обоснование выбранного паттерна и стройность архитектуры, а тупо быстродействие и оптимальность выполнения. Видел кучу проектов на Spring Repository. На тестах все хорошо, но когда проект обрастает данными, приходится делать оптимизации, которые рушат всю структуру этих паттернов. Вылазят проблемы типа N+1 запросов, проблемы выборки нескольких связанных сущностей, «проекции» сущностей, etc… Поэтому все паттерны очень условны и ограничены.

Здесь же автор совершенно не убедил преимуществами Repository перед DAO.

Сложнее создавать моки для интерфейса DAO во время юнит-тестирования. Необходимо было бы реализовывать больше методов DAO даже в тех тестовых сценариях, когда они не используются;

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

Интрфейс DAO становится все более привязанным к полям класса Account. Возникает необходимость в изменении интрфейса и его реализаций при изменении типов полей класса Account.

А к чему еще он должен быть привязан? Это же DAO этой сущности! Нафига тогда он нужен, если он ничего ни знает о самой сущности? И как Repository нас избавляет от необходимого рефакторинга при изменении типа поля? В том, что типы заткнуты в Specification?

List query(AccountSpecification specification);

И как мокировать данный метод? Вы можете по этому методу сказать, какие запросы тут нужно мокировать? Если бы стоял простой findByUserName(), все было бы просто и понятно. Здесь же приходится писать дополнительную логику, узнающую specification, переданную в параметре.

Более того, specification у нас зависима от платформы, а не какой-то там абстрактный критерий!!! Т.е. простой unit-test с repository мокнутым в коллекцию и простым поиском по имени без привлечения hibernate criteria уже не сделать, если в итоге мы хотим использовать hibernate.

P.S. Риторический вопрос: какой человек в здравом уме и с лимитированным бюджетом будет писать для каждого запроса кучу рудиментарного по сути кода типа specification и прочей лабуды? Ну и апофеоз маразма — это юзать Repository с ORM, когда EntityManager решает абсолютно ВСЕ задачи, поставленные перед этим паттерном.
Извиняюсь за резкость, но по-моему подобные теоретические изыскания топят Java, заставляя адептов делать сложно простые вещи.
-4
> это юзать Repository с ORM, когда EntityManager решает абсолютно ВСЕ задачи

ORM является одной из реализаций паттерна Repository.
+1
Любую технологию нужно применять с умом. Цель статьи — разъяснить особенности и отличия двух подходов к выполнению одних и тех же действий, а не убедить, что именно этот подход является единственно верным.
Пусть реализует только те, которые используются в данном сценарии, в чем проблема? IDE не сгенерит заглушки? Или же пусть создает один универсальный осмысленный mock для DAO, реализующий все методы, который будет использоваться во всех тестах. Если DAO описывает интерфейс между логикой программы и хранилищем, то mock должен симулировать это хранилище.

Можно и так. Но для меня, например, чем меньше методов у мокнутого класса — тем проще работать с его моками.
А к чему еще он должен быть привязан? Это же DAO этой сущности! Нафига тогда он нужен, если он ничего ни знает о самой сущности? И как Repository нас избавляет от необходимого рефакторинга при изменении типа поля? В том, что типы заткнуты в Specification?

А почему он должен быть к ней привязан и что-то про нее знать? Чтобы что с этим делать? В описанном примере репозиторий знает только к какому бекэнду стучаться и все. ORM осуществляется так же каким-то другим классом, как я понимаю. Поэтому чем меньше репозиторий знает про модель — тем лучше, т.к. изменения в модели не тянут за собой изменений в логике репозитория.
И как мокировать данный метод? Вы можете по этому методу сказать, какие запросы тут нужно мокировать? Если бы стоял простой findByUserName(), все было бы просто и понятно. Здесь же приходится писать дополнительную логику, узнающую specification, переданную в параметре.

Какие вы будете выполнять запросы к БД — те и подставляйте в качестве моков. На каждый запрос отдельный мок. И, может, не один — чтобы проверить все возможные варианты со всеми возможными параметрами (корректными и некорректными).

P.S. Мне кажется, вы слишком цеплятесь за конкретные вещи в статье, посвященной довольно абстрактной тематике. Ну, замените мысленно Hibernate на MongoDB, если оно вам так режет глаз.
Спасибо за комментарий!
+2
Есть и другой взгляд на репозитории и DAO. Например, уже достаточно старый тренд «Repository is dead».
+3
Пирамида абстракций. Над SQL надстроим ORM, потом HQL, над ним паттерн Repository, потом еще что-нибудь. Но это никак не снижает сложность программы и не улучшает ее читаемость. Гради Буч говорил, что самая полезная абстракция — абстракция сущности. Т.е. чем ближе к таблице(классу) тем полезнее.
+1
у меня первым делом возникает сразу вопрос, а что делать если вам нужен запрос с join?)
0
Все просто — если нужен запрос с JOIN делаем запрос с JOIN :)
Если же речь идет о выборке связанных списков из разных таблиц, то в зависимости от ситуации.
+2
Ну допустим вам нужен left join от accounts к logs. Чтобы вы сделали в данной ситуации? Вопрос чисто из интереса)
0
Повторюсь :) Если вам нужен зачем-то join то делайте join.
Как бы сделал я — отдельные DAO accounts, logs. Склеивание на уровне PHP.
Почему так — App серверов можно поставить сколько угодно, простое горизонтальное масштабирование, в отличие от источника данных.
Далее — логи, особенно сырые хранить в базе данных, возможно, очень быстро расхочется, тут как раз появляется один из главных плюсов DAO — берем и переносим логи куда угодно, переписыв всего один класс.

Вообще запросы с join, делать при любом намеке на хайлоад не стоит, это может вызвать очень большие проблемы в будущем.
Если зачем-то очень надо таки сделать join — ну пишем запрос тупо в DAO accounts, в практике я такое часто встречал, хоть и не одобряю.
0
Это просто пример таблиц. Так в какое DAO сувать запрос с join, в accounts или logs?
При любом намеке на highload скорее всего и от реляционных БД придется отказаться.
В целом inner join очень быстро работает.
0
>В целом inner join очень быстро работает.
Дело не в скорости.
Во первых самое простое масштабирование разнести таблицы по разным серверам.
Далее можно заменить конкретный источник на другой тип базы данных, либо на что-то еще, на демон например.

>При любом намеке на highload скорее всего и от реляционных БД придется отказаться.
Это не так

>Это просто пример таблиц. Так в какое DAO сувать запрос с join, в accounts или logs?
Зачем вообще так необходим запрос с Join?
Вообще логику можно учесть, вы выводите логи с указанием пользователя или логи пользователя.
Впрочем как по мне — пофигу, одинаково фигово
0
>Это просто пример таблиц. Так в какое DAO сувать запрос с join, в accounts или logs?
А ну да, еще можно какой-нибудь хитрый mapper намутить, но я это дело не люблю.
Оно может показаться, что так потом проще понять кто с кем связан, но по-моему проще стандартно вынести названия таблиц в константы DAO а потом тупо поиском по ним в любой момент понять кто куда обращается.
Большие проблемы обычно вызывает не понимание кто с кем связан, а сама связанность.
Only those users with full accounts are able to leave comments., please.