Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 13.06.16 14:55
Оценка: +1
Удивляют люди, которые считают, что правильный репозиторий должен выголядить как этот интерфейс
    interface IRepository<T> where T : class
    {
        List<T> GetAll();
        T Find(int entityId);
        T SaveOrUpdate(T entity);
        T Add(T entity);
        T Update(T entity);
        void Delete(T entity);
    }

Это ни что иное, как реализация DAO паттерна. Сам же репозиторий может совершенно не знать о механике и деталях хранения данных (база, файлы, сервис). На маленьких проектах разницу не так сильно видно. А на больших, когда помимо самих запросов, еще необходимо прикрутить кеширование, логирование, фильтрацию по служебным полям, вдруг оказывается, что репозиторий уже совсем не то же, что DAO.
Отсюда и вопросы у новичков, типа, должен ли репозиторий иметь методы GetSomethingByUser, GetUserTickets или просто CRUD не зная про другие репозитории и типы.
А современные фреймворки еще сильнее путают разрабов, реализуя весь Data Access слой. И те, что DAO называли репозиториями начинают думать, что репозитории не нужны, и можно Linq-кодом прошить всю бизнес логику. Даже картинки у Фаулера и на MSDN способствуют тому, что репозиторий воспринимается, главным образом, как провайдер данных, т.е. абстракция над RDBMS, хотя его роль более значимая при проектировании. Возможно, я начитался DDD книжек.

Не кажется ли вам, что пора идею репозитория несколько подправить, чтобы когда один говорил другому этот термин, то оба понимали что-то одно?
Re: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 13.06.16 15:58
Оценка: +4
Здравствуйте, another_coder, Вы писали:

_>Не кажется ли вам, что пора идею репозитория несколько подправить, чтобы когда один говорил другому этот термин, то оба понимали что-то одно?


Нам кажется, что идею репозитория в отношении БД давно пора похоронить.
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: Про путаницу с репозиториями и DAO
От: MozgC США http://nightcoder.livejournal.com
Дата: 13.06.16 16:17
Оценка:
Здравствуйте, IT, Вы писали:

IT>Нам кажется, что идею репозитория в отношении БД давно пора похоронить.


А что за класс у нас в проекте, называется Repository?
Re: Про путаницу с репозиториями и DAO
От: Sinix  
Дата: 13.06.16 17:02
Оценка: +5
Здравствуйте, another_coder, Вы писали:

_>Возможно, я начитался DDD книжек.

Если коротко, то domain-driven — это очень здравый и циничный подход к проектированию систем. Ещё раз: про проектирование, про "способ думать", не про реализацию. Т.е. как только очередной эксперт начинает рассказывать про репозитарий на уровне кода, потому что DDD, то можно доставить томик и ласково стучать по башке, предлагать освежить.


ну и далее по тексту
Автор: Sinix
Дата: 15.02.16
.

_>Не кажется ли вам, что пора идею репозитория несколько подправить, чтобы когда один говорил другому этот термин, то оба понимали что-то одно?

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

Начинать надо с задачи, языка и используемых фреймворков — вот тогда уже можно что-то предметно обсуждать.
Re[2]: Про путаницу с репозиториями и DAO
От: hrensgory Россия  
Дата: 13.06.16 18:54
Оценка: +1
13.06.2016 18:58, IT пишет:
> _>Не кажется ли вам, что пора идею репозитория несколько подправить,
> чтобы когда один говорил другому этот термин, то оба понимали что-то одно?
>
> Нам кажется, что идею репозитория в отношении БД давно пора похоронить.

Ура, я такой не один.

--
WBR,
Serge.
Posted via RSDN NNTP Server 2.1 beta
Re[3]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 13.06.16 20:18
Оценка:
Здравствуйте, MozgC, Вы писали:

IT>>Нам кажется, что идею репозитория в отношении БД давно пора похоронить.


MC>А что за класс у нас в проекте, называется Repository?


У нас класс так называется только потому что как-то не придумалось другого названия. Но с предложенным интерфейсом репозитория он не имеет ничего общего. Во-первых, у нас вообще нет никакого интерфейса, потому что он нафиг не нужен. Во-вторых, основная задача нашего класса — возможность тестирования процессов, которые его используют без чтения данных из базы. Т.е. это ближе к DI, чем к паттерну Repository. А здесь репозиторий нужен как часть или даже замена DAO, такой классический пережиток тяжелого прошлого.
Если нам не помогут, то мы тоже никого не пощадим.
Re: Про путаницу с репозиториями и DAO
От: Sharov Россия  
Дата: 14.06.16 11:42
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Это ни что иное, как реализация DAO паттерна. Сам же репозиторий может совершенно не знать о механике и деталях хранения данных (база, файлы, сервис). На маленьких проектах разницу не так сильно видно. А на больших, когда помимо самих запросов, еще необходимо прикрутить кеширование, логирование, фильтрацию по служебным полям, вдруг оказывается, что репозиторий уже совсем не то же, что DAO.

_>Отсюда и вопросы у новичков, типа, должен ли репозиторий иметь методы GetSomethingByUser, GetUserTickets или просто CRUD не зная про другие репозитории и типы.

У меня для всех центральных типов, т.е. master в терминологии master-detail есть свой репозиторий и вот там как раз куча вот таких методов GetSomethingByUser, GetSomethingByUser и т.д.
Кодом людям нужно помогать!
Re[2]: Про путаницу с репозиториями и DAO
От: diez_p  
Дата: 15.06.16 19:41
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, another_coder, Вы писали:


_>>Не кажется ли вам, что пора идею репозитория несколько подправить, чтобы когда один говорил другому этот термин, то оба понимали что-то одно?


IT>Нам кажется, что идею репозитория в отношении БД давно пора похоронить.


А можно по подробнее(для собственного кругозора)?
По серьезному с данными из базы мы не работаем, в основном редактирование гридов, и есть чуть по сложнее функционал — ну и все.
Простой DataGrid в .NET, очень похож на репозиторий. Хранятся объекты, со статусом Updated/Deleted/Inserted и потом по некой кнопке ОК все это пишется в базу. Это тривиальный кейс, но для редактирования гридов вполне. Если усложнить логику, то что-то делаем, делаем, делаем, потом жмем Save и все ушло в базу (тут конечно можно нарваться на конфликты, но это уже другая тема).
Re: Про путаницу с репозиториями и DAO
От: Baudolino  
Дата: 16.06.16 06:09
Оценка:
Этот вопрос в сети поднимается довольно часто, но можно поговорить о нем ещё раз.
Во-первых, для репозитория есть достаточно хорошее определение: это коллекция, реализующая долгосрочное хранение однотипных объектов модели предметной области и абстрагирующая пользователя от деталей реализации (=чуть другими словами формулировка Фаулера). Приведенный вами пример полностью соответствует этому определению, поэтому вполне может описывать интерфейс репозитория.
Во-вторых, Фаулер не выделяет отдельно DAO как паттерн и, честно говоря, я не встречал классификации, в которой одновременно существуют оба паттерна, поэтому можно их считать независимыми друг от друга. Если взглянуть например на эту статью, которая описывает DAO так, как его понимают очень многие разработчики, то можно заметить, что DAO это просто компонент слоя доступа к данным, без каких-либо дополнительных определений и условий. Фактически, под определение DAO попадают все паттерны раздела Data Source Architectural Layer отсюда.
Т.е. любой репозиторий — DAO, но не любой DAO — репозиторий.

Далее, вы пишете:
a>А на больших, когда помимо самих запросов, еще необходимо прикрутить кеширование, логирование, фильтрацию по служебным полям, вдруг оказывается, что репозиторий уже совсем не то же, что DAO.
Здесь все смешано в одну большую кучу с неверным заключением. Логика реализации "запросов" (методов, или, как в олдскульном ООП, "сообщений"?), кэширование и логирование — особенности внутренней реализации, о которых клиент не знает. Фильтрация по служебным полям — часть интерфейса DAO (и, как сказано выше, может быть, репозитория) и объекта доменной модели (свойство объекта, которое использует бизнес-логика, является частью доменной модели, поэтому слово "служебный" здесь не имеет особого смысла).
Re[2]: Про путаницу с репозиториями и DAO
От: Baudolino  
Дата: 16.06.16 06:10
Оценка:
Здравствуйте, IT, Вы писали:
IT>Нам кажется, что идею репозитория в отношении БД давно пора похоронить.
Это почему же?
Re[3]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 16.06.16 18:54
Оценка:
Здравствуйте, Baudolino, Вы писали:

IT>>Нам кажется, что идею репозитория в отношении БД давно пора похоронить.

B>Это почему же?

Потому что в последние лет 8 появились более продвинутые способы работы с БД.
Если нам не помогут, то мы тоже никого не пощадим.
Re[4]: Про путаницу с репозиториями и DAO
От: Baudolino  
Дата: 17.06.16 07:51
Оценка:
Здравствуйте, IT, Вы писали:

IT>Потому что в последние лет 8 появились более продвинутые способы работы с БД.

А можно более развернуто? Какие?
Re[2]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 17.06.16 08:18
Оценка:
Здравствуйте, Baudolino.

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

Вы писали:
B>Во-первых, для репозитория есть достаточно хорошее определение: это коллекция, реализующая долгосрочное хранение однотипных объектов модели предметной области и абстрагирующая пользователя от деталей реализации (=чуть другими словами формулировка Фаулера).
В настоящее время, такое определение, на мой взгляд, вводит в заблуждение людей, т.к. является слишком общим. За этим определением может скрываться любой функционал, абсолютно любой. И приведенный пример, действительно, удовлетворяет. Но это не репозиторий.

B>Т.е. любой репозиторий — DAO, но не любой DAO — репозиторий.

Из-за такого представления и возникает путаница в общении. Думаю, следует разделять мух от котлет: DAO — абстракция над хранилищем данным, без логики связанной с бизнесом, Repository — аналогично, как aggregate root в DDD.

Ничего не смешано. Смотрите сами и попробуйте подумать, где вы будете все это сводить в единую систему?
B>Логика реализации "запросов"
Это чистый DAO, или ORM framework.

B>кэширование и логирование — особенности внутренней реализации, о которых клиент не знает.

Это сервисная логика, которая может меняться не зависимо от хранилища по не зависящим от проектирования и ПО хотелкам либо клиента, либо самих разрабов. На каком уровне она будет использоваться?

B>Фильтрация по служебным полям — часть интерфейса DAO (и, как сказано выше, может быть, репозитория)

Служебные поля служебным полям рознь. Есть те, что являются частью бизнес логики, есть те, частью целостности хранения данных, а есть те, что нужны в рамках обслуживания системы. Куда относится, например, IsDeleted (имеется ввиду, что ничего не удаляется), а куда фильтрация по CreatedDate (имеется ввиду, не показывать слишком старые)?

И в дополнение при наличии всего-всего вы начнете думать, как же все это протестировать. Начнете выделять функциональности, чтобы от них изолироваться. Вы обнаружите, что, например, IsDeleted должен быть на уровне DAO, CreatedDate — в Repository, при этом вам 2L кеширование нужно и вы думаете о том, чтобы прицепить 3rd party библиотеку и это тоже нужно в Repsoitory. А вот аудитлог вам скорее всего понадобиться в DAO. Хотя, какую-то умную служебную (алерты для саппорта, например, или для сбора спец информации) нотификацию вы опять же засунете в репозиторй.

Конечно, условий слишком много, чтобы проводить четкую линию между между тем что куда относится. Но то, что хочется видеть, это понимание того, что Data Layer — это не один класс, в который сначала все сваливается, а потом превращается в то, что трудно тестировать и поддерживать. Это должно быть просто и сердито: DAO — объект доступа к данным, Repository — объект подготовки данных к BO. Без всяких обобщений и "перевоплощений" ропозитория в DAO, как вы писали.

И сразу отвечу на возможный вопрос о кеше и доп логике в ропозитории. Нет, она не прошивает его код, а инжектится туда, будучи описанной в других классах. И никак иначе.
Отредактировано 17.06.2016 8:30 another_coder . Предыдущая версия .
Re[3]: Про путаницу с репозиториями и DAO
От: Baudolino  
Дата: 17.06.16 09:36
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Здравствуйте, Baudolino.


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

Любой, удовлетворяющий определению паттерна. Это нормальная ситуация.

_>И приведенный пример, действительно, удовлетворяет. Но это не репозиторий.

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


B>>Т.е. любой репозиторий — DAO, но не любой DAO — репозиторий.

_>Из-за такого представления и возникает путаница в общении.
Путаница в общении обычно возникает из-за того, что кто-то что-то не знает или не понимает. Обычно решается фразой "давай для начала определим, что такое..." и глоссарием в архитектурной документации.

_>Думаю, следует разделять мух от котлет: DAO — абстракция над хранилищем данным, без логики связанной с бизнесом, Repository — аналогично, как aggregate root в DDD.

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

_>Ничего не смешано. Смотрите сами и попробуйте подумать, где вы будете все это сводить в единую систему?

B>>Логика реализации "запросов"
_>Это чистый DAO, или ORM framework.
Что такое "чистый DAO"? Что значит в данном случае "или": если это ORM, то это не DAO? Или DAO=ORM? Как от этого меняется определение репозитория?

B>>кэширование и логирование — особенности внутренней реализации, о которых клиент не знает.

_>Это сервисная логика, которая может меняться не зависимо от хранилища по не зависящим от проектирования и ПО хотелкам либо клиента, либо самих разрабов. На каком уровне она будет использоваться?
Что такое "сервисная логика"? Вспомогательная? Логика сервисного слоя? Кэширование — это алгоритмический, а не архитектурный паттерн. Как реализован ваш компонент доступа к БД — это скрытая деталь реализации. Репозиторий может кэшировать, шлюз БД — может, клиент слоя доступа к данным — может. Суть определений от этого не меняется.

B>>Фильтрация по служебным полям — часть интерфейса DAO (и, как сказано выше, может быть, репозитория)

_>Служебные поля служебным полям рознь. Есть те, что являются частью бизнес логики, есть те, частью целостности хранения данных, а есть те, что нужны в рамках обслуживания системы. Куда относится, например, IsDeleted (имеется ввиду, что ничего не удаляется), а куда фильтрация по CreatedDate (имеется ввиду, не показывать слишком старые)?
Это специфика функциональных требований к конкретной системе, которые нужно как следует анализировать. Давайте разберем на примере IsDeleted. Само по себе такое поле никогда не появляется — оно является частью требований к инфраструктуре приложения, к тому как обрабатываются данные вообще, а не конкретные сущности. Как правило, его задача — сохранить данные для аудита, планового или в чрезвычайной ситуации (для бэкапа такой способ подходит плохо). Для того, чтобы понять, на каком уровне этот флаг появляется, необходимо найти конечного пользователя и используемый им интерфейс. Если это то же самое приложение, то без вариантов — это часть доменной модели и интерфейс DAO знает о его существовании (например, в виде специальных методов типа findDeleted). Если для чтения этого флага используется другое приложение (например, штатный клиент БД) — у вас имеет место быть интеграция через БД, этот флаг является частью протокола интеграции, т.е. особенностью внутренней реализации DAO. При этом все вышесказанное никак не влияет на выбор конкретного вида DAO: это может относиться в равной степени к репозиторию, шлюзу таблицы БД (table data gateway) и т.п.

_>И в дополнение при наличии всего-всего вы начнете думать, как же все это протестировать. Начнете выделять функциональности, чтобы от них изолироваться. Вы обнаружите, что, например, IsDeleted должен быть на уровне DAO, CreatedDate — в Repository, при этом вам 2L кеширование нужно и вы думаете о том, чтобы прицепить 3rd party библиотеку и это тоже нужно в Repsoitory. А вот аудитлог вам скорее всего понадобиться в DAO. Хотя, какую-то умную служебную (алерты для саппорта, например, или для сбора спец информации) нотификацию вы опять же засунете в репозиторй.

Как я уже говорил, в моем представлении репозиторий — это разновидность DAO, и вы пока не предложили альтернативной трактовки. Проблем с тестированием не вижу вообще никаких. Даже со всем, что вы упомянули, тесты (и юнит, и интеграционные) тут будут очень простые. Что меняется от того, что у вас появляется зависимость одного компонента от другого с четко определенным интерфейсом, позволяющим при необходимости в тесте инжектировать заглушку для верификации взаимодействия?

_>Конечно, условий слишком много, чтобы проводить четкую линию между между тем что куда относится. Но то, что хочется видеть, это понимание того, что Data Layer — это не один класс, в который сначала все сваливается, а потом превращается в то, что трудно тестировать и поддерживать.


_>Это должно быть просто и сердито: DAO — объект доступа к данным, Repository — объект подготовки данных к BO. Без всяких обобщений и "перевоплощений" ропозитория в DAO, как вы писали.

Кому должно? Вот тут я, кстати, начал примерно понимать, куда вы клоните. Давайте отбросим на минутку все наши представления о паттернах-фигаттернах, и рассмотрим задачу в общем виде, чтобы определить их заново.
Итак, у нас есть бизнес-логика, реализованная в рамках ООП. Есть хранилище данных, чаще всего реляционное (РСУБД). Нам нужно обеспечить передачу данных из хранилища в бизнес-логику. Поскольку хранилище тяжелое, реализованное посторонними людьми, мы хотим в целях простоты поддержки и тестирования изолировать доступ к нему в отдельном архитектурном слое. Назовем его Data Access Layer (DAL). В этом слое у нас будет некоторое количество компонентов, которые будет использовать бизнес-логика. Следующим шагом мы определим вид этих компонентов, выбрав уровень связности бизнес-логики и хранилища. Если мы готовы пожертвовать гибкостью в пользу большей производительности, мы выбираем интерфейс компонентов, в котором каждый метод представляет собой некий запрос к БД и возвращает временный объект (DTO). Мы называем тип таких компонентов Table Data Gateway. Если мы хотим наоборот, чтобы бизнес-логика воспринимала хранилище просто как некую коллекцию, мы в интерфейсе компонентов DAL будем использовать методы вида добавить/заменить/удалить и назовем такие компоненты Repository. Каждый такой компонент будет соответствовать одной сущности модели (или одной иерархии). Определившись с интерфейсами, приступаем к выбору реализации. Мы будем использовать какое-либо стандартное API для доступа к БД, которое позволит нам выполнять SQL-запросы и получать в ответ табличные данные.
Поскольку нам нужно преобразовать реляционную модель в объектную, нам требуется объектно-реляционное отображение (ORM). Его можно реализовать самостоятельно, строя запросы и преобразуя полученные данные (в таком случае стоит вынести логику построения запросов и чтения результатов в отдельный класс — Object-Relational Mapper), а можно выбрать готовое ORM-решение, которое используя декларативную модель описания отображения, сведет код вашего репозитория к одной строчке в каждом методе.
Далее, хранилище далеко от узла развертывания, поэтому мы хотим ускорить его работу за счет алгоритма кэширования. Мы можем использовать готовый кэш, поэтому добавим в нужные методы компонента DAL попытку чтения из кэша перед чтением из БД. Кэш будет представлен у нас простым интерфейсом с методами вида get/putIfAbsent/invalidate и т.п. За инжектирование конкретной реализации кэша в наш компонент DAL ,будет отвечать контейнер DI. Но лучше всего будет в таком случае настроить кэш в ORM/f и вообще не заморачиваться на собственную реализацию логики. Что касается журналирования и обработки ошибок, замусоривать код такой вспомогательной логикой не обязательно, если у вас есть возможность завернуть ваши компоненты в прокси, которые будут журналировать вызовы методов. Это обычно делается с помощью AOP. А теперь внимание, вопрос: а где здесь собственно должно появиться ваше разделение на "объект доступа к данным" и "объект подготовки данных к ВО"? (и что такое ВО?)


_>И сразу отвечу на возможный вопрос о кеше и доп логике в ропозитории.

А кто бы ставил этот вопрос? Вроде все нормальные люди давно уже используют DI.
Re[4]: Про путаницу с репозиториями и DAO
От: Qulac Россия  
Дата: 17.06.16 10:03
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Baudolino, Вы писали:


IT>>>Нам кажется, что идею репозитория в отношении БД давно пора похоронить.

B>>Это почему же?

IT>Потому что в последние лет 8 появились более продвинутые способы работы с БД.


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

 class UserCollection
 {
   int Add(User user){}

   void Remove(User user){}

   IEnumerable<User> Find(Specification s){}
 }


Чем не репозиторий? Если при разработке использовать принципы grasp, то репозиторий должен был бы появиться.
Программа – это мысли спрессованные в код
Re[5]: Про путаницу с репозиториями и DAO
От: Sinix  
Дата: 17.06.16 10:25
Оценка:
Здравствуйте, Qulac, Вы писали:


Q>Чем не репозиторий? Если при разработке использовать принципы grasp, то репозиторий должен был бы появиться.


Кэп: код здорового человека выглядит примерно так:
[Transactional]
void SetRecordData(string code, string data)
{
  var existing = Context.FirstOrDefault<MyRecord>(r => r.Code == code);
  if (data == null)
  {
    existing?.Delete();
  }
  else
  {
    if (existing == null)
    {
       existing = Context.NewObject<MyRecord>(r => r.Code = code);
    }

    existing.Data = data;
  }  
}


Т.е. ноль технических деталей, вся магия под капотом.

Разумеется, код сферический в вакууме, просто чтоб показать основные сценарии. В отдельных случаях может оказаться выгоднее сгенерить sql merge statement, в других — заморочиться с корректной обработкой одновременных update/delete и тыды и тыпы.
Re[5]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.06.16 10:34
Оценка: +1
Здравствуйте, Qulac, Вы писали:

IT>>Потому что в последние лет 8 появились более продвинутые способы работы с БД.

Q>А причем тут БД, а если нужно хранить данные просто в памяти, а для хранения скидывать их в файл через серелизацию, как тогда организовывать работу с данными?
При том, что без БД репозиторий имеет еще меньше смысла.

Если у вас тупо коллекция в памяти, то это List<T>. На крайняк Dictionary<TKey, T>. В нужные моменты времени они сериализуются в файл, а при запуске программы десериализуются из файла.
Re[6]: Про путаницу с репозиториями и DAO
От: Qulac Россия  
Дата: 17.06.16 10:47
Оценка: +1 -1
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Qulac, Вы писали:


IT>>>Потому что в последние лет 8 появились более продвинутые способы работы с БД.

Q>>А причем тут БД, а если нужно хранить данные просто в памяти, а для хранения скидывать их в файл через серелизацию, как тогда организовывать работу с данными?
G>При том, что без БД репозиторий имеет еще меньше смысла.

G>Если у вас тупо коллекция в памяти, то это List<T>. На крайняк Dictionary<TKey, T>. В нужные моменты времени они сериализуются в файл, а при запуске программы десериализуются из файла.


Не ну это естественно, что внутри нашей коллекции будет List, Dictionary или что то еще подобное. Смысл: у нас ни чего не торчит лишнего через интерфейс, а если какие-то выборки данных повторяются в программе в многих местах, то здесь мы их объедением в нашей коллекции в виде методов. Вся логика работы с данными в одном месте, проще тестировать.
Программа – это мысли спрессованные в код
Re[6]: Про путаницу с репозиториями и DAO
От: Qulac Россия  
Дата: 17.06.16 10:49
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, Qulac, Вы писали:



Q>>Чем не репозиторий? Если при разработке использовать принципы grasp, то репозиторий должен был бы появиться.


S>Кэп: код здорового человека выглядит примерно так:

S>
S>[Transactional]
S>void SetRecordData(string code, string data)
S>{
S>  var existing = Context.FirstOrDefault<MyRecord>(r => r.Code == code);
S>  if (data == null)
S>  {
S>    existing?.Delete();
S>  }
S>  else
S>  {
S>    if (existing == null)
S>    {
S>       existing = Context.NewObject<MyRecord>(r => r.Code = code);
S>    }

S>    existing.Data = data;
S>  }  
S>}
S>


S>Т.е. ноль технических деталей, вся магия под капотом.


S>Разумеется, код сферический в вакууме, просто чтоб показать основные сценарии. В отдельных случаях может оказаться выгоднее сгенерить sql merge statement, в других — заморочиться с корректной обработкой одновременных update/delete и тыды и тыпы.


Ну на здоровье как говориться, кому как нравиться.
Программа – это мысли спрессованные в код
Re[7]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.06.16 11:45
Оценка:
Здравствуйте, Qulac, Вы писали:

Q>Здравствуйте, gandjustas, Вы писали:


G>>Здравствуйте, Qulac, Вы писали:


IT>>>>Потому что в последние лет 8 появились более продвинутые способы работы с БД.

Q>>>А причем тут БД, а если нужно хранить данные просто в памяти, а для хранения скидывать их в файл через серелизацию, как тогда организовывать работу с данными?
G>>При том, что без БД репозиторий имеет еще меньше смысла.

G>>Если у вас тупо коллекция в памяти, то это List<T>. На крайняк Dictionary<TKey, T>. В нужные моменты времени они сериализуются в файл, а при запуске программы десериализуются из файла.


Q>Не ну это естественно, что внутри нашей коллекции будет List, Dictionary или что то еще подобное. Смысл: у нас ни чего не торчит лишнего через интерфейс, а если какие-то выборки данных повторяются в программе в многих местах, то здесь мы их объедением в нашей коллекции в виде методов. Вся логика работы с данными в одном месте, проще тестировать.


В List<T> есть лишнее? Логика в разных местах? Вы о чем вообще?
Если вам в программе нужен список людей, например, то зачем городить что-то поверх List<T>?

Вам не надо тестировать List<T>. А если выборки повторяются, то делаете extension-метод для List<Person>.
Re[8]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 17.06.16 11:57
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Вам не надо тестировать List<T>. А если выборки повторяются, то делаете extension-метод для List<Person>.


А если ему надо по-разному работать с List<T> (хранилищем) в зависимости от кастомера? Разные выборки без изменения основной логики.
Re[9]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.06.16 12:05
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Здравствуйте, gandjustas, Вы писали:


G>>Вам не надо тестировать List<T>. А если выборки повторяются, то делаете extension-метод для List<Person>.


_>А если ему надо по-разному работать с List<T> (хранилищем) в зависимости от кастомера? Разные выборки без изменения основной логики.


По-разному это как? Приведи пример.
Re[6]: Про путаницу с репозиториями и DAO
От: Baudolino  
Дата: 17.06.16 12:08
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

Q>>А причем тут БД, а если нужно хранить данные просто в памяти, а для хранения скидывать их в файл через серелизацию, как тогда организовывать работу с данными?

G>При том, что без БД репозиторий имеет еще меньше смысла..
А при чем тут вообще какая-либо конкретная реализация? Списки, БД... почему вы не рассматриваете удаленный веб-сервис, например, в таком случае?
Задача любого архитектурного паттерна — фрагментация сложности. Паттерны DAL отделяют сложность бизнес-логики от сложности работы с хранилищем данных с помощью разного вида интерфейсов. Repository абстрагирует БД как коллекцию, TDG как объектный интерфейс к таблице, CQRS изолирует отдельные операции и т.д. Архитектурные паттерны — это интерфейсы, реализация которых может быть абсолютно любой. Используя их, вы говорите: клиенту должно быть всё равно, что за этим интерфейсом, пока они соблюдают контракт. Изменения в реализации, например, оптимизации производительности или журналирование, надежно скрыты за этим контрактом и не ломают клиентский код.
Реализация в виде хранилища в памяти тут просто удобная плюшка для ранних этапов разработки и юнит-тестов.
Re[7]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.06.16 12:33
Оценка: +3
Здравствуйте, Baudolino, Вы писали:

B>Здравствуйте, gandjustas, Вы писали:


Q>>>А причем тут БД, а если нужно хранить данные просто в памяти, а для хранения скидывать их в файл через серелизацию, как тогда организовывать работу с данными?

G>>При том, что без БД репозиторий имеет еще меньше смысла..
B>А при чем тут вообще какая-либо конкретная реализация? Списки, БД... почему вы не рассматриваете удаленный веб-сервис, например, в таком случае?
Потому что с веб-сервисом, базой данных и списком в памяти работа ведется совершенно по-разному.
1) Список в памяти можно обойти весь оператором foreach
2) К веб-сервису можно сделать запрос с определенными параметрами
3) К базе можно сделать SQL запрос, а еще она поддерживает транзакции.

Как вы эти три принципиально разные операции спрячете за одним универсальным интерфейсом?

В C# кстати придумали такой универсальный интерфейс — linq запросы. Но свой linq провайдер мало кто пишет, пользуется готовыми.
Вот и ответ на вопрос почему паттерн Repository бесполезен в наше время.


B>Задача любого архитектурного паттерна — фрагментация сложности. Паттерны DAL отделяют сложность бизнес-логики от сложности работы с хранилищем данных с помощью разного вида интерфейсов.

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

B>Repository абстрагирует БД как коллекцию

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


B>Архитектурные паттерны — это интерфейсы, реализация которых может быть абсолютно любой.

Не может, у них контракты разные. См выше.

B>Изменения в реализации, например, оптимизации производительности или журналирование, надежно скрыты за этим контрактом и не ломают клиентский код.

Для этого снова не нужен репозиторий как паттерн.

B>Реализация в виде хранилища в памяти тут просто удобная плюшка для ранних этапов разработки и юнит-тестов.

Само по себе добавление репозитория на ранних этапах — бесполезное усложнение кода. Можно банально обращаться к статическим спискам в памяти пока не появится необходимость сохранять данные.
Re[8]: Про путаницу с репозиториями и DAO
От: Qulac Россия  
Дата: 17.06.16 12:38
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Qulac, Вы писали:


Q>>Здравствуйте, gandjustas, Вы писали:


G>>>Здравствуйте, Qulac, Вы писали:


IT>>>>>Потому что в последние лет 8 появились более продвинутые способы работы с БД.

Q>>>>А причем тут БД, а если нужно хранить данные просто в памяти, а для хранения скидывать их в файл через серелизацию, как тогда организовывать работу с данными?
G>>>При том, что без БД репозиторий имеет еще меньше смысла.

G>>>Если у вас тупо коллекция в памяти, то это List<T>. На крайняк Dictionary<TKey, T>. В нужные моменты времени они сериализуются в файл, а при запуске программы десериализуются из файла.


Q>>Не ну это естественно, что внутри нашей коллекции будет List, Dictionary или что то еще подобное. Смысл: у нас ни чего не торчит лишнего через интерфейс, а если какие-то выборки данных повторяются в программе в многих местах, то здесь мы их объедением в нашей коллекции в виде методов. Вся логика работы с данными в одном месте, проще тестировать.


G>В List<T> есть лишнее? Логика в разных местах? Вы о чем вообще?

G>Если вам в программе нужен список людей, например, то зачем городить что-то поверх List<T>?

Зачем вообще тогда нужна архитектура по, разделение интерфейсов и прочее, прочее. Мы ведь про архитектуру говорим или нет? Тогда вообще ни чего не нужно, можно писать как бог на душу положит.

G>Вам не надо тестировать List<T>. А если выборки повторяются, то делаете extension-метод для List<Person>.


Это частный случай решения проблемы, он не решает проблему слишком широкого интерфейса list, кто муже есть не во всех языках.
Программа – это мысли спрессованные в код
Re[9]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.06.16 12:55
Оценка: +2
Здравствуйте, Qulac, Вы писали:

G>>В List<T> есть лишнее? Логика в разных местах? Вы о чем вообще?

G>>Если вам в программе нужен список людей, например, то зачем городить что-то поверх List<T>?

Q>Зачем вообще тогда нужна архитектура по, разделение интерфейсов и прочее, прочее. Мы ведь про архитектуру говорим или нет? Тогда вообще ни чего не нужно, можно писать как бог на душу положит.


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

G>>Вам не надо тестировать List<T>. А если выборки повторяются, то делаете extension-метод для List<Person>.

Q>Это частный случай решения проблемы, он не решает проблему слишком широкого интерфейса list, кто муже есть не во всех языках.
А в чем интерфейс List слишком широк? И почему это вообще должно быть проблемой?
Re[8]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 17.06.16 12:56
Оценка: :))
Здравствуйте, gandjustas.

На мой взгляд, вы не разделяете сущности от их технической реализации. В примере с тремя источниками данных, они все предлставляют собой одно и то же: хранилище данных. Для модуля системы, который использует хранилище данных, не важно что именно это (БД, IList, файл или web service). Весь функционал, который относится к доступу к данным спрятан там же и наружу не показывается. И вот тут как раз приходин на помощь объект, представляющий собой DAO. Конкретная его реализация может быть разной и зависить от уровня закладываемой абстракции в архитектуру. Это может быть EF, а может быть и просто объект с простым CRUD интерфейсом.

Вам это описание понятно?

В тексте Вы постоянно подразумеваете конкретную техническую реализацию, хотя это, в данным случае, не имеет значения.
Re[10]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 17.06.16 13:00
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>По-разному это как? Приведи пример.


Ну например, 1 выбирает по одному флагу, 2й выбирает по двум. Система мультитенант, и логика работы определяется динамически. Хранилище у всех IList.
Re[9]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.06.16 13:11
Оценка: +1
Здравствуйте, another_coder, Вы писали:

_>Здравствуйте, gandjustas.


_>На мой взгляд, вы не разделяете сущности от их технической реализации. В примере с тремя источниками данных, они все предлставляют собой одно и то же: хранилище данных. Для модуля системы, который использует хранилище данных, не важно что именно это (БД, IList, файл или web service). Весь функционал, который относится к доступу к данным спрятан там же и наружу не показывается. И вот тут как раз приходин на помощь объект, представляющий собой DAO. Конкретная его реализация может быть разной и зависить от уровня закладываемой абстракции в архитектуру. Это может быть EF, а может быть и просто объект с простым CRUD интерфейсом.


Ок, давайте конкретнее. Пусть у вас есть "хранилище данных" сотрудников.
Фактически это будет три разных хранилища:
1) Список в памяти — List<T>
2) База данных
3) Веб-сервис, который имеет только методы получения и сохранения по ID.

У вас задача поднять сотрудникам определенной категории зарплату на 10%.

Какой интерфейс универсального "хранилища данных" вы спроектируете для решения этой задачи.
Естественно писать заведомо неэффективный код — моветон.
Re[11]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.06.16 13:12
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Здравствуйте, gandjustas, Вы писали:


G>>По-разному это как? Приведи пример.


_>Ну например, 1 выбирает по одному флагу, 2й выбирает по двум. Система мультитенант, и логика работы определяется динамически. Хранилище у всех IList.

Сделать две функции не?
Re[8]: Про путаницу с репозиториями и DAO
От: Baudolino  
Дата: 17.06.16 13:18
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

G>Как вы эти три принципиально разные операции спрячете за одним универсальным интерфейсом?

Есть такое слово "абстракция", знаете? Давайте вспомним его определение: абстракция — это техника анализа, в которой выявляются существенные аспекты, а несущественные исключаются. Как работает абстракция в объектно-ориентированном анализе? Вы определяете аспекты поведения объекта, существенные для клиента, скрывая (инкапсулируя) детали, для него не существенные. Как это работает в случае с паттерном "репозиторий" на примере упомянутых вами операций?
1. Клиенту нужен foreach: репозиторий должен уметь возвращать итератор по хранящимся в нем объектам. Это может быть реализовано за счет обращения к хранилищу и кэширования результатов для оптимизации производительности. Если хранилище — область оперативной памяти, кэш не требуется, если удаленный сервис или СУБД — можно вычитывать блоки данных и сохранять в кэш.
2. Клиенту нужен поиск по параметрам: репозиторий должен предоставлять метод поиска с параметрами. Реализация будет строить фильтр или формировать запрос к хранилищу — вариантов много.
3. Клиенту нужна транзакционность: ок, но это обычно делается на уровне бизнес-логики (менеджер транзакций должен использовать общие сессии вместе со всеми транзакционными службами, не только с репозиториями, но и, например, очередями). Если вдруг вы решили все же сделать поддержку транзакций частью репозитория, придется, разумеется реализовывать ее логику.

G>В C# кстати придумали такой универсальный интерфейс — linq запросы. Но свой linq провайдер мало кто пишет, пользуется готовыми.

G>Вот и ответ на вопрос почему паттерн Repository бесполезен в наше время.
LINQ представляет собой удобный интерфейс описания возможных запросов к объектам. В случае с БД, таким объектом является компонент DAL, если точнее — ORM.
Факт существования LINQ, вообще говоря, ничего не означает.

B>>Задача любого архитектурного паттерна — фрагментация сложности. Паттерны DAL отделяют сложность бизнес-логики от сложности работы с хранилищем данных с помощью разного вида интерфейсов.

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

B>>Repository абстрагирует БД как коллекцию

G>Это первая и главная ошибка. Коллекцция объектов в памяти и таблица в БД обладают принципиально разными свойствами с точки зрения кода, даже если можно их свести к одному интерфейсу.
С точки зрения сферического коня в вакууме возможно всё. С точки зрения конкретного клиентского кода принципиальных различий может и не быть.


B>>Архитектурные паттерны — это интерфейсы, реализация которых может быть абсолютно любой.

G>Не может, у них контракты разные.
Контракт определяет интерфейс, а не его реализация. Реализация обязана соблюдать контракт по определению.

B>>Реализация в виде хранилища в памяти тут просто удобная плюшка для ранних этапов разработки и юнит-тестов.

G>Само по себе добавление репозитория на ранних этапах — бесполезное усложнение кода. Можно банально обращаться к статическим спискам в памяти пока не появится необходимость сохранять данные.
У вас десять классов, работающих с хранилищем объектов типа А. Что проще — поменять в одном месте реализацию интерфейса хранилища объектов, или поменять во всех местах работу с ним со списка на что-то иное?

P.S. Давайте для более конструктивного разговора уточним позиции. Я не утверждаю, что репозиторий нужен всегда — скорее, я имею в виду, что у этого паттерна есть смысл и область применения, поэтому утверждение что он "не нужен вообще" не является истинным и связано, скорее, с непониманием этого смысла и области применения.
Re[10]: Про путаницу с репозиториями и DAO
От: Sharov Россия  
Дата: 17.06.16 14:05
Оценка: +1 -3
Здравствуйте, gandjustas, Вы писали:


Q>>Зачем вообще тогда нужна архитектура по, разделение интерфейсов и прочее, прочее. Мы ведь про архитектуру говорим или нет? Тогда вообще ни чего не нужно, можно писать как бог на душу положит.


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


Для этого и придуманы паттерны. Современная архитектура -- это взаимодействие паттерноподобных сущностей, абстракций. А гофосккие паттерны -- платоновский идеал.
Кодом людям нужно помогать!
Re[9]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.06.16 14:10
Оценка: 110 (2) +2
Здравствуйте, Baudolino, Вы писали:

B>Здравствуйте, gandjustas, Вы писали:


G>>Как вы эти три принципиально разные операции спрячете за одним универсальным интерфейсом?

B>Есть такое слово "абстракция", знаете? Давайте вспомним его определение: абстракция — это техника анализа, в которой выявляются существенные аспекты, а несущественные исключаются.
Вы несущественными не то объявили.
Несущественные различия это делать Repository или extension-методы для List<T>. Это различие не влияет на характеристики программы.
А тип источника данных очень сильно влияет.


B>1. Клиенту нужен foreach: репозиторий должен уметь возвращать итератор по хранящимся в нем объектам. Это может быть реализовано за счет обращения к хранилищу и кэширования результатов для оптимизации производительности. Если хранилище — область оперативной памяти, кэш не требуется, если удаленный сервис или СУБД — можно вычитывать блоки данных и сохранять в кэш.

B>2. Клиенту нужен поиск по параметрам: репозиторий должен предоставлять метод поиска с параметрами. Реализация будет строить фильтр или формировать запрос к хранилищу — вариантов много.
B>3. Клиенту нужна транзакционность: ок, но это обычно делается на уровне бизнес-логики (менеджер транзакций должен использовать общие сессии вместе со всеми транзакционными службами, не только с репозиториями, но и, например, очередями). Если вдруг вы решили все же сделать поддержку транзакций частью репозитория, придется, разумеется реализовывать ее логику.
Отлично — у вас есть веб-сервис, обычный web api с Get и Post по ключу. А вы напроектировани репозиторий с поиском по параметрам. Что дальше делать?
И стоило его изначально таким делать?

G>>В C# кстати придумали такой универсальный интерфейс — linq запросы. Но свой linq провайдер мало кто пишет, пользуется готовыми.

G>>Вот и ответ на вопрос почему паттерн Repository бесполезен в наше время.
B>LINQ представляет собой удобный интерфейс описания возможных запросов к объектам. В случае с БД, таким объектом является компонент DAL, если точнее — ORM.
B>Факт существования LINQ, вообще говоря, ничего не означает.
Факт существования ORM_ов c поддержкой Linq означает. Потому что Linq может за одинаковым интерфейсом скрыть и список в памяти, и веб-сервисы (с ограничениями) и базы данных.
Но писать такое с нуля для одного приложения никто не будет, надо пользоваться готовым. Поэтому паттерн repository категорически не в тему, надо пользоваться orm.


B>>>Задача любого архитектурного паттерна — фрагментация сложности. Паттерны DAL отделяют сложность бизнес-логики от сложности работы с хранилищем данных с помощью разного вида интерфейсов.

G>>Обычная функциональная декомпозиция тоже занимается фрагметацией сложности. Для этого не нужно создавать отдельные сущности и паттерны.
B>Паттерны стандартизируют такую декомпозицию, предлагая типовой подход к решению типовых задач. Отказываться от их использования там, где оно оправдано, — изобретать ненужный велосипед.
Repository сам по себе велосипед при наличии ORM.
Вы еще докажите оправданность репозитория по сравнению с обычной декомпозицией.

B>>>Repository абстрагирует БД как коллекцию

G>>Это первая и главная ошибка. Коллекцция объектов в памяти и таблица в БД обладают принципиально разными свойствами с точки зрения кода, даже если можно их свести к одному интерфейсу.
B>С точки зрения сферического коня в вакууме возможно всё. С точки зрения конкретного клиентского кода принципиальных различий может и не быть.
Принципиальное различие в скорости работы. Как только вызовы начинают пересекать граници процесса надо внимательно следить как часто происходят обращения и сколько данных передается. Иначе получается тормозное говно.
И это тоже часть архитектуры. Причем гораздо более важная, чем паттерны.


B>>>Архитектурные паттерны — это интерфейсы, реализация которых может быть абсолютно любой.

G>>Не может, у них контракты разные.
B>Контракт определяет интерфейс, а не его реализация.
Ты чего сейчас сказал?
Контрат шире чем интерфейс. Например интерфейс не может описать сайд-эффекты.
Для ORMов контракт обычно такой — ты работает с коллекцией объектов в памяти, а потом вызываешь функцию сохранения изменений в БД. В интерфейсе сохранение никак не фигурирует. И ты можешь ошибочно подумать, что таблица в базе эквивалентна List<T>, хотя это совсем не так.


B>>>Реализация в виде хранилища в памяти тут просто удобная плюшка для ранних этапов разработки и юнит-тестов.

G>>Само по себе добавление репозитория на ранних этапах — бесполезное усложнение кода. Можно банально обращаться к статическим спискам в памяти пока не появится необходимость сохранять данные.
B>У вас десять классов, работающих с хранилищем объектов типа А. Что проще — поменять в одном месте реализацию интерфейса хранилища объектов, или поменять во всех местах работу с ним со списка на что-то иное?
Кто тебе сказал про десятки классов?

B>P.S. Давайте для более конструктивного разговора уточним позиции. Я не утверждаю, что репозиторий нужен всегда — скорее, я имею в виду, что у этого паттерна есть смысл и область применения, поэтому утверждение что он "не нужен вообще" не является истинным и связано, скорее, с непониманием этого смысла и области применения.

Я утверждаю, что у этого паттерна нет области применения, потому что для БД есть ORM, а для остальных случаев repository не подходит.
Re[11]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.06.16 14:12
Оценка: +1 :)
Здравствуйте, Sharov, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:



Q>>>Зачем вообще тогда нужна архитектура по, разделение интерфейсов и прочее, прочее. Мы ведь про архитектуру говорим или нет? Тогда вообще ни чего не нужно, можно писать как бог на душу положит.

G>>Архитектура это не тогда, когда много паттернов, а когда сделано ровно столько, сколько нужно, чтобы решить задачу и не изобретать велосипеды.
S>Для этого и придуманы паттерны. Современная архитектура -- это взаимодействие паттерноподобных сущностей, абстракций. А гофосккие паттерны -- платоновский идеал.
Для этого придуманы библиотеки и компоненты, а паттерны нужны когда готовых средств нет.
Но огромное количество программистов вместо использования готового лепят паттерны, а потом в них заворачивают готовые компоненты, так как свою реализацию написать не способны и называют это архитектурой.
Re[12]: Про путаницу с репозиториями и DAO
От: Sharov Россия  
Дата: 17.06.16 14:17
Оценка:
Здравствуйте, gandjustas, Вы писали:


S>>Для этого и придуманы паттерны. Современная архитектура -- это взаимодействие паттерноподобных сущностей, абстракций. А гофосккие паттерны -- платоновский идеал.

G>Для этого придуманы библиотеки и компоненты, а паттерны нужны когда готовых средств нет.

Пол-правды. Заиграть вместе придуманные библиотеки и компоненты тоже надо. Вот тут многие паттерны и пригождаются.
Кодом людям нужно помогать!
Re[13]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.06.16 14:19
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:



S>>>Для этого и придуманы паттерны. Современная архитектура -- это взаимодействие паттерноподобных сущностей, абстракций. А гофосккие паттерны -- платоновский идеал.

G>>Для этого придуманы библиотеки и компоненты, а паттерны нужны когда готовых средств нет.
S>Пол-правды. Заиграть вместе придуманные библиотеки и компоненты тоже надо. Вот тут многие паттерны и пригождаются.
Но не repository, о нем же разговор, не?
Re[13]: Про путаницу с репозиториями и DAO
От: Sinix  
Дата: 17.06.16 14:39
Оценка: +2
Здравствуйте, Sharov, Вы писали:

S>Пол-правды. Заиграть вместе придуманные библиотеки и компоненты тоже надо. Вот тут многие паттерны и пригождаются.

Из практики — наоборот.

Хорошие библиотеки просто подключаются и используются. А вот если автор был укушен Фаулером, то без костылей, которые превращают сфероконину в рабочий код, не обойтись.

Я ещё не видел ни одного случая, когда дизайн "от паттернов" не разбивался бы вдребезги при попытке реального использования. Даже если общий дизайн отлично продуман и за идеями есть солидный бэкграунд, всё равно даже простейшие вещи неизбежно требуют обкладывания хелперами.
Желающие поспорить могут посмотреть на популярность Rx, TPL DataFlow или CodeContracts.
Re[14]: Про путаницу с репозиториями и DAO
От: Sharov Россия  
Дата: 17.06.16 16:35
Оценка: 21 (1)
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, Sharov, Вы писали:


S>>Пол-правды. Заиграть вместе придуманные библиотеки и компоненты тоже надо. Вот тут многие паттерны и пригождаются.

S>Из практики — наоборот.

"Многие паттерны" следует заменить на "некоторые паттерны", согласен. Архитектуры бывают разные и иногда это не просто подключил 10 сторонних библиотек и используешь.

S>Хорошие библиотеки просто подключаются и используются. А вот если автор был укушен Фаулером, то без костылей, которые превращают сфероконину в рабочий код, не обойтись.


1)Архитектуры бывают разные и иногда это не просто подключил 10 сторонних библиотек и используешь.
2)Библиотеки, за исключением базовых типа io, маршалинга, синх. примитиовов и проч. не из воздуха берутся.

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


Это уже здесь обсуждалось раз 100500. Я ни в коем случае(!!!) не призываю разрабатывать от паттернов. Но. Это типовые решения -- раз, это некоторый язык общения разработчиков -- два. А mvc это вообще основа всех основ. На этом форуме все это обсуждалось 3-4 года назад и хватит.

S>Желающие поспорить могут посмотреть на популярность Rx, TPL DataFlow или CodeContracts.


Если я правильно понял Ваш посыл, то:
1) этот список можно долго дополнять;
2) смысл сравнивать слабый маркетинг и инженерные решения?

Если было бы надо, ms на силу впарило бы это все. Желания не було. Вот можно подумать win32 гений инженерной мысли, однако люди-то пользовались.


ЗЫ:Вот я это написал, и как водится перечитал сообщения на которое отвечаю -- что в этом списке делает TPL, у него вроде все неплохо? Я точно правильно понял Ваш посыл?
Кодом людям нужно помогать!
Re[15]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.06.16 16:46
Оценка: :)
Здравствуйте, Sharov, Вы писали:

S>Это уже здесь обсуждалось раз 100500. Я ни в коем случае(!!!) не призываю разрабатывать от паттернов. Но. Это типовые решения -- раз, это некоторый язык общения разработчиков -- два. А mvc это вообще основа всех основ. На этом форуме все это обсуждалось 3-4 года назад и хватит.

Аминь!
Re[15]: Про путаницу с репозиториями и DAO
От: Sinix  
Дата: 17.06.16 17:09
Оценка:
Здравствуйте, Sharov, Вы писали:

S>1)Архитектуры бывают разные и иногда это не просто подключил 10 сторонних библиотек и используешь.

Если библиотеку писали люди, осилившие FDG — именно что подключается и используется. Посмотри на сам фреймворк — полно отличных примеров и хорошего дизайна, и хорошего, но перенавороченного. А вот примеры из разряда "не делайте так вообще" по пальцам пересчитать можно и в основном это или наследство от fw 1.0 или последствия попытки поиграть в "мы модные и современные" с .Net Core.


S>2)Библиотеки, за исключением базовых типа io, маршалинга, синх. примитиовов и проч. не из воздуха берутся.

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


S>>Желающие поспорить могут посмотреть на популярность Rx, TPL DataFlow или CodeContracts.


S>Если я правильно понял Ваш посыл, то:

S>2) смысл сравнивать слабый маркетинг и инженерные решения?

Не, я именно про удобство использования — все вышеперечисленные требуют держать в голове ментальную модель самой библиотеки (местами крайне нелогичную) и пересказывать свои хотелки в терминах этой модели. Что ставит однозначный крест на сколько-нибудь широком использовании.
Ради справедливости, Rx сам по себе очень даже неплох, а свежие версии Code Contracts в принципе почти пригодны к использованию в продакшне. Но задел на старте что там, что там был прокакан (пардон мой французский) прям образцово. А проблема всё та же, что сейчас с .Net Core — внезапно™ выяснилось, что идея забить на реальные сценарии использования немножко не работает.


S>Если было бы надо, ms на силу впарило бы это все. Желания не було. Вот можно подумать win32 гений инженерной мысли, однако люди-то пользовались.

Угу. И сколько таких мазохистов осталось после появления хоть какой-то альтернативы?


S>ЗЫ:Вот я это написал, и как водится перечитал сообщения на которое отвечаю -- что в этом списке делает TPL, у него вроде все неплохо? Я точно правильно понял Ваш посыл?

Не TPL, а TPL Dataflow. У них своя атмосфера
Re[5]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 17.06.16 17:11
Оценка:
Здравствуйте, Qulac, Вы писали:

Q>А причем тут БД,


БД при этой теме. Можно посмотреть на заголовок. А сериализовать, да, можно.
Если нам не помогут, то мы тоже никого не пощадим.
Re[5]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 17.06.16 17:14
Оценка:
Здравствуйте, Baudolino, Вы писали:

IT>>Потому что в последние лет 8 появились более продвинутые способы работы с БД.

B>А можно более развернуто? Какие?

Большие дяди в последнее время используют исключительно LINQ.
Если нам не помогут, то мы тоже никого не пощадим.
Re[9]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 17.06.16 17:22
Оценка:
Здравствуйте, Qulac, Вы писали:

Q>Зачем вообще тогда нужна архитектура по, разделение интерфейсов и прочее, прочее. Мы ведь про архитектуру говорим или нет?


А зачем нужна архитектура и интерфейсы? Для ответа на этот вопрос есть другой более конкретный вопрос для каждого конкретного случая:

— Какую проблему решает конкретное разделение интерфейсов, конкретное прочее и конкретное другое прочее?

Часто оказывается, что разделение на интерфейсы делается только ради архитектуры и ничего другого. И тогда мы задаём первоначальный вопрос:

— А зачем нужна такая архитектура?

Q>Тогда вообще ни чего не нужно, можно писать как бог на душу положит.


Попробуй. Хорошо писать как бог на душу положит мало у кого получается. Это вывсшее дао программирования. Я пока таких людей не встречал.
Если нам не помогут, то мы тоже никого не пощадим.
Re[9]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 17.06.16 17:25
Оценка:
Здравствуйте, another_coder, Вы писали:

_>В примере с тремя источниками данных, они все предлставляют собой одно и то же: хранилище данных. Для модуля системы, который использует хранилище данных, не важно что именно это (БД, IList, файл или web service).


Дружище, скажи сколько ты лично написал систем, в которых неважно что именно есть источник данных (БД, IList, файл или web service)?
Если нам не помогут, то мы тоже никого не пощадим.
Re[16]: Про путаницу с репозиториями и DAO
От: Sharov Россия  
Дата: 17.06.16 17:34
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, Sharov, Вы писали:


S>>1)Архитектуры бывают разные и иногда это не просто подключил 10 сторонних библиотек и используешь.

S>Если библиотеку писали люди, осилившие FDG — именно что подключается и используется. Посмотри на сам фреймворк — полно отличных примеров и хорошего дизайна, и хорошего, но перенавороченного. А вот примеры из разряда "не делайте так вообще" по пальцам пересчитать можно и в основном это или наследство от fw 1.0 или последствия попытки поиграть в "мы модные и современные" с .Net Core.

Сам фреймворк между прочим и есть базовая библиотека. Там воленс-ноленс сложных решений быть не должно. С архитектурной точки зрения.


S>>2)Библиотеки, за исключением базовых типа io, маршалинга, синх. примитиовов и проч. не из воздуха берутся.

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

Не понял куда опять дискуссия зашла Все мало-мальски серьезные приложения используют кучу библиотек в императивном стиле штоле? Типа:
библиотека1.вызов1()
библиотека2.вызов3()
библиотека1.вызов2()
...

Или же идет оркестрации на несколько более абстрактному уровне. Т.е. если исп. стороннюю библиотеку или компонент, то паттерны ни-ни. Судя по дискуссии получается так. Я не вижу проблем использовать паттерны и сторонние компоненты, если речь об этом.

S>>>Желающие поспорить могут посмотреть на популярность Rx, TPL DataFlow или CodeContracts.


S>>Если я правильно понял Ваш посыл, то:

S>>2) смысл сравнивать слабый маркетинг и инженерные решения?

S>Не, я именно про удобство использования — все вышеперечисленные требуют держать в голове ментальную модель самой библиотеки (местами крайне нелогичную) и пересказывать свои хотелки в терминах этой модели. Что ставит однозначный крест на сколько-нибудь широком использовании.

S>Ради справедливости, Rx сам по себе очень даже неплох, а свежие версии Code Contracts в принципе почти пригодны к использованию в продакшне. Но задел на старте что там, что там был прокакан (пардон мой французский) прям образцово. А проблема всё та же, что сейчас с .Net Core — внезапно™ выяснилось, что идея забить на реальные сценарии использования немножко не работает.

S>>ЗЫ:Вот я это написал, и как водится перечитал сообщения на которое отвечаю -- что в этом списке делает TPL, у него вроде все неплохо? Я точно правильно понял Ваш посыл?

S>Не TPL, а TPL Dataflow. У них своя атмосфера

Я думал это пример не взлетевших фреймворков.
Кодом людям нужно помогать!
Re[10]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 17.06.16 20:51
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, another_coder, Вы писали:


_>>В примере с тремя источниками данных, они все предлставляют собой одно и то же: хранилище данных. Для модуля системы, который использует хранилище данных, не важно что именно это (БД, IList, файл или web service).


IT>Дружище, скажи сколько ты лично написал систем, в которых неважно что именно есть источник данных (БД, IList, файл или web service)?


Парочка найдется.
Re[6]: Про путаницу с репозиториями и DAO
От: Baudolino  
Дата: 18.06.16 06:58
Оценка: 6 (1)
Здравствуйте, IT, Вы писали:

IT>Большие дяди в последнее время используют исключительно LINQ.

Между нами, большими дядями, LINQ существует только на одной, причем не самой популярной платформе.
Re[10]: Про путаницу с репозиториями и DAO
От: Baudolino  
Дата: 18.06.16 07:45
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Вы несущественными не то объявили.

G>Несущественные различия это делать Repository или extension-методы для List<T>. Это различие не влияет на характеристики программы.
G>А тип источника данных очень сильно влияет.
Начнем с того, что я несущественным ничего не объявлял. Я продемонстрировал, как детали реализации могут быть скрыты в определенных ситуациях, но я не утверждал, что это всегда возможно. Вы понимаете различие между словами "существует" и "всегда"? А их отрицание построить можете и применить к нашему разговору?

G>Отлично — у вас есть веб-сервис, обычный web api с Get и Post по ключу. А вы напроектировани репозиторий с поиском по параметрам. Что дальше делать?

G>И стоило его изначально таким делать?
Думаю, стоит начать с того, что это "напроектировали" вы, а не я. Я не натягиваю паттерны на сферических коней.

G>Но писать такое с нуля для одного приложения никто не будет, надо пользоваться готовым. Поэтому паттерн repository категорически не в тему, надо пользоваться orm.

Ваша категоричность пока что голословна.

G>Repository сам по себе велосипед при наличии ORM.

А LINQ это, по-вашему, ORM? И, кстати, сколько разных ORM вы знаете (в смысле использовали)? На каких платформах?

G>Принципиальное различие в скорости работы. Как только вызовы начинают пересекать граници процесса надо внимательно следить как часто происходят обращения и сколько данных передается. Иначе получается тормозное говно.

G>И это тоже часть архитектуры. Причем гораздо более важная, чем паттерны.
Термин "преждевременная оптимизация" знаком? Где вы увидели пересечение "границ процесса" при реализации паттерна?

B>>Контракт определяет интерфейс, а не его реализация.

G>Ты чего сейчас сказал?
G>Контрат шире чем интерфейс. Например интерфейс не может описать сайд-эффекты.
Интерфейс не должен описывать побочные эффекты. С точки зрения пользователя интерфейса побочных эффектов не должно существовать — это аксиома ООП, которую самое время выучить.

G>Для ORMов контракт обычно такой — ты работает с коллекцией объектов в памяти, а потом вызываешь функцию сохранения изменений в БД. В интерфейсе сохранение никак не фигурирует.

Функция сохранения изменений в БД — часть интерфейса.

G>И ты можешь ошибочно подумать, что таблица в базе эквивалентна List<T>, хотя это совсем не так.

Вы всё время делаете какие-то очень странные и ничем не обоснованные суждения насчёт того, как я думаю, и выдаёте необоснованные отрицания достойные включения в "Женскую логику" Беклемишева в качестве примера отвергнутых аргументов. Предложение: вместо того, чтобы использовать фразы типа "это совсем не так", вы будете с этого момента для продолжения дискуссии аргументировать, почему не так, в каких обстоятельствах?

G>>>Само по себе добавление репозитория на ранних этапах — бесполезное усложнение кода. Можно банально обращаться к статическим спискам в памяти пока не появится необходимость сохранять данные.

B>>У вас десять классов, работающих с хранилищем объектов типа А. Что проще — поменять в одном месте реализацию интерфейса хранилища объектов, или поменять во всех местах работу с ним со списка на что-то иное?
G>Кто тебе сказал про десятки классов?
Допустим, у вас примерно десять классов... Рядовая ситуация на крупном проекте.
Re[11]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 18.06.16 13:35
Оценка: :)
Здравствуйте, Baudolino, Вы писали:

B>Здравствуйте, gandjustas, Вы писали:


G>>Вы несущественными не то объявили.

G>>Несущественные различия это делать Repository или extension-методы для List<T>. Это различие не влияет на характеристики программы.
G>>А тип источника данных очень сильно влияет.
B>Начнем с того, что я несущественным ничего не объявлял. Я продемонстрировал, как детали реализации могут быть скрыты в определенных ситуациях, но я не утверждал, что это всегда возможно. Вы понимаете различие между словами "существует" и "всегда"? А их отрицание построить можете и применить к нашему разговору?
Что сказать-то хотел?

G>>Отлично — у вас есть веб-сервис, обычный web api с Get и Post по ключу. А вы напроектировани репозиторий с поиском по параметрам. Что дальше делать?

G>>И стоило его изначально таким делать?
B>Думаю, стоит начать с того, что это "напроектировали" вы, а не я. Я не натягиваю паттерны на сферических коней.
То есть вам таки важно какой источник данных? Тогда зачем repository?

G>>Но писать такое с нуля для одного приложения никто не будет, надо пользоваться готовым. Поэтому паттерн repository категорически не в тему, надо пользоваться orm.

B>Ваша категоричность пока что голословна.
То есть объективных причин использовать repository нет?

G>>Repository сам по себе велосипед при наличии ORM.

B>А LINQ это, по-вашему, ORM?
По нашему linq это query object, а ORM это linq-провайдер, он же repository в определении Фаулера.

B>И, кстати, сколько разных ORM вы знаете (в смысле использовали)? На каких платформах?

Использовал EF, Linq2DB, Nhibernate, linq2sql на C#, FSharp.Data, Hibernate на java, а также писал свои ORM на Delphi, C# и C++.


G>>Принципиальное различие в скорости работы. Как только вызовы начинают пересекать граници процесса надо внимательно следить как часто происходят обращения и сколько данных передается. Иначе получается тормозное говно.

G>>И это тоже часть архитектуры. Причем гораздо более важная, чем паттерны.
B>Термин "преждевременная оптимизация" знаком? Где вы увидели пересечение "границ процесса" при реализации паттерна?
Мне знаком термин преждевременная пессимизация. Когда в угоду паттернам код пишется на порядок более медленным, чем он мог бы быть.
Пересечение границ процесса случается чуть менее чем в каждом случае, кроме хранилища в памяти. А каждый второй писать repository проектирует его, как будто это просто коллекция в памяти. Вот и получается пессимизация, которую уже крайне сложно поправить.

B>>>Контракт определяет интерфейс, а не его реализация.

G>>Ты чего сейчас сказал?
G>>Контрат шире чем интерфейс. Например интерфейс не может описать сайд-эффекты.
B>Интерфейс не должен описывать побочные эффекты. С точки зрения пользователя интерфейса побочных эффектов не должно существовать — это аксиома ООП, которую самое время выучить.
Дай ссылку на того, кто эту аксиому доказал. Мне кажется ты ООП с ФП путаешь и то неверно понимаешь побочные эффекты в ФП.
Алан Кей и Бертран Мейер как раз в своих определениях ООП опираются на побочные эффекты при взаимодействии с объектами.

G>>Для ORMов контракт обычно такой — ты работает с коллекцией объектов в памяти, а потом вызываешь функцию сохранения изменений в БД. В интерфейсе сохранение никак не фигурирует.

B>Функция сохранения изменений в БД — часть интерфейса.
Точно? А если у нас таки коллекция в памяти? Или веб-сервис?

G>>И ты можешь ошибочно подумать, что таблица в базе эквивалентна List<T>, хотя это совсем не так.

B>Вы всё время делаете какие-то очень странные и ничем не обоснованные суждения насчёт того, как я думаю, и выдаёте необоснованные отрицания достойные включения в "Женскую логику" Беклемишева в качестве примера отвергнутых аргументов. Предложение: вместо того, чтобы использовать фразы типа "это совсем не так", вы будете с этого момента для продолжения дискуссии аргументировать, почему не так, в каких обстоятельствах?
Мне приходится озвучивать твои мысли, потому что ты их не озвучиваешь и общаешься полунамеками. Я уже перестал понимать какую позицию ты пытаешься отстоять.

G>>>>Само по себе добавление репозитория на ранних этапах — бесполезное усложнение кода. Можно банально обращаться к статическим спискам в памяти пока не появится необходимость сохранять данные.

B>>>У вас десять классов, работающих с хранилищем объектов типа А. Что проще — поменять в одном месте реализацию интерфейса хранилища объектов, или поменять во всех местах работу с ним со списка на что-то иное?
G>>Кто тебе сказал про десятки классов?
B>Допустим, у вас примерно десять классов... Рядовая ситуация на крупном проекте.
И что? Какую задачу мы решаем?
Re[11]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 18.06.16 13:36
Оценка:
Здравствуйте, another_coder, Вы писали:

IT>>Дружище, скажи сколько ты лично написал систем, в которых неважно что именно есть источник данных (БД, IList, файл или web service)?

_>Парочка найдется.

Расскажи про них.
Re[12]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 18.06.16 14:34
Оценка: :))
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, another_coder, Вы писали:


IT>>>Дружище, скажи сколько ты лично написал систем, в которых неважно что именно есть источник данных (БД, IList, файл или web service)?

_>>Парочка найдется.

G>Расскажи про них.


После вас. Только не забудьте привести архитектуру с коментами почему именно такое решение было принято. Более не вижу смысла продолжать этот тред, т.к. оффтоп.
Re[12]: Про путаницу с репозиториями и DAO
От: Baudolino  
Дата: 18.06.16 15:21
Оценка: 18 (1) +1 :))
G>Дай ссылку на того, кто эту аксиому доказал.
Я пожалуй закончу наш разговор на этой чудесной цитате.
Re[13]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 18.06.16 16:16
Оценка:
Здравствуйте, Baudolino, Вы писали:

G>>Дай ссылку на того, кто эту аксиому доказал.

B>Я пожалуй закончу наш разговор на этой чудесной цитате.

Ах, простите, неверно выразился.

Приведите ссылку на общеизвестный труд по ООП, в котором данная аксиома хоть как-то используется для обоснования модели ООП и её преимуществ.
Re[13]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 18.06.16 16:18
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Здравствуйте, gandjustas, Вы писали:


G>>Здравствуйте, another_coder, Вы писали:


IT>>>>Дружище, скажи сколько ты лично написал систем, в которых неважно что именно есть источник данных (БД, IList, файл или web service)?

_>>>Парочка найдется.
G>>Расскажи про них.
_>После вас. Только не забудьте привести архитектуру с коментами почему именно такое решение было принято. Более не вижу смысла продолжать этот тред, т.к. оффтоп.

Да вот у меня как-то не было примеров, чтобы одна и та же программа могла работать с любым источником данных. Скорее обратное. Даже небольшие различия в разных движках БД приводили к различному коду, даже с применением ORM.
Re[11]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 19.06.16 20:34
Оценка:
Здравствуйте, another_coder, Вы писали:

IT>>Дружище, скажи сколько ты лично написал систем, в которых неважно что именно есть источник данных (БД, IList, файл или web service)?

_>Парочка найдется.

А оно там реально было нужно или всё ради понтов?
Если нам не помогут, то мы тоже никого не пощадим.
Re[7]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 19.06.16 20:36
Оценка: :))) :)
Здравствуйте, Baudolino, Вы писали:

IT>>Большие дяди в последнее время используют исключительно LINQ.

B>Между нами, большими дядями, LINQ существует только на одной, причем не самой популярной платформе.

Нам, большим дядям пофиг.
Если нам не помогут, то мы тоже никого не пощадим.
Re[13]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 19.06.16 21:10
Оценка: +4
Здравствуйте, another_coder, Вы писали:

G>>Расскажи про них.

_>После вас.

Категорически тянет на слив.
Если нам не помогут, то мы тоже никого не пощадим.
Re[12]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 20.06.16 05:55
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, another_coder, Вы писали:


_>>Здравствуйте, gandjustas, Вы писали:


G>>>По-разному это как? Приведи пример.


_>>Ну например, 1 выбирает по одному флагу, 2й выбирает по двум. Система мультитенант, и логика работы определяется динамически. Хранилище у всех IList.

G>Сделать две функции не?

Так можно. Речь тут не о выборе оптимального решения, а о взгляде на роль ропозитория. Не оффтопьте, плиз.
Re[12]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 20.06.16 05:57
Оценка: -1 :)
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, another_coder, Вы писали:


IT>>>Дружище, скажи сколько ты лично написал систем, в которых неважно что именно есть источник данных (БД, IList, файл или web service)?

_>>Парочка найдется.

IT>А оно там реально было нужно или всё ради понтов?


Было сделано по необходимости. Смысла отвечать глубже нет. Если интересно заведите отдельную тему.
Re[13]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 20.06.16 09:39
Оценка:
Здравствуйте, another_coder, Вы писали:

_>>>Ну например, 1 выбирает по одному флагу, 2й выбирает по двум. Система мультитенант, и логика работы определяется динамически. Хранилище у всех IList.

G>>Сделать две функции не?
_>Так можно. Речь тут не о выборе оптимального решения, а о взгляде на роль ропозитория. Не оффтопьте, плиз.
Не просто о роли, а о том осмысленно ли его использовать. Ты предлагаешь лепить некоторый паттерн, там где достаточно двух фукнций.
В этом случае паттерн должен обладать явными преимуществами. Расскажи о них.
Re[12]: Маленький оффтоп
От: Sharov Россия  
Дата: 20.06.16 10:05
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Алан Кей и Бертран Мейер как раз в своих определениях ООП опираются на побочные эффекты при взаимодействии с объектами.


Извините, но резануло глаз. Алане Кей опр. ООП как возможность отправки сообщений объектам -- на это можно, конечно, натянуть побочные эффекты. Мейер в определении ООП вообще побочные эффекты не упоминает.
Мартин или кто-то подобный говорил, что побочные эффекты -- это то, ради чего происходит вычисление.
Кодом людям нужно помогать!
Re[14]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 20.06.16 10:19
Оценка:
Здравствуйте, gandjustas.
G>Ты предлагаешь...
Я не предлагаю. Внимательнее перечитай, плиз?
Re[13]: Маленький оффтоп
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 20.06.16 11:26
Оценка: 6 (1)
Здравствуйте, Sharov, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:


G>>Алан Кей и Бертран Мейер как раз в своих определениях ООП опираются на побочные эффекты при взаимодействии с объектами.


S>Извините, но резануло глаз. Алане Кей опр. ООП как возможность отправки сообщений объектам -- на это можно, конечно, натянуть побочные эффекты. Мейер в определении ООП вообще побочные эффекты не упоминает.


Кей говорил что объекты общаются посредством отправки сообщений.
Мейер прямо говорит, что объекты — данные+методы, а также инкапсуляция+полиморфизм+наследоание.

Какое из этих определений осмысленно, если мы исключаем побочные эффекты от методов или сообщений?


S>Мартин или кто-то подобный говорил, что побочные эффекты -- это то, ради чего происходит вычисление.

Я знаю, что побочные эффекты — то, ради чего происходит вычисление. Но это вовсе не означает, что надо при проектировании опираться на побочные эффекты. На практике как раз проще делать чистые функции, а побочные эффекты изолировать. Но это по ФП, а не ООП.
Re[14]: Маленький оффтоп
От: Sharov Россия  
Дата: 20.06.16 12:05
Оценка: :)
Здравствуйте, gandjustas, Вы писали:


S>>Извините, но резануло глаз. Алане Кей опр. ООП как возможность отправки сообщений объектам -- на это можно, конечно, натянуть побочные эффекты. Мейер в определении ООП вообще побочные эффекты не упоминает.


G>Кей говорил что объекты общаются посредством отправки сообщений.

G>Мейер прямо говорит, что объекты — данные+методы, а также инкапсуляция+полиморфизм+наследоание.

Не-а, он(Мейер) говорил про абстрактные типы данных, как важнейшую черту ООП. "данные+методы" сильно ниже.

G>Какое из этих определений осмысленно, если мы исключаем побочные эффекты от методов или сообщений?


Справедливое замечание, но вообще-то любое определение осмысленно, поскольку речь идет о другом способе размышлений над побочными эффектами, на другом уровне абстракции. Т.е. они где-то присутствуют (в методах, вестимо), но не это уже главное.

G>опираться на побочные эффекты.


Хорошо звучит, жизнеутверждающе.
Кодом людям нужно помогать!
Re[6]: Про путаницу с репозиториями и DAO
От: __SPIRIT__ Россия  
Дата: 21.06.16 17:34
Оценка:
Здравствуйте, IT, Вы писали:

IT>>>Потому что в последние лет 8 появились более продвинутые способы работы с БД.

B>>А можно более развернуто? Какие?

IT>Большие дяди в последнее время используют исключительно LINQ.


Дык, а если в базе больше сотни таблиц и несколько сотен хранимок, функций и другого барахла? ИМХО проще чутка подрехтовать интерфейс топикстартера и получить сверхтонкий "репозиторий" поверх линка, тупо для удобства работы.

    interface IRepository<T> where T : class
    {
        ListIQueryable<T> All { get; private set; };
    T Find(int entityId);
        T SaveOrUpdate(T entity);
        T Add(T entity);
        T Update(T entity);
        void Delete(T entity);
    }


В таком случае все эти SP ушли бы в соответствующие репозитории(для совместимости) и их можно было спокойно переделывать в нормальные шарповые функции, оставляя SP только там где реально нужно. Плюс будет место для общей логики.
Re[7]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 21.06.16 17:48
Оценка:
Здравствуйте, Baudolino, Вы писали:

B>Задача любого архитектурного паттерна — фрагментация сложности.


При условии, что архитектурные решения не вносят ещё больше проблем, чем решают.

B>Паттерны DAL отделяют сложность бизнес-логики от сложности работы с хранилищем данных с помощью разного вида интерфейсов.


Не совсем так. DAL изолирует работу с базой в терминах самой базы от остальной части приложения путём конвертации терминов приложения в термины БД и наоборот.

С этим гораздо лучше справляется LINQ.
Если нам не помогут, то мы тоже никого не пощадим.
Re[7]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 21.06.16 17:56
Оценка:
Здравствуйте, __SPIRIT__, Вы писали:

IT>>Большие дяди в последнее время используют исключительно LINQ.


__S>Дык, а если в базе больше сотни таблиц и несколько сотен хранимок, функций и другого барахла?


От хранимок нужно избавлятся при первом удобном случае. Логика в БД — это сегодня нонсенс. Что касается сотен всякого барахла, то не вижу никаких проблем. По таблицам генерируется модель данных приложения, которая кстати, может включать и функции и сохранённые процедуры.
Если нам не помогут, то мы тоже никого не пощадим.
Re[8]: Про путаницу с репозиториями и DAO
От: __SPIRIT__ Россия  
Дата: 21.06.16 18:03
Оценка:
Здравствуйте, IT, Вы писали:

IT>>>Большие дяди в последнее время используют исключительно LINQ.


__S>>Дык, а если в базе больше сотни таблиц и несколько сотен хранимок, функций и другого барахла?


IT>От хранимок нужно избавлятся при первом удобном случае.


Так то оно так, но процесс весьма растянут во времени...

IT>Логика в БД — это сегодня нонсенс. Что касается сотен всякого барахла, то не вижу никаких проблем. По таблицам генерируется модель данных приложения, которая кстати, может включать и функции и сохранённые процедуры.


да но если это все запихнуть в один дб контекст, там ничерта не найти будет. Намного удобнее, раскидать их по "репозиториям"
Re[9]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 21.06.16 19:01
Оценка: 4 (1) +2
Здравствуйте, __SPIRIT__, Вы писали:

IT>>От хранимок нужно избавлятся при первом удобном случае.

__S>Так то оно так, но процесс весьма растянут во времени...

Именно поэтому рулит linq2db, т.к. совершенно не навязывает своей архитектуры и можно всё начинать использовать в паралель.

__S>да но если это все запихнуть в один дб контекст, там ничерта не найти будет. Намного удобнее, раскидать их по "репозиториям"


Используйте схемы. linq2db для схем генерирует отдельные классы. Например, если таблица Issuer находится в схеме Instrument, то обращение к ней будет

db.Instrument.Issuers


Очень удобно.
Если нам не помогут, то мы тоже никого не пощадим.
Re[10]: Про путаницу с репозиториями и DAO
От: __SPIRIT__ Россия  
Дата: 21.06.16 19:12
Оценка:
Здравствуйте, IT, Вы писали:

IT>Используйте схемы. linq2db для схем генерирует отдельные классы. Например, если таблица Issuer находится в схеме Instrument, то обращение к ней будет


IT>
db.Instrument.Issuers


IT>Очень удобно.


в принципе вариант.
Re[8]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 22.06.16 08:06
Оценка:
Здравствуйте, IT.

Вопрос относительно терминологии. Как вы назовете объект, который будет связывать в единую конфигурацию такие вещи, как кеш, аудит доступка к данным, сам доступ к данным и что-нибудь еще? При этом я хочу менять способ доступа к данным, оставляя все остальное.
Re[4]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 22.06.16 08:16
Оценка:
Здравствуйте, Baudolino.

Я еще гляну на этот большой ответ. Но вот тут интересный вопрос у меня возник по той же теме. Можешь, плиз, глянуть? Интересно твое мнение здесь
Автор: another_coder
Дата: 22.06.16
Re[9]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 22.06.16 14:53
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Вопрос относительно терминологии. Как вы назовете объект, который будет связывать в единую конфигурацию такие вещи, как кеш, аудит доступка к данным, сам доступ к данным и что-нибудь еще?


Я бы такую штуку назвал помойкой.
Если нам не помогут, то мы тоже никого не пощадим.
Re[10]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 22.06.16 22:16
Оценка:
Здравствуйте, IT, Вы писали:

IT>Я бы такую штуку назвал помойкой.


Тем не менее, она есть в каждом проекте. Опишите, как вы все это объединяете в одном проекте? Если у вас был опыт, конечно.
Re[9]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 23.06.16 01:14
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Здравствуйте, IT.


_>Вопрос относительно терминологии. Как вы назовете объект, который будет связывать в единую конфигурацию такие вещи, как кеш, аудит доступка к данным, сам доступ к данным и что-нибудь еще? При этом я хочу менять способ доступа к данным, оставляя все остальное.


Не знаю как у тебя, а у меня эти функции выполняет Entity Framework. Он умеет логировать запросы, сохраняет в памяти запрошенные объекты и собственно получает данные.

Ты все-таки потрудись привести сценарий, когда и чего нужно менять в доступе к данным и какие для этого абстракции ты создашь. Иначе получается сферический конь.
Re[11]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 23.06.16 02:46
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Тем не менее, она есть в каждом проекте. Опишите, как вы все это объединяете в одном проекте? Если у вас был опыт, конечно.


Опыт если когда-то и был, то я уже не помню.
Если нам не помогут, то мы тоже никого не пощадим.
Re[10]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 23.06.16 06:08
Оценка:
Здравствуйте, gandjustas.

G>Не знаю как у тебя, а у меня эти функции выполняет Entity Framework. Он умеет логировать запросы, сохраняет в памяти запрошенные объекты и собственно получает данные.


G>Ты все-таки потрудись привести сценарий, когда и чего нужно менять в доступе к данным и какие для этого абстракции ты создашь. Иначе получается сферический конь.


Это хорошо, но если так получилось что проект писали те, кто не знает, как EF все это делает, или когда его еще не было, то, вполне возможно, они захотят ручками это все реализовать. Например, старинный проект, где все было написано на ADO.NET, а теперь решили переехать на EF. Сразу все переписать — нереально. Получается поэтапный переезд с выделением одного, другого. Появляется сначала, как бы, два репозитория, которые выполняют разные вещи:
    1) реализует всю кухню по созданию бизнесс объектов (начальные условия выборок, системные фильтры, если нет в хранимках и пр)
    2) реализует сам доступ к данным с помощью либо ADO.NET, либо EF.
Тут получается неоднозначная ситуация: два объекта, с разным назначением, описываются одним и тем же термином, по классическому определению: оба оперируют списками объектов. Но по факту у объектов разные цели.

Получилось мне описать ситуацию, чтобы она не была похожа на коня в вакууме?
Ну вот еще один пример о том же, но с другой стороны. Например, делался стартап. DAL написали по фаулеру, создали репозиторий, как в самом начале. С ростом проекта решили, что надо поддерживать разные базы (классика!), но BLL и DAL то уже написаны и это не переписать за неделю. <долгий рассказ о детялх>. Поэтому выделили в DAL провайдер данных из репозитория. По сути выделили один объект из другого, у которых практически идентичный CRUD интерфейс. Только прошлый стал как бы более умным и его дергают объекты BLL, а он дергает новый "репозиторий". И тут снова ситуация, когда новый человек в проекте может спросить: "нафига вам два репозитория? что за булшит?"
Можно предположить, что второй объект это не репозиторий, а DAO. А вот первый — репозиторий. Если так, то до момента изменения архитектуры никакого репозитория не было. Иначе, получается, что термин репозиторий сначала обозначал одно, потом другое. Собственно, этот момент мне и не нравится. Я считаю, что термин должен однозначно описывать то, что за ним скрывается. Например, при старте проекта так и называть: мы создаем DAO. Тогда акценты сразу правильно будут расставляться и говны в коде будет поменьше. Ведь никто не захочет в DAO запихивать еще и джойны и пр, как мне кажется.

Как у вас получается различать объекты в похожей ситуации?
Re[11]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 23.06.16 10:31
Оценка: 15 (1)
Здравствуйте, another_coder, Вы писали:

_>Здравствуйте, gandjustas.


G>>Не знаю как у тебя, а у меня эти функции выполняет Entity Framework. Он умеет логировать запросы, сохраняет в памяти запрошенные объекты и собственно получает данные.


G>>Ты все-таки потрудись привести сценарий, когда и чего нужно менять в доступе к данным и какие для этого абстракции ты создашь. Иначе получается сферический конь.


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

Я писал проект на делфи в 2005 году и делал свой orm, у меня там было и логирование запросов и identity map для кеша объектов. Там был реализован паттерн repository точно как описал фаулер. Но ни одного класса с таким именем не было.

Сегодня вообще нет необходимости писать полностью доступ к данным, а не использовать ef\linq2db\hibernate.


_>Например, старинный проект, где все было написано на ADO.NET, а теперь решили переехать на EF. Сразу все переписать — нереально. Получается поэтапный переезд с выделением одного, другого. Появляется сначала, как бы, два репозитория, которые выполняют разные вещи:

А с чего ты взял что репозитарий там был? Мания везде тулить репозитарии появилась относительно недавно. И уже сильно после того, как появились ORM.
Но даже если предположить, что бы, то зачем делать два — неясно совершенно.
1) Для начала надо заменить ручной маппинг и DbCommand на linq2db
2) Потом текстовые запросы\хранимки переписать на Linq
3) Потом прокинуть IQueryable выше по стеку вызовов и сократить количество методов в dal
Если что — я уже так делал на одном проекте.

_>Тут получается неоднозначная ситуация: два объекта, с разным назначением, описываются одним и тем же термином, по классическому определению: оба оперируют списками объектов. Но по факту у объектов разные цели.

Никаких двух не будет.

Я уже понял что наличие repository обосновывается только желанием делать repository. Никаких объективных причин пилить их нету.

_>Получилось мне описать ситуацию, чтобы она не была похожа на коня в вакууме?

Пока нет.

Так и не увидел необходимости иметь два разных метода доступа к одним и тем же данным. Так и не увидел потребности вручную реализовывать паттерн repository.


_>Ну вот еще один пример о том же, но с другой стороны. Например, делался стартап. DAL написали по фаулеру, создали репозиторий, как в самом начале. С ростом проекта решили, что надо поддерживать разные базы (классика!), но BLL и DAL то уже написаны и это не переписать за неделю. <долгий рассказ о детялх>. Поэтому выделили в DAL провайдер данных из репозитория. По сути выделили один объект из другого, у которых практически идентичный CRUD интерфейс. Только прошлый стал как бы более умным и его дергают объекты BLL, а он дергает новый "репозиторий". И тут снова ситуация, когда новый человек в проекте может спросить: "нафига вам два репозитория? что за булшит?"

Процитирую себя

Я уже понял что наличие repository обосновывается только желанием делать repository. Никаких объективных причин пилить их нету.


_>Как у вас получается различать объекты в похожей ситуации?

Никак, я сотру нафиг это говно. код доступа к данным, у которого в баз 10 табличек (стартап же) переписывается на ORM за полдня.
Re[9]: Про путаницу с репозиториями и DAO
От: Baudolino  
Дата: 23.06.16 11:25
Оценка: 15 (1)
Здравствуйте, another_coder, Вы писали:

_>Здравствуйте, IT.


_>Вопрос относительно терминологии. Как вы назовете объект, который будет связывать в единую конфигурацию такие вещи, как кеш, аудит доступка к данным, сам доступ к данным и что-нибудь еще? При этом я хочу менять способ доступа к данным, оставляя все остальное.

Связыванием занимается обычно контейнер DI, на основе конфигурации. Конкретные задачи кэширования и аудита можно решать декларативно через AOP, тогда у вас реализация доступа к данным от них зависеть не будет, и ее будет проще изменить. Пример изменения: почитав восторженные отзывы технохипстеров, попробовали какую-нибудь MongoDB, не понравилось, переехали в старый добрый Postgres. Меняется только конфигурация DI и реализация работы с хранилищем в DAO (ODM -> ORM). DI как навешивал, используя паттерн Proxy, журналирование и кэширование результатов вызовов методов DAO, так и навешивает — ему пофиг, что там внутри. Потом, конечно, надо будет посмотреть, как система ведёт себя в разных профилях нагрузки, но там доработки будут точечными.
Re[4]: Про путаницу с репозиториями и DAO
От: Gattaka Россия  
Дата: 24.06.16 05:33
Оценка:
Здравствуйте, IT, Вы писали:

IT>У нас класс так называется только потому что как-то не придумалось другого названия. Но с предложенным интерфейсом репозитория он не имеет ничего общего. Во-первых, у нас вообще нет никакого интерфейса, потому что он нафиг не нужен. Во-вторых, основная задача нашего класса — возможность тестирования процессов, которые его используют без чтения данных из базы. Т.е. это ближе к DI, чем к паттерну Repository. А здесь репозиторий нужен как часть или даже замена DAO, такой классический пережиток тяжелого прошлого.


А не могли бы вы приблизительную сигнатуру вашего класса показать? Так чтобы точно понимать...
Re[8]: Про путаницу с репозиториями и DAO
От: Gattaka Россия  
Дата: 24.06.16 05:37
Оценка:
Здравствуйте, IT, Вы писали:

IT>От хранимок нужно избавлятся при первом удобном случае. Логика в БД — это сегодня нонсенс.

Красивый сказка, которая не выдерживает проверку практикой. При большее детальном рассмотрении запросов, создаваемых ORM выясняется что сильно не эффективны. Нужно избавляться от кодогенратов во всех их проявленяих в том числе и от ORM.
Re[9]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 24.06.16 10:59
Оценка:
Здравствуйте, Gattaka, Вы писали:

G>Здравствуйте, IT, Вы писали:


IT>>От хранимок нужно избавлятся при первом удобном случае. Логика в БД — это сегодня нонсенс.

G>Красивый сказка, которая не выдерживает проверку практикой. При большее детальном рассмотрении запросов, создаваемых ORM выясняется что сильно не эффективны. Нужно избавляться от кодогенратов во всех их проявленяих в том числе и от ORM.
А можно пример? На моей практике люди сильно хуже пишут запросы в sql, чем на linq. Тем более последний отсекает ряд банальных ошибок, из-за которых запросы могут тормозить.
Re[10]: Про путаницу с репозиториями и DAO
От: Gattaka Россия  
Дата: 24.06.16 11:25
Оценка:
Здравствуйте, gandjustas, Вы писали:

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

G>А можно пример? На моей практике люди сильно хуже пишут запросы в sql, чем на linq. Тем более последний отсекает ряд банальных ошибок, из-за которых запросы могут тормозить.

Привести пример это целое дело. Попробую кратко.
ORM — NHibernate. Есть таблица узлов, они по дискриминатору разделены на два класса. Между узлами есть связи, что хранятся в отдельной таблице. Я выбираю такие связи у которых с одной стороны узел определенного типа, а также узлы с определенным свойством. У ORM получается 4 join, хотя можно было бы обойтись 2-мя с and в условии. Пришлось изголятся и использовать устаревший синтаксис. Там получилось нормальное количество join, но при этом вытягиваются данные не только по связям, но и по узлам... Вот такая альтернатива.
Re[11]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 24.06.16 13:57
Оценка:
Здравствуйте, Gattaka, Вы писали:

G>Здравствуйте, gandjustas, Вы писали:


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

G>>А можно пример? На моей практике люди сильно хуже пишут запросы в sql, чем на linq. Тем более последний отсекает ряд банальных ошибок, из-за которых запросы могут тормозить.

G>Привести пример это целое дело. Попробую кратко.

G>ORM — NHibernate. Есть таблица узлов, они по дискриминатору разделены на два класса. Между узлами есть связи, что хранятся в отдельной таблице. Я выбираю такие связи у которых с одной стороны узел определенного типа, а также узлы с определенным свойством. У ORM получается 4 join, хотя можно было бы обойтись 2-мя с and в условии. Пришлось изголятся и использовать устаревший синтаксис. Там получилось нормальное количество join, но при этом вытягиваются данные не только по связям, но и по узлам... Вот такая альтернатива.

Приведите код, так не понятно о чем речь. Скорее всего вы слишком много игрались моделированием в NHibernate, поэтому такой результат.
Re[10]: Про путаницу с репозиториями и DAO
От: Sharov Россия  
Дата: 24.06.16 14:14
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Gattaka, Вы писали:


G>>Здравствуйте, IT, Вы писали:


IT>>>От хранимок нужно избавлятся при первом удобном случае. Логика в БД — это сегодня нонсенс.

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

G>А можно пример? На моей практике люди сильно хуже пишут запросы в sql, чем на linq. Тем более последний отсекает ряд банальных ошибок, из-за которых запросы могут тормозить.


По-моему наоборт -- на sql можно четко сформулировать запрос без всяческих подпрыжек, вот с linq недавно огрбеб -- http://docs.telerik.com/data-access/developers-guide/profiling-and-tuning/profiler-and-tuning-advisor/data-access-profiler-n-plus-one-problem
Кодом людям нужно помогать!
Re[11]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 24.06.16 14:45
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:


G>>Здравствуйте, Gattaka, Вы писали:


G>>>Здравствуйте, IT, Вы писали:


IT>>>>От хранимок нужно избавлятся при первом удобном случае. Логика в БД — это сегодня нонсенс.

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

G>>А можно пример? На моей практике люди сильно хуже пишут запросы в sql, чем на linq. Тем более последний отсекает ряд банальных ошибок, из-за которых запросы могут тормозить.


S>По-моему наоборт -- на sql можно четко сформулировать запрос без всяческих подпрыжек

Нельзя, если запрос параметризованный.

типичный случай для приложения — отображение списка с фильтрами — 3 фильтра+постраничная разбивка. Фильтры по умолчанию не фильтруют ничего и могут быть выбраны в любой комбинации. Linq сгенерирует 8 или 16 разных запросов для различных сочетаний фильтров и страниц.
На SQL будет написан один запрос с тремя условиями примерно такого вида:
where 1=1
and (t.Field1 = @p1 or @p1 is null)
and (t.Field2 = @p2 or @p2 is null)
and (t.Field3 = @p3 or @p3 is null)

План запроса в этом случае будет далек от оптимального и подвержен parameter sniffing problem.

Тут подробно http://blog.gandjustas.ru/2014/09/23/asp.net-linq-ef-sql-server-performance/

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

S>вот с linq недавно огрбеб -- http://docs.telerik.com/data-access/developers-guide/profiling-and-tuning/profiler-and-tuning-advisor/data-access-profiler-n-plus-one-problem

lazy load надо отключать всегда и лучше не пользоваться ORM где он включен. В EF он отключен по-умолчанию для code-first, а в linq2db его вовсе нет.
Re[9]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 24.06.16 15:16
Оценка:
Здравствуйте, Gattaka, Вы писали:

IT>>От хранимок нужно избавлятся при первом удобном случае. Логика в БД — это сегодня нонсенс.

G>Красивый сказка, которая не выдерживает проверку практикой.

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

G>При большее детальном рассмотрении запросов, создаваемых ORM выясняется что сильно не эффективны. Нужно избавляться от кодогенратов во всех их проявленяих в том числе и от ORM.


Это вы, батенька, готовить не умеете. Выкинь все свои ORM на помойку и возьми ту, которая генерирует только то, что ей сказано.
Если нам не помогут, то мы тоже никого не пощадим.
Re[5]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 24.06.16 15:23
Оценка:
Здравствуйте, Gattaka, Вы писали:

G>А не могли бы вы приблизительную сигнатуру вашего класса показать? Так чтобы точно понимать...


Не мог бы. Там и сигнатуры особенной нет. Это просто класс без всяких интерфейсов и паттернов, в котором куча коллекций с данными. Методы для инициализации и ещё какой-то фигни по мелочи. Просто большой контейнер. Это его основная задача.
Если нам не помогут, то мы тоже никого не пощадим.
Re[12]: Про путаницу с репозиториями и DAO
От: Sharov Россия  
Дата: 24.06.16 15:41
Оценка:
Здравствуйте, gandjustas, Вы писали:



G>типичный случай для приложения — отображение списка с фильтрами — 3 фильтра+постраничная разбивка. Фильтры по умолчанию не фильтруют ничего и могут быть выбраны в любой комбинации. Linq сгенерирует 8 или 16 разных запросов для различных сочетаний фильтров и страниц.

G>На SQL будет написан один запрос с тремя условиями примерно такого вида:
G>
G>where 1=1
G>and (t.Field1 = @p1 or @p1 is null)
G>and (t.Field2 = @p2 or @p2 is null)
G>and (t.Field3 = @p3 or @p3 is null)
G>

G>План запроса в этом случае будет далек от оптимального и подвержен parameter sniffing problem.

G>Тут подробно http://blog.gandjustas.ru/2014/09/23/asp.net-linq-ef-sql-server-performance/


G>В реальности еще добавится сортировка и разные проекции в запросах с одинаковыми предикатами. Общее количество возможных запросов перевалит за 50. При этом такой запрос будет на главной странице и должен работать максимально быстро. Linq тут очень поможет, а в SQL это будет АД.


Не верю. Во-первых, надо смотреть на план linq запросов, которых аж целых 8 вместо 1. Во-вторых, спорно ибо нормальный спец. в рбд. напишет может написать запрос явно не хуже чем чем linq сгенеренный.


S>>вот с linq недавно огрбеб -- http://docs.telerik.com/data-access/developers-guide/profiling-and-tuning/profiler-and-tuning-advisor/data-access-profiler-n-plus-one-problem

G>lazy load надо отключать всегда и лучше не пользоваться ORM где он включен. В EF он отключен по-умолчанию для code-first, а в linq2db его вовсе нет.

да-да-да, и сразу подгружать всю базу данных со всеми join'ами, которые не нужны.
Кодом людям нужно помогать!
Re[13]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 24.06.16 19:17
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:




G>>типичный случай для приложения — отображение списка с фильтрами — 3 фильтра+постраничная разбивка. Фильтры по умолчанию не фильтруют ничего и могут быть выбраны в любой комбинации. Linq сгенерирует 8 или 16 разных запросов для различных сочетаний фильтров и страниц.


S>Не верю.

Можешь проверить


S>Во-первых, надо смотреть на план linq запросов, которых аж целых 8 вместо 1.

Тогда идея с рукопашными запросами покажется совсем глупой. Потому что у каждого запроса из 8 будет свой оптимальный план.
А рукопашный запрос вида
where 1=1
and (t.Field1 = @p1 or @p1 is null)
and (t.Field2 = @p2 or @p2 is null)
and (t.Field3 = @p3 or @p3 is null)

Будет иметь план, который оптимален только для одного из 8 сочетаний параметров. Чем больше параметров тем хуже, никакие индексы не помогут. Поможет только option (recompile), но добавит затраты на компиляцию запроса каждый раз.

Погугли советы использовать option (recompile), почти везде вместо него можно использовать генератор запросов.


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

Нормальный спец напишет запрос явно не хуже, чем linq. ОДИН запрос.
Поддерживать самописный генератор или несколько десятков однотипных запросов даже "нормальный спец" не сможет.


S>>>вот с linq недавно огрбеб -- http://docs.telerik.com/data-access/developers-guide/profiling-and-tuning/profiler-and-tuning-advisor/data-access-profiler-n-plus-one-problem

G>>lazy load надо отключать всегда и лучше не пользоваться ORM где он включен. В EF он отключен по-умолчанию для code-first, а в linq2db его вовсе нет.
S>да-да-да, и сразу подгружать всю базу данных со всеми join'ами, которые не нужны.
Ты о чем сейчас?
Re[14]: Про путаницу с репозиториями и DAO
От: Sharov Россия  
Дата: 24.06.16 21:19
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Sharov, Вы писали:


S>>Здравствуйте, gandjustas, Вы писали:




G>>>типичный случай для приложения — отображение списка с фильтрами — 3 фильтра+постраничная разбивка. Фильтры по умолчанию не фильтруют ничего и могут быть выбраны в любой комбинации. Linq сгенерирует 8 или 16 разных запросов для различных сочетаний фильтров и страниц.


S>>Не верю.

G>Можешь проверить


S>>Во-первых, надо смотреть на план linq запросов, которых аж целых 8 вместо 1.

G>Тогда идея с рукопашными запросами покажется совсем глупой. Потому что у каждого запроса из 8 будет свой оптимальный план.
G>А рукопашный запрос вида
G>
G>where 1=1
G>and (t.Field1 = @p1 or @p1 is null)
G>and (t.Field2 = @p2 or @p2 is null)
G>and (t.Field3 = @p3 or @p3 is null)
G>

G>Будет иметь план, который оптимален только для одного из 8 сочетаний параметров. Чем больше параметров тем хуже, никакие индексы не помогут. Поможет только option (recompile), но добавит затраты на компиляцию запроса каждый раз.

G>Погугли советы использовать option (recompile), почти везде вместо него можно использовать генератор запросов.


А linq то как поможет?

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

G>Нормальный спец напишет запрос явно не хуже, чем linq. ОДИН запрос.
G>Поддерживать самописный генератор или несколько десятков однотипных запросов даже "нормальный спец" не сможет.

Вы смеетесь ??? Т.е. если Вы возьмете человека на фул-тайм с задачей написания sql запросов (специалиста), он больше одного запроса написать не сможет? Не считая того, что вообще говоря это должно быть компетенцией разработчика, отв. за соотв. таблицы (сущности).

S>>>>вот с linq недавно огрбеб -- http://docs.telerik.com/data-access/developers-guide/profiling-and-tuning/profiler-and-tuning-advisor/data-access-profiler-n-plus-one-problem

G>>>lazy load надо отключать всегда и лучше не пользоваться ORM где он включен. В EF он отключен по-умолчанию для code-first, а в linq2db его вовсе нет.
S>>да-да-да, и сразу подгружать всю базу данных со всеми join'ами, которые не нужны.
G>Ты о чем сейчас?

Ну как-бы у openaccess telerik'овского допустим есть eager loading и laze loading (как и везде). При подгрузке родительских сущностей все дочерние сущности соотв родительской тоже подгрузятся, даже если они не нужны.
Кодом людям нужно помогать!
Re[14]: Про путаницу с репозиториями и DAO
От: Gattaka Россия  
Дата: 25.06.16 04:32
Оценка: :)
Здравствуйте, gandjustas, Вы писали:

S>>Во-первых, надо смотреть на план linq запросов, которых аж целых 8 вместо 1.

G>Тогда идея с рукопашными запросами покажется совсем глупой. Потому что у каждого запроса из 8 будет свой оптимальный план.
G>А рукопашный запрос вида
G>
G>where 1=1
G>and (t.Field1 = @p1 or @p1 is null)
G>and (t.Field2 = @p2 or @p2 is null)
G>and (t.Field3 = @p3 or @p3 is null)
G>

G>Будет иметь план, который оптимален только для одного из 8 сочетаний параметров. Чем больше параметров тем хуже, никакие индексы не помогут. Поможет только option (recompile), но добавит затраты на компиляцию запроса каждый раз.

G>Погугли советы использовать option (recompile), почти везде вместо него можно использовать генератор запросов.

Во первых это не такие уж большие затраты, потом еще не известно что более затратно перекомпилировать один запрос. Или хранить N-атцать планов запросов и тоже их перекомпилировать при обновлении статистики, например.
И давайте от противного. А может ли ваш linq добавить этот option (recompile)? Нет не может — будет кучу запросов генерить
По кейсу что вы описали: На SQL вы можете разбить запрос на 2 подзапроса, например с помощью табличных выражений. Можете добавить view, затем ее проиндексировать. Все зависит от конкретной системы и т.п. Точно так же никто не отменял динамический sql sp_exec... Вы можете сформировать строчку запроса, а потом ее выполнить. Прямо как linq.
Re[8]: Про путаницу с репозиториями и DAO
От: Baudolino  
Дата: 25.06.16 07:52
Оценка:
Здравствуйте, IT, Вы писали:

B>>Задача любого архитектурного паттерна — фрагментация сложности.

IT>При условии, что архитектурные решения не вносят ещё больше проблем, чем решают.
Разумеется.

B>>Паттерны DAL отделяют сложность бизнес-логики от сложности работы с хранилищем данных с помощью разного вида интерфейсов.

IT>Не совсем так. DAL изолирует работу с базой в терминах самой базы от остальной части приложения путём конвертации терминов приложения в термины БД и наоборот.
Ну в общем, да. Можно еще короче сказать: абстрагируют работу с хранилищем, причем не обязательно БД: например, вы обрабатываете какие-то документы и изначально храните их в блобах в БД, потом переносите в облачное хранилище типа Amazon S3 — если оптимизация за счет отдачи прямых ссылок на хранилище невозможна (например, потому что клиент в интернете, а хранилище в VPN) или не требуется (например, потому что клиенту нужны результаты обработки, а не сами документы), то интерфейс DAL и бизнес-логику можно при этом не менять.
Re[15]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 25.06.16 12:17
Оценка:
Здравствуйте, Sharov, Вы писали:

G>>А рукопашный запрос вида

G>>
G>>where 1=1
G>>and (t.Field1 = @p1 or @p1 is null)
G>>and (t.Field2 = @p2 or @p2 is null)
G>>and (t.Field3 = @p3 or @p3 is null)
G>>

G>>Будет иметь план, который оптимален только для одного из 8 сочетаний параметров. Чем больше параметров тем хуже, никакие индексы не помогут. Поможет только option (recompile), но добавит затраты на компиляцию запроса каждый раз.

S>А linq то как поможет?


C linq будет написан такой код:
if(p1 != null) q = q.Where(t => t.Field1 = p1);
if(p2 != null) q = q.Where(t => t.Field2 = p2);
if(p3 != null) q = q.Where(t => t.Field3 = p3);

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


S>Вы смеетесь ??? Т.е. если Вы возьмете человека на фул-тайм с задачей написания sql запросов (специалиста), он больше одного запроса написать не сможет? Не считая того, что вообще говоря это должно быть компетенцией разработчика, отв. за соотв. таблицы (сущности).

Конечно сможет. И два сможет, и десять и может даже 50. Для статистики — у меня в очень простом приложении с 5 таблицами в базе получилось суммарно 60 разных запросов. А нанимать спеца отдельно по БД для базы менее чем два десятка таблиц никто не будет. Вот и посчитай какое количество запросов ему надо будет поддерживать.


S>Ну как-бы у openaccess telerik'овского допустим есть eager loading и laze loading (как и везде). При подгрузке родительских сущностей все дочерние сущности соотв родительской тоже подгрузятся, даже если они не нужны.

Выкини это говно и возьми linq2db или ef. Они грузят ровно то, что скажешь.
Re[15]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 25.06.16 12:48
Оценка:
Здравствуйте, Gattaka, Вы писали:

G>>Погугли советы использовать option (recompile), почти везде вместо него можно использовать генератор запросов.

G>Во первых это не такие уж большие затраты, потом еще не известно что более затратно перекомпилировать один запрос. Или хранить N-атцать планов запросов и тоже их перекомпилировать при обновлении статистики, например.
Конечно перекомпиляция на каждый вызов более затрата если вызовов больше чем N, где N — количество запросов сгенерированных linq. N обычно не превышает 15. То есть если у тебя запрос вызывается 15 и более раз, то выгоднее использовать linq (или другой генератор), а не option recompile. Хранение планов не стоит ничего, потому что планов всегда ограниченное количество и надо сервак подобрать, чтобы plan cache не вытеснялся.

G>И давайте от противного. А может ли ваш linq добавить этот option (recompile)? Нет не может — будет кучу запросов генерить

Правильно. Генерить кучу запросов выгоднее. Хотя наверное linq2db может, я не проверял.

G>По кейсу что вы описали: На SQL вы можете разбить запрос на 2 подзапроса, например с помощью табличных выражений.

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

G>Можете добавить view, затем ее проиндексировать.

Индексированные view точно также можно и в linq использовать.

G>Все зависит от конкретной системы и т.п. Точно так же никто не отменял динамический sql sp_exec... Вы можете сформировать строчку запроса, а потом ее выполнить. Прямо как linq.

Да-да, давайте клеить строки руками, а не пользоваться гарантиями компилятора.
Опытный разработчик sql склейку строк делает один раз, а потом не связывается с такой архитектурой.
Re[16]: Про путаницу с репозиториями и DAO
От: Gattaka Россия  
Дата: 25.06.16 13:05
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

G>>И давайте от противного. А может ли ваш linq добавить этот option (recompile)? Нет не может — будет кучу запросов генерить

G>Правильно. Генерить кучу запросов выгоднее. Хотя наверное linq2db может, я не проверял.
Выгоднее это да, но насколько? План запроса компилируется пару милисекунд, можно для каждого конкретного посмотреть в плане. И на чем мы экономим? Если сам запрос выполняется пару секунд. Ну... если хорошо скомпилировался. Если плохо — полчаса. Здесь баланс нужно соблюдать. На самом деле что ORM хорошо делает, это простейший CRUD, самый простейший. И сценарий что вы описали. Но это по факту Read.

G>>По кейсу что вы описали: На SQL вы можете разбить запрос на 2 подзапроса, например с помощью табличных выражений.

G>Все равно план запроса будет один, со всеми вытекающими.

G>>Можете добавить view, затем ее проиндексировать.

G>Индексированные view точно также можно и в linq использовать.
Ну и что это за уровень абстракции в таком случае? Это всего лишь способ писать SQL на C#. Извращение...
Re: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 25.06.16 14:11
Оценка: +1
Здравствуйте, another_coder, Вы писали:

_>Удивляют люди, которые считают, что правильный репозиторий должен выголядить как этот интерфейс

Я сам был таким и писал такой код, однако через некоторое время он переставал писаться и всё становилось очень криво. Когда код пишется с трудом, для меня это знак что что-то я делаю не так.
_>Это ни что иное, как реализация DAO паттерна. Сам же репозиторий может совершенно не знать о механике и деталях хранения данных (база, файлы, сервис). На маленьких проектах разницу не так сильно видно. А на больших, когда помимо самих запросов, еще необходимо прикрутить кеширование, логирование, фильтрацию по служебным полям, вдруг оказывается, что репозиторий уже совсем не то же, что DAO.
_>Отсюда и вопросы у новичков, типа, должен ли репозиторий иметь методы GetSomethingByUser, GetUserTickets или просто CRUD не зная про другие репозитории и типы.
Это шлюз таблицы, а не репозиторий. http://martinfowler.com/eaaCatalog/tableDataGateway.html

_>А современные фреймворки еще сильнее путают разрабов, реализуя весь Data Access слой. И те, что DAO называли репозиториями начинают думать, что репозитории не нужны, и можно Linq-кодом прошить всю бизнес логику. Даже картинки у Фаулера и на MSDN способствуют тому, что репозиторий воспринимается, главным образом, как провайдер данных, т.е. абстракция над RDBMS, хотя его роль более значимая при проектировании. Возможно, я начитался DDD книжек.

Фаулер отличает репозиторий и шлюз таблицы — у него это разные концепции. Проблема примеров — им надо показать всё и сразу, поэтому они очень сильно упрощают код. Код из примеров нельзя воспринимать как руководство к действию.

_>Не кажется ли вам, что пора идею репозитория несколько подправить, чтобы когда один говорил другому этот термин, то оба понимали что-то одно?

Репозиторий — это просто коллекция объектов из предметной области. Когда люди заводят речь о репозиториях, они имеют в виду совсем другое — как объекты предметной области будут существовать между запусками программы. Абсолютно две разных проблемы — управление множеством объектов и управление жизнью объектов. Надо задавать наводящие вопросы и уточнять термины.
Re[6]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 25.06.16 14:46
Оценка: -1
Здравствуйте, Sinix, Вы писали:

S>Кэп: код здорового человека выглядит примерно так:

S>
S>[Transactional]
S>void SetRecordData(string code, string data)
S>{
S>  var existing = Context.FirstOrDefault<MyRecord>(r => r.Code == code);
S>  if (data == null)
S>  {
S>    existing?.Delete();
S>  }
S>  else
S>  {
S>    if (existing == null)
S>    {
S>       existing = Context.NewObject<MyRecord>(r => r.Code = code);
S>    }

S>    existing.Data = data;
S>  }  
S>}
S>


S>Т.е. ноль технических деталей, вся магия под капотом.


Вся магия наружу, эти два вызова подряд сделают две разных операции. Один вызов удалит запись, другой создаст, или наоборот.
SetRecordData("abc", null);
SetRecordData("abc", null);


Даже непонятно, что оно вообще делать должно. Upsert?
Re[7]: Про путаницу с репозиториями и DAO
От: Sinix  
Дата: 25.06.16 14:57
Оценка: +1
Здравствуйте, Vladek, Вы писали:
V>Вся магия наружу, эти два вызова подряд сделают две разных операции. Один вызов удалит запись, другой создаст, или наоборот.

Эй, это как бы профильный форум Тут ответы можно читать, а не пропускать и сразу переходить к "а вот почему ты неправ"

1. С кодом всё ок. Хинт: if (data == null) ... else ...

2.

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


Вопросы?
Re[17]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 25.06.16 15:11
Оценка:
Здравствуйте, Gattaka, Вы писали:

G>Здравствуйте, gandjustas, Вы писали:


G>>>И давайте от противного. А может ли ваш linq добавить этот option (recompile)? Нет не может — будет кучу запросов генерить

G>>Правильно. Генерить кучу запросов выгоднее. Хотя наверное linq2db может, я не проверял.
G>Выгоднее это да, но насколько? План запроса компилируется пару милисекунд, можно для каждого конкретного посмотреть в плане. И на чем мы экономим? Если сам запрос выполняется пару секунд. Ну... если хорошо скомпилировался. Если плохо — полчаса. Здесь баланс нужно соблюдать. На самом деле что ORM хорошо делает, это простейший CRUD, самый простейший. И сценарий что вы описали. Но это по факту Read.
Баланс чего? Тут нет tradeoff, запросы, сгенерированные Linq не требуют перекомпиляции, а рукопашные требуют.

G>Ну и что это за уровень абстракции в таком случае? Это всего лишь способ писать SQL на C#. Извращение...

Ну и пусть извращение, какая разница? Я готов заниматься любыми извращениями если сокращает затраты.
Re[18]: Про путаницу с репозиториями и DAO
От: Gattaka Россия  
Дата: 25.06.16 18:04
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Gattaka, Вы писали:


G>>Здравствуйте, gandjustas, Вы писали:


G>>>>И давайте от противного. А может ли ваш linq добавить этот option (recompile)? Нет не может — будет кучу запросов генерить

G>>>Правильно. Генерить кучу запросов выгоднее. Хотя наверное linq2db может, я не проверял.
G>>Выгоднее это да, но насколько? План запроса компилируется пару милисекунд, можно для каждого конкретного посмотреть в плане. И на чем мы экономим? Если сам запрос выполняется пару секунд. Ну... если хорошо скомпилировался. Если плохо — полчаса. Здесь баланс нужно соблюдать. На самом деле что ORM хорошо делает, это простейший CRUD, самый простейший. И сценарий что вы описали. Но это по факту Read.
G>Баланс чего? Тут нет tradeoff, запросы, сгенерированные Linq не требуют перекомпиляции, а рукопашные требуют.

G>>Ну и что это за уровень абстракции в таком случае? Это всего лишь способ писать SQL на C#. Извращение...

G>Ну и пусть извращение, какая разница? Я готов заниматься любыми извращениями если сокращает затраты.


Вобще то это подмена понятий. Приложение ведь не состоит из одних фильтраций, это даже не 0,0001% от всего разнообразия запросов. А вот как раз с ними и возникают проблемы.
Я уже описывал что за проблемы — при работе с дискриминаторами в 2 раза больше джойнов вместо условий в самих джойнах. И что тогда будет толку от кучи конкретных запросов, которые не перекомпилируются, но содержат лишние джойны?
Если посмотреть на EF там постоянно запросы вида select ... from (select ... запрос...) — почему-то селект из селекта, очень странно.
Вспомнилось про NH недавнее чудо — при обновлении объекта происходит не обновление конкретного поля, а перезапись всех полей строчки. (Как у EF сейчас с этим?). Это можно разрулить с помощью аттрибута на свойстве сущности, но опять же надо везде ставить, что несколько портит внешний вид самой сущности.
Отредактировано 25.06.2016 18:20 Gattaka . Предыдущая версия .
Re[19]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 25.06.16 20:59
Оценка: +3
Здравствуйте, Gattaka, Вы писали:


G>Вобще то это подмена понятий. Приложение ведь не состоит из одних фильтраций, это даже не 0,0001% от всего разнообразия запросов. А вот как раз с ними и возникают проблемы.

Типичное веб-приложение а 98% состоит из выборок, а их удобнее делать в генераторе запросов типа linq, а не в ХП.

G>Я уже описывал что за проблемы — при работе с дискриминаторами в 2 раза больше джойнов вместо условий в самих джойнах. И что тогда будет толку от кучи конкретных запросов, которые не перекомпилируются, но содержат лишние джойны?

Это проблема конкретно NHibernate и модели, а не ORM в принципе. Выбирай нормальные средства.

G>Если посмотреть на EF там постоянно запросы вида select ... from (select ... запрос...) — почему-то селект из селекта, очень странно.

derived table прекрасно оптимизируются, смотри планы запросов.

G>Вспомнилось про NH недавнее чудо — при обновлении объекта происходит не обновление конкретного поля, а перезапись всех полей строчки. (Как у EF сейчас с этим?). Это можно разрулить с помощью аттрибута на свойстве сущности, но опять же надо везде ставить, что несколько портит внешний вид самой сущности.

хз. а на что вообще это влияет?
Re[20]: Про путаницу с репозиториями и DAO
От: Gattaka Россия  
Дата: 26.06.16 04:23
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Gattaka, Вы писали:



G>>Вобще то это подмена понятий. Приложение ведь не состоит из одних фильтраций, это даже не 0,0001% от всего разнообразия запросов. А вот как раз с ними и возникают проблемы.

G>Типичное веб-приложение а 98% состоит из выборок, а их удобнее делать в генераторе запросов типа linq, а не в ХП.
Ну тифичное веб-приложение это да.

G>>Вспомнилось про NH недавнее чудо — при обновлении объекта происходит не обновление конкретного поля, а перезапись всех полей строчки. (Как у EF сейчас с этим?). Это можно разрулить с помощью аттрибута на свойстве сущности, но опять же надо везде ставить, что несколько портит внешний вид самой сущности.

G>хз. а на что вообще это влияет?
Ну вобщето при обновлении лучше обновлять только то, что вы изменили. Как и с выборкой. Как стреляет. Положим вы в приложении обновляете какое-то свойство, но обнаруживаете что это происходит как-то медленно. Капаете и обнаруживаете, что NH обновляет все свойства, в том числе которое фигурирует в индексированной вьюшке и развернуто на 100500 строчек в этой вьюшке. Теперь SQL Server нужно пройтись записям этой вьюшки и везде заменить то же самое значение на то же самой. Как-то так...
Re[21]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 26.06.16 09:06
Оценка:
Здравствуйте, Gattaka, Вы писали:

G>>>Вспомнилось про NH недавнее чудо — при обновлении объекта происходит не обновление конкретного поля, а перезапись всех полей строчки. (Как у EF сейчас с этим?). Это можно разрулить с помощью аттрибута на свойстве сущности, но опять же надо везде ставить, что несколько портит внешний вид самой сущности.

G>>хз. а на что вообще это влияет?
G>Ну вобщето при обновлении лучше обновлять только то, что вы изменили.
Кому лучше? Ты хоть представляешь как работает обновление на уровне страниц?

G>Как и с выборкой.

Не как с выборкой

G>Положим вы в приложении обновляете какое-то свойство, но обнаруживаете что это происходит как-то медленно. Капаете и обнаруживаете, что NH обновляет все свойства, в том числе которое фигурирует в индексированной вьюшке и развернуто на 100500 строчек в этой вьюшке. Теперь SQL Server нужно пройтись записям этой вьюшки и везде заменить то же самое значение на то же самой. Как-то так...

Уверен, что отсутствие поля в update не вызывает пересчет вьюшки? Я что-то очень сомневаюсь.
Re[22]: Про путаницу с репозиториями и DAO
От: Gattaka Россия  
Дата: 26.06.16 12:53
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>>Ну вобщето при обновлении лучше обновлять только то, что вы изменили.

G>Кому лучше? Ты хоть представляешь как работает обновление на уровне страниц?
Ну хотябы потому что трафик за счет самого запроса больше. Там ведь у сущности могут быть и с мегатекстом и бинарной гадостью всякой. Да даже если просто поля... В любом случае лучше меньше трафика. Насчет апдейта на самом сервере я соглашусь что в большенстве случаев это почти то же самое. Но если у вас фрагментация страниц большая или есть расщипленные страницы? Вобщем ИМХО очевидно, что лучше минимизировать объем обнавления данных — это хороший стиль. Как зубы чистить. С какого бабуя я должен то же самое на то же самое перетирать? Только из-за того что у разработчиков ОРМ — лениво писать грамотный код.

G>>Как и с выборкой.

G>Не как с выборкой

G>>Положим вы в приложении обновляете какое-то свойство, но обнаруживаете что это происходит как-то медленно. Капаете и обнаруживаете, что NH обновляет все свойства, в том числе которое фигурирует в индексированной вьюшке и развернуто на 100500 строчек в этой вьюшке. Теперь SQL Server нужно пройтись записям этой вьюшки и везде заменить то же самое значение на то же самой. Как-то так...

G>Уверен, что отсутствие поля в update не вызывает пересчет вьюшки? Я что-то очень сомневаюсь.
Проверяли. А что оно будет обновлятс? Поле, которое фигурирует в вьюшке не меняется. А в случае с перезаписью то же самое на то же самое — формально меняется...
Re[23]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 26.06.16 17:22
Оценка: 1 (1) +1
Здравствуйте, Gattaka, Вы писали:

G>Здравствуйте, gandjustas, Вы писали:


G>Только из-за того что у разработчиков ОРМ — лениво писать грамотный код.

Это у разработчиков NHibernate, в EF проверил — в Update уходят только обновленные поля. В linq2db уверен то же самое.
Re[9]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 26.06.16 18:52
Оценка:
Здравствуйте, Baudolino, Вы писали:

IT>>Не совсем так. DAL изолирует работу с базой в терминах самой базы от остальной части приложения путём конвертации терминов приложения в термины БД и наоборот.

B>Ну в общем, да. Можно еще короче сказать: абстрагируют работу с хранилищем, причем не обязательно БД:

Нет. Основная функция DAL — изоляция работы с БД от остальной части приложения. То, что это обладает ещё какими-то другими свойствами — это побочный эффект. Например, ту же обстракцию БД вполне хорошо, даже лучше чем DAL выполняет LINQ, но принципиально по-другому.
Если нам не помогут, то мы тоже никого не пощадим.
Re[16]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 26.06.16 22:16
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>>И давайте от противного. А может ли ваш linq добавить этот option (recompile)? Нет не может — будет кучу запросов генерить

G>Правильно. Генерить кучу запросов выгоднее. Хотя наверное linq2db может, я не проверял.

https://github.com/linq2db/linq2db/blob/master/Tests/Linq/Linq/QueryHintsTests.cs#L63
Если нам не помогут, то мы тоже никого не пощадим.
Re[24]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 26.06.16 23:46
Оценка: +2
Здравствуйте, gandjustas, Вы писали:

G>Это у разработчиков NHibernate, в EF проверил — в Update уходят только обновленные поля. В linq2db уверен то же самое.


В linq2db вообще нет такого. Вместо этого linq2db поддерживат DML и DDL.
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: Про путаницу с репозиториями и DAO
От: Sharov Россия  
Дата: 27.06.16 09:22
Оценка: -1
Здравствуйте, Vladek, Вы писали:


_>>А современные фреймворки еще сильнее путают разрабов, реализуя весь Data Access слой. И те, что DAO называли репозиториями начинают думать, что репозитории не нужны, и можно Linq-кодом прошить всю бизнес логику. Даже картинки у Фаулера и на MSDN способствуют тому, что репозиторий воспринимается, главным образом, как провайдер данных, т.е. абстракция над RDBMS, хотя его роль более значимая при проектировании. Возможно, я начитался DDD книжек.

V>Фаулер отличает репозиторий и шлюз таблицыу него это разные концепции. Проблема примеров — им надо показать всё и сразу, поэтому они очень сильно упрощают код. Код из примеров нельзя воспринимать как руководство к действию.

Какой смысл лепить отдельный паттерн "шлюз таблицы" Идиотизм. Это тоже самое, что и репозиторий, только конкретно типизированный под бд. Т.е. это одна и та же концепция.
Кодом людям нужно помогать!
Re[6]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 27.06.16 09:39
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Gattaka, Вы писали:


G>>А не могли бы вы приблизительную сигнатуру вашего класса показать? Так чтобы точно понимать...


IT>Не мог бы. Там и сигнатуры особенной нет. Это просто класс без всяких интерфейсов и паттернов, в котором куча коллекций с данными. Методы для инициализации и ещё какой-то фигни по мелочи. Просто большой контейнер. Это его основная задача.


God-object )
Re[3]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 27.06.16 09:45
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Здравствуйте, Vladek, Вы писали:



_>>>А современные фреймворки еще сильнее путают разрабов, реализуя весь Data Access слой. И те, что DAO называли репозиториями начинают думать, что репозитории не нужны, и можно Linq-кодом прошить всю бизнес логику. Даже картинки у Фаулера и на MSDN способствуют тому, что репозиторий воспринимается, главным образом, как провайдер данных, т.е. абстракция над RDBMS, хотя его роль более значимая при проектировании. Возможно, я начитался DDD книжек.

V>>Фаулер отличает репозиторий и шлюз таблицыу него это разные концепции. Проблема примеров — им надо показать всё и сразу, поэтому они очень сильно упрощают код. Код из примеров нельзя воспринимать как руководство к действию.

S>Какой смысл лепить отдельный паттерн "шлюз таблицы" Идиотизм. Это тоже самое, что и репозиторий, только конкретно типизированный под бд. Т.е. это одна и та же концепция.


У них разные задачи. Репозиторий может совершенно не знать как именно хранятся данные. Попробуйте глянуть на это с такой точки зрения: вам надо протестировать отдельно бизнес логику и отдельно правильное создание бизнес объектов юнит тестами. Как вы бедете изолироваться от хранилища, если все свалите в один класс?
Re[10]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 27.06.16 09:54
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Gattaka, Вы писали:


G>>Здравствуйте, IT, Вы писали:


IT>>>От хранимок нужно избавлятся при первом удобном случае. Логика в БД — это сегодня нонсенс.

G>>Красивый сказка, которая не выдерживает проверку практикой. При большее детальном рассмотрении запросов, создаваемых ORM выясняется что сильно не эффективны. Нужно избавляться от кодогенратов во всех их проявленяих в том числе и от ORM.
G>А можно пример? На моей практике люди сильно хуже пишут запросы в sql, чем на linq. Тем более последний отсекает ряд банальных ошибок, из-за которых запросы могут тормозить.

Эпическое движение стиля кодирования.
Помню, сначала писали SQL-стейтменты в коде.
Потом информации о том, как это плохо поддерживать появилось больше и начали перемещать стейтменты в хранимки.
Потом появился linq. SQL-стейтменты снова оказались в коде.
Теперь снова появляется много информации о том, как это хреново поддерживать...

Не пора ли уже остановиться?
Re[11]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 27.06.16 10:05
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Эпическое движение стиля кодирования.

_>Помню, сначала писали SQL-стейтменты в коде.
_>Потом информации о том, как это плохо поддерживать появилось больше и начали перемещать стейтменты в хранимки.
_>Потом появился linq. SQL-стейтменты снова оказались в коде.
_>Теперь снова появляется много информации о том, как это хреново поддерживать...

_>Не пора ли уже остановиться?


Не так все было.
С момента появления серверных СУБД было выгодно делать хранимки, они реально быстрее работали, чем adhoc запросы.
Ситуация кардинально поменялась в начале 2000-х, примерно в это время во всех популярных СУБД быстродействие adhoc запросов и хранимок сравнялась.
Тогда же появился холивар ХП vs запросы в коде. Оба подхода имели преимущества и недостатки.
Когда появился linq холивар можно было заканчивать. Ни одного реального преимущества у хранимок не осталось, кроме сценариев тупо не покрытых linq. Но холивар по привычке продолжился.
Относительно недавно появился linq2db, который покрывает любые сценарии работы с данными из приложения с помощью linq.
Re[12]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 27.06.16 10:12
Оценка:
Здравствуйте, gandjustas.

Вообще, это интересная тема сравнить эффективность linq2db, linq2entities...
Re[16]: Про путаницу с репозиториями и DAO
От: Sharov Россия  
Дата: 27.06.16 11:00
Оценка:
Здравствуйте, gandjustas, Вы писали:


S>>Вы смеетесь ??? Т.е. если Вы возьмете человека на фул-тайм с задачей написания sql запросов (специалиста), он больше одного запроса написать не сможет? Не считая того, что вообще говоря это должно быть компетенцией разработчика, отв. за соотв. таблицы (сущности).

G>Конечно сможет. И два сможет, и десять и может даже 50. Для статистики — у меня в очень простом приложении с 5 таблицами в базе получилось суммарно 60 разных запросов. А нанимать спеца отдельно по БД для базы менее чем два десятка таблиц никто не будет. Вот и посчитай какое количество запросов ему надо будет поддерживать.

Каждый разработчик может самостоятельно поддерживать свои запросы. Спец не обязателен. Или он пишет и отдает разработчику, а тот в дальнейшем за запрос отвечает.
Кодом людям нужно помогать!
Re[17]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 27.06.16 11:18
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Каждый разработчик может самостоятельно поддерживать свои запросы. Спец не обязателен. Или он пишет и отдает разработчику, а тот в дальнейшем за запрос отвечает.


И зачем разработчику поддерживать 10 почти одинаковых запросов на sql, а не одну функцию с linq и тремя if?
Re[18]: Про путаницу с репозиториями и DAO
От: Sharov Россия  
Дата: 27.06.16 11:20
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Sharov, Вы писали:


S>>Каждый разработчик может самостоятельно поддерживать свои запросы. Спец не обязателен. Или он пишет и отдает разработчику, а тот в дальнейшем за запрос отвечает.


G>И зачем разработчику поддерживать 10 почти одинаковых запросов на sql, а не одну функцию с linq и тремя if?


Если самописные быстрее, почему бы и нет?
Кодом людям нужно помогать!
Re[19]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 27.06.16 11:23
Оценка: +2
Здравствуйте, Sharov, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:


G>>Здравствуйте, Sharov, Вы писали:


S>>>Каждый разработчик может самостоятельно поддерживать свои запросы. Спец не обязателен. Или он пишет и отдает разработчику, а тот в дальнейшем за запрос отвечает.

G>>И зачем разработчику поддерживать 10 почти одинаковых запросов на sql, а не одну функцию с linq и тремя if?
S>Если самописные быстрее, почему бы и нет?
А с чего они могут быть быстрее? И насколько они быстрее, что стоят десятикратного (а на деле больше) геморроя?
Re[10]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 27.06.16 13:09
Оценка: :)
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, another_coder, Вы писали:


_>>Здравствуйте, gandjustas.


_>>На мой взгляд, вы не разделяете сущности от их технической реализации. В примере с тремя источниками данных, они все предлставляют собой одно и то же: хранилище данных. Для модуля системы, который использует хранилище данных, не важно что именно это (БД, IList, файл или web service). Весь функционал, который относится к доступу к данным спрятан там же и наружу не показывается. И вот тут как раз приходин на помощь объект, представляющий собой DAO. Конкретная его реализация может быть разной и зависить от уровня закладываемой абстракции в архитектуру. Это может быть EF, а может быть и просто объект с простым CRUD интерфейсом.


G>Ок, давайте конкретнее. Пусть у вас есть "хранилище данных" сотрудников.

G>Фактически это будет три разных хранилища:
G>1) Список в памяти — List<T>
G>2) База данных
G>3) Веб-сервис, который имеет только методы получения и сохранения по ID.

G>У вас задача поднять сотрудникам определенной категории зарплату на 10%.


G>Какой интерфейс универсального "хранилища данных" вы спроектируете для решения этой задачи.

G>Естественно писать заведомо неэффективный код — моветон.

var employees = await employeeRepo.GetEmployeesAsync(EmployeeCategory.SpecificCategory);
foreach (var employee in employees)
{
    employee.ChangeSalary(factor: +0.1f);
}
await Employee.SaveChangesAsync(employees);


Тут репозиторий знает откуда брать сотрудников (и их менеджеров, то есть репозиторий возвращает агрегаты из DDD), сотрудники знают как менять себе (в ООП лампочка вкручивает себя в люстру сама) зарплаты (все проверки и согласования с менеджерами внутри) и сохранять изменения. Хранилище тут находится уровнем ниже и используется репозиторием и сотрудниками — это может быть простой шлюз таблицы, обменивающийся с репозиторием и сотрудниками простыми объектами (DTO). И уже само хранилище владеет знанием откуда данные собственно берутся.

Зависимости:
Объекты предметной области -> объекты передачи данных -> объекты реализации (объекты EF, объекты веб-сервисов, а List<T> хранит объекты передачи данных)
Re[14]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 27.06.16 13:16
Оценка: -1
Здравствуйте, gandjustas, Вы писали:

G>Да вот у меня как-то не было примеров, чтобы одна и та же программа могла работать с любым источником данных. Скорее обратное. Даже небольшие различия в разных движках БД приводили к различному коду, даже с применением ORM.


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

Код доступа к бд через ORM менять не надо, надо просто добавить ещё один.
Re[11]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 27.06.16 13:18
Оценка:
Здравствуйте, Vladek, Вы писали:

G>>Естественно писать заведомо неэффективный код — моветон.


V>
V>var employees = await employeeRepo.GetEmployeesAsync(EmployeeCategory.SpecificCategory);
V>foreach (var employee in employees)
V>{
V>    employee.ChangeSalary(factor: +0.1f);
V>}
V>await Employee.SaveChangesAsync(employees);
V>


Ох сколько вопросов сразу:
1) В случае List<T> надо все синхронные операции в async\await оборачивать?
2) В случае базы данных вместо одного апдейта в базу улетит 100500?
3) Откуда вообще у веб-сервиса метод с фильтрами по категории или будут тянуться все сотрудники, а потом фильтр пойдет на клиенте?


У вас получился заведомо неэффективный код для каждого из случаев И сразу же отрублены все возможности улучшить ситуацию.
Re[15]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 27.06.16 13:20
Оценка: +1
Здравствуйте, Vladek, Вы писали:

V>Здравствуйте, gandjustas, Вы писали:


G>>Да вот у меня как-то не было примеров, чтобы одна и та же программа могла работать с любым источником данных. Скорее обратное. Даже небольшие различия в разных движках БД приводили к различному коду, даже с применением ORM.


V>У тебя есть приложение, которое берёт данные из корпоративной бд. Ваша контора слилась с другой конторой, приложение должно теперь обслуживать и её, однако данные та другая контора хранила в каком-то облаке с доступом по wep api и что-то менять будет дорого, поэтому приложение теперь должно уметь общаться с тем веб-сервисом, а бизнес-процесс менять не надо.


Тупо загружается из облако все в базу и проблема на этом кончается.
И, как это ни странно, я как раз такой сценарий реализовывал на практике при слиянии компаний.

V>Код доступа к бд через ORM менять не надо, надо просто добавить ещё один.

Никто такой фигней не занимается. Проще перелить данные в один источник, чем заниматься таким геморроем.
Re[11]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 27.06.16 14:01
Оценка:
Здравствуйте, Sharov, Вы писали:

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


S>Для этого и придуманы паттерны. Современная архитектура -- это взаимодействие паттерноподобных сущностей, абстракций. А гофосккие паттерны -- платоновский идеал.


Паттерны носят описательный характер. Они описывают то, что уже есть в коде, никто их отдельно не придумывал и вообще вменяемый разработчик переизобретает их сам, даже не читая книжек про паттерны. Хорошая архитектура не та, в которой много паттернов, а та, которую легко описать.
Re[7]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 27.06.16 14:02
Оценка:
Здравствуйте, another_coder, Вы писали:

_>God-object )


Помоечка с кучей необходимого для одного процесса.
Если нам не помогут, то мы тоже никого не пощадим.
Re[10]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 27.06.16 14:12
Оценка:
Здравствуйте, IT, Вы писали:

IT>Попробуй. Хорошо писать как бог на душу положит мало у кого получается. Это вывсшее дао программирования. Я пока таких людей не встречал.


У кода две функции — выполнять поставленную задачу, быть готовым к изменениям. Любой код первую функцию выполняет худо-бедно, человек освоивший программирование такой код напишет. Это код можно написать как угодно.

Проблемы возникают со второй функцией кода — быть готовым к изменениям. Для этого и нужна архитектура.

Подход, когда код сначала пишется с наскока, а потом, будучи испытанным в деле, рефакторится в нечто вменяемое, работает довольно неплохо если с самого начала проектирование архитектуры буксовало.
Re[12]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 27.06.16 14:32
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Ох сколько вопросов сразу:

G>1) В случае List<T> надо все синхронные операции в async\await оборачивать?
Тривиальная задача.
G>2) В случае базы данных вместо одного апдейта в базу улетит 100500?
Не обязательно. Скорее всего, не важно.
G>3) Откуда вообще у веб-сервиса метод с фильтрами по категории или будут тянуться все сотрудники, а потом фильтр пойдет на клиенте?
Метод с фильтрами по категории у нашего репозитория, а как он реализован — дело десятое. Главное, код работы с зарплатой от этого никак не зависит.

G>У вас получился заведомо неэффективный код для каждого из случаев И сразу же отрублены все возможности улучшить ситуацию.

Что такое "эффективный код" в случае изменения важной информации о сотрудниках предприятия?
Re[16]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 27.06.16 14:39
Оценка:
Здравствуйте, gandjustas, Вы писали:

V>>У тебя есть приложение, которое берёт данные из корпоративной бд. Ваша контора слилась с другой конторой, приложение должно теперь обслуживать и её, однако данные та другая контора хранила в каком-то облаке с доступом по wep api и что-то менять будет дорого, поэтому приложение теперь должно уметь общаться с тем веб-сервисом, а бизнес-процесс менять не надо.


G>Тупо загружается из облако все в базу и проблема на этом кончается.

G>И, как это ни странно, я как раз такой сценарий реализовывал на практике при слиянии компаний.

И руководство компаний тебе просто доверилось? А потом сразу упразднили во второй компании старый процесс и перешли на новый? Почему слиянием БД занимался программист, а не команда администраторов БД и бизнес-аналитиков?

V>>Код доступа к бд через ORM менять не надо, надо просто добавить ещё один.

G>Никто такой фигней не занимается. Проще перелить данные в один источник, чем заниматься таким геморроем.

Занимаются те, для кого это совсем не гемморой, кто хорошо изолировал свой код от источников данных.
Re[13]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 27.06.16 14:41
Оценка:
Здравствуйте, Vladek, Вы писали:

V>Здравствуйте, gandjustas, Вы писали:


G>>Ох сколько вопросов сразу:

G>>1) В случае List<T> надо все синхронные операции в async\await оборачивать?
V>Тривиальная задача.
Так и запишем — лишняя работа.
Кстати что SaveChangesAsync будет в этом случае делать? Как гарантировать в тестах что он вызывается, если мы изначально делаем на List<T>, а потом "подменяем storage"?

G>>2) В случае базы данных вместо одного апдейта в базу улетит 100500?

V>Не обязательно. Скорее всего, не важно.
Скорее всего очень важно. Примерно на два порядка будет отличаться время работы в зависимости от реализации.

G>>3) Откуда вообще у веб-сервиса метод с фильтрами по категории или будут тянуться все сотрудники, а потом фильтр пойдет на клиенте?

V>Метод с фильтрами по категории у нашего репозитория, а как он реализован — дело десятое. Главное, код работы с зарплатой от этого никак не зависит.
Во-во и на практике приведёт к тому, что будет затягиваться вес список и фильтроваться на клиенте. А потом этот потрясающий get-метод ктонить использует на главной странице и приложением станет невозможно пользоваться.

G>>У вас получился заведомо неэффективный код для каждого из случаев И сразу же отрублены все возможности улучшить ситуацию.

V>Что такое "эффективный код" в случае изменения важной информации о сотрудниках предприятия?
Эффективный код, который не делает лишней работы или соотношение лишней или доля лишней работы крайне мала. Чем он занимается — неважно, потому что имеется тенденция распространения стиля на все приложение. То что кажется в одном месте маленькой неэффективностью копируется в другое место, где появляются тормоза на несколько десятков секунд.

Не знаю как ты, а я все это уже проходил. За каждым из приведенных выше вопросов стоит косяк в реальной программе, который привел к реальным потерям.
Re[17]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 27.06.16 14:48
Оценка:
Здравствуйте, Vladek, Вы писали:

G>>Тупо загружается из облако все в базу и проблема на этом кончается.

G>>И, как это ни странно, я как раз такой сценарий реализовывал на практике при слиянии компаний.
V>И руководство компаний тебе просто доверилось? А потом сразу упразднили во второй компании старый процесс и перешли на новый? Почему слиянием БД занимался программист, а не команда администраторов БД и бизнес-аналитиков?
Примерно так было. Было две CRM, с одной стороны самописная, с другой нет. Из самописной выгрузили послезные данные и загрузили в рабочую.
Не просто доверилось, а когда посчитали затраты на доработку самописной + такую архитектуру как ты предлагаешь, так сразу просили перелить данные побыстрее.

G>>Никто такой фигней не занимается. Проще перелить данные в один источник, чем заниматься таким геморроем.

V>Занимаются те, для кого это совсем не гемморой, кто хорошо изолировал свой код от источников данных.
У меня начали закрадываться подозрения, что ты ни одного реального приложения не сделал. Чтобы сотня ГБ данных и хотя бы 1000 пользователей была.
Re[14]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 27.06.16 15:14
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Vladek, Вы писали:


V>>Здравствуйте, gandjustas, Вы писали:


G>>>Ох сколько вопросов сразу:

G>>>1) В случае List<T> надо все синхронные операции в async\await оборачивать?
V>>Тривиальная задача.
G>Так и запишем — лишняя работа.


G>Кстати что SaveChangesAsync будет в этом случае делать? Как гарантировать в тестах что он вызывается, если мы изначально делаем на List<T>, а потом "подменяем storage"?


Извлекает из сотрудников все нужные данные (в виде DTO) и отправляет их на сохранение в хранилище. Не важное какое хранилище, на входе у него всегда эти DTO.

G>>>2) В случае базы данных вместо одного апдейта в базу улетит 100500?

V>>Не обязательно. Скорее всего, не важно.
G>Скорее всего очень важно. Примерно на два порядка будет отличаться время работы в зависимости от реализации.

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

G>>>3) Откуда вообще у веб-сервиса метод с фильтрами по категории или будут тянуться все сотрудники, а потом фильтр пойдет на клиенте?

V>>Метод с фильтрами по категории у нашего репозитория, а как он реализован — дело десятое. Главное, код работы с зарплатой от этого никак не зависит.
G>Во-во и на практике приведёт к тому, что будет затягиваться вес список и фильтроваться на клиенте. А потом этот потрясающий get-метод ктонить использует на главной странице и приложением станет невозможно пользоваться.
На практике у веб-сервиса есть подходящий API для выборки данных, хотя бы простой поиск. Ты в своей фантазии про медленный get можешь добавить в реализацию хранилища кэш, это опять же никак не повлияет на код репозитория.

G>>>У вас получился заведомо неэффективный код для каждого из случаев И сразу же отрублены все возможности улучшить ситуацию.

V>>Что такое "эффективный код" в случае изменения важной информации о сотрудниках предприятия?
G>Эффективный код, который не делает лишней работы или соотношение лишней или доля лишней работы крайне мала. Чем он занимается — неважно, потому что имеется тенденция распространения стиля на все приложение. То что кажется в одном месте маленькой неэффективностью копируется в другое место, где появляются тормоза на несколько десятков секунд.

Эффективный код в отношении изменения зарплаты сотрудников должен быть атомарным. Мы это достигаем за счёт аккумулирования всех изменений в одном вызове SaveChangesAsync. Будет он выполняться 50 мс или 10 секунд — дело десятое и легко решаемое, для задачи гораздо важнее гарантированное выполнение или чёткий откат в случае сбоя.

G>Не знаю как ты, а я все это уже проходил. За каждым из приведенных выше вопросов стоит косяк в реальной программе, который привел к реальным потерям.


С изменением требований я сталкиваюсь постоянно. Сегодня надо работать с таким источником данных, завтра с ещё одним. Использую описанный подход и не имею особых проблем.
Re[11]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 27.06.16 15:27
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Не пора ли уже остановиться?


Дык спираль развития же.
Если нам не помогут, то мы тоже никого не пощадим.
Re[10]: Про путаницу с репозиториями и DAO
От: Baudolino  
Дата: 27.06.16 17:38
Оценка:
Здравствуйте, IT, Вы писали:

IT>Нет. Основная функция DAL — изоляция работы с БД от остальной части приложения. То, что это обладает ещё какими-то другими свойствами — это побочный эффект. Например, ту же обстракцию БД вполне хорошо, даже лучше чем DAL выполняет LINQ, но принципиально по-другому.


Два момента:
1. LINQ не "выполняет абстракцию БД", потому что это интерфейс доступа к наборам произвольных данных (и первый же пример из MSDN об этом, поскольку использует LINQ на обычном списке).
2. DAL в случае с LINQ никуда не девается, просто всю работу за вас делает вендор ORM-решения, которое и представляет собой тот самый DAL, абстрагирующий вас от особенностей работы с БД с помощью LINQ.
Re[18]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 27.06.16 18:22
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Vladek, Вы писали:


G>>>Тупо загружается из облако все в базу и проблема на этом кончается.

G>>>И, как это ни странно, я как раз такой сценарий реализовывал на практике при слиянии компаний.
V>>И руководство компаний тебе просто доверилось? А потом сразу упразднили во второй компании старый процесс и перешли на новый? Почему слиянием БД занимался программист, а не команда администраторов БД и бизнес-аналитиков?
G>Примерно так было. Было две CRM, с одной стороны самописная, с другой нет. Из самописной выгрузили послезные данные и загрузили в рабочую.
G>Не просто доверилось, а когда посчитали затраты на доработку самописной + такую архитектуру как ты предлагаешь, так сразу просили перелить данные побыстрее.

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

G>>>Никто такой фигней не занимается. Проще перелить данные в один источник, чем заниматься таким геморроем.

V>>Занимаются те, для кого это совсем не гемморой, кто хорошо изолировал свой код от источников данных.
G>У меня начали закрадываться подозрения, что ты ни одного реального приложения не сделал. Чтобы сотня ГБ данных и хотя бы 1000 пользователей была.

Лучше опиши свой подход к архитектуре, которая однако благополучно переживала потрясения типа описанных выше.
Re[11]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 27.06.16 19:06
Оценка:
Здравствуйте, Baudolino, Вы писали:

B>1. LINQ не "выполняет абстракцию БД", потому что это интерфейс доступа к наборам произвольных данных (и первый же пример из MSDN об этом, поскольку использует LINQ на обычном списке).


Это у кого как и смотря что мы под этим понимаем.

B>2. DAL в случае с LINQ никуда не девается, просто всю работу за вас делает вендор ORM-решения, которое и представляет собой тот самый DAL, абстрагирующий вас от особенностей работы с БД с помощью LINQ.


Нет. Функция изоляции переехала в LINQ. А сам DAL больше нафиг не нужен.
Если нам не помогут, то мы тоже никого не пощадим.
Re[19]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 27.06.16 21:26
Оценка: 1 (1)
Здравствуйте, Vladek, Вы писали:

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

Никому твоя архитектура не нужна. Нужно чтобы быстро работало и быстро вносились изменения.
Самый быстрый способ в случае объединения двух систем — перелить данные одной в другую.


G>>>>Никто такой фигней не занимается. Проще перелить данные в один источник, чем заниматься таким геморроем.

V>>>Занимаются те, для кого это совсем не гемморой, кто хорошо изолировал свой код от источников данных.
G>>У меня начали закрадываться подозрения, что ты ни одного реального приложения не сделал. Чтобы сотня ГБ данных и хотя бы 1000 пользователей была.
V>Лучше опиши свой подход к архитектуре, которая однако благополучно переживала потрясения типа описанных выше.

Мой подход — использовать ORM https://habrahabr.ru/post/230623/ и http://blog.gandjustas.ru/2014/09/23/asp.net-linq-ef-sql-server-performance/
И делать кеширование
https://habrahabr.ru/post/168725/
https://habrahabr.ru/post/168869/
https://habrahabr.ru/post/227129/
https://habrahabr.ru/post/240269/

Еще я применяю IOC иногда http://blog.gandjustas.ru/categories/#IOC

И вот такие подходы к архитектуре http://blog.gandjustas.ru/categories/#Архиетктураприложений
Re[14]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 28.06.16 08:08
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Кстати что SaveChangesAsync будет в этом случае делать? Как гарантировать в тестах что он вызывается, если мы изначально делаем на List<T>, а потом "подменяем storage"?


Вы действительно проверяете в тесте вызовы методов? Если да, то зачем именно этот и как?

На мой взгляд, в юнит тестах вызов такого метода (сохраняющего в базу) проверять не надо. И в интеграциогнных тестах не следует проверять его вызов, а только наличие данных в хранилище после "действий". Поэтому не понимаю вопрос про проверку.
Отредактировано 28.06.2016 9:46 another_coder . Предыдущая версия .
Re[15]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 28.06.16 10:12
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Здравствуйте, gandjustas, Вы писали:


G>>Кстати что SaveChangesAsync будет в этом случае делать? Как гарантировать в тестах что он вызывается, если мы изначально делаем на List<T>, а потом "подменяем storage"?


_>Вы действительно проверяете в тесте вызовы методов? Если да, то зачем именно этот и как?

Затем что был проект, покрытый юнит тестами и они не падали, но не работало. Оказалось, что тест проверял только добавление в коллекцию объекта, а метод сохранения не вызывался.
Проверяется банально моками.

_>На мой взгляд, в юнит тестах вызов такого метода (сохраняющего в базу) проверять не надо.

Тогда ценность юнит-теста падает до нуля. Зеленый тест перестает показывать что метод работает.

_>И в интеграциогнных тестах не следует проверять его вызов, а только наличие данных в хранилище после "действий". Поэтому не понимаю вопрос про проверку.

В интеграционных да, но разработчики часто не прогоняют интеграционные тесты, а юнит-тесты, особенно с ReSharper прогоняются всегда.
Re[15]: Про путаницу с репозиториями и DAO
От: Sinclair Россия https://github.com/evilguest/
Дата: 28.06.16 11:21
Оценка:
Здравствуйте, Vladek, Вы писали:


V>Эффективный код в отношении изменения зарплаты сотрудников должен быть атомарным. Мы это достигаем за счёт аккумулирования всех изменений в одном вызове SaveChangesAsync. Будет он выполняться 50 мс или 10 секунд — дело десятое и легко решаемое, для задачи гораздо важнее гарантированное выполнение или чёткий откат в случае сбоя.

Это вам так кажется потому, что вы не представляете себе масштабов проблемы. Дело в том, что тормоза в подобных приложениях имеют многостадийный эффект.
То есть сначала просто всё работает чуть медленнее, чем хочется. Это нефункциональная проблема, и нам наплевать.
Потом оказывается, что с ростом нагрузки на базу транзакции, внутри которых происходит общение с клиентом, начинают тормозить всё сильнее — потому, что каждый update застревает на чуть-чуть в ожидании отпускания локов.
Транзакции становятся всё дольше, и удерживают локи на всё более длинное время. Тормоза стремительно прогрессируют.
Затем тормоза начинают становиться фейлами, потому что периодически происходят дедлоки. И наша атомарность оборачивается тем, что мы получаем "чёткий откат" вместо внесения изменений.
Начинаются вот эти вот знаменитые "девочки, все выйдите из терминала, я отгрузку провожу".
А всё оттого что в кузнице не было гвоздя вначале кто-то решил, что засосать тыщу записей в память, а потом по одной фигачить update на их основе — это нормальная стратегия. А чо — в тестах в single-user моде всё исполняется 10 секунд, что должно бы всех устроить.

V>С изменением требований я сталкиваюсь постоянно. Сегодня надо работать с таким источником данных, завтра с ещё одним. Использую описанный подход и не имею особых проблем.

Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[15]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 28.06.16 11:36
Оценка:
Здравствуйте, Vladek, Вы писали:


G>>Кстати что SaveChangesAsync будет в этом случае делать? Как гарантировать в тестах что он вызывается, если мы изначально делаем на List<T>, а потом "подменяем storage"?

V>Извлекает из сотрудников все нужные данные (в виде DTO) и отправляет их на сохранение в хранилище. Не важное какое хранилище, на входе у него всегда эти DTO.
Чего и откуда извлекаем? У нас list<T> — хранилище. Ты для него будет маппинги T на T писать?

G>>>>2) В случае базы данных вместо одного апдейта в базу улетит 100500?

V>>>Не обязательно. Скорее всего, не важно.
G>>Скорее всего очень важно. Примерно на два порядка будет отличаться время работы в зависимости от реализации.
V>Хранилище на входе получает список DTO со всеми изменёнными данными и вольно делать с ними всё, что пожелает: упаковать в один запрос к БД, в несколько, как угодно. Проблемы с этим решаются на уровне кода хранилища, другой код от вносимых изменений зависеть не будет.
Каким образом интересно?
Как из набора запросов вида
update T set F=F+50 where ID=1
update T set F=F+50 where ID=4
update T set F=F+50 where ID=7
...

Получить запрос

update T set F=F+50 where Category=x

?

V>На практике у веб-сервиса есть подходящий API для выборки данных, хотя бы простой поиск.

Это очень сильное предположение. Я гораздо больше веб-сервисов видел без методов поиска, чем с ним.


V>Эффективный код в отношении изменения зарплаты сотрудников должен быть атомарным. Мы это достигаем за счёт аккумулирования всех изменений в одном вызове SaveChangesAsync. Будет он выполняться 50 мс или 10 секунд — дело десятое и легко решаемое, для задачи гораздо важнее гарантированное выполнение или чёткий откат в случае сбоя.

Прости, а как ты обеспечишь атомарность для:
1) Веб-сервиса, которому нужно PUT на каждый объект
2) List<T> в памяти?

Или у тебя контракт репозитария меняется в зависимости от реализации?


G>>Не знаю как ты, а я все это уже проходил. За каждым из приведенных выше вопросов стоит косяк в реальной программе, который привел к реальным потерям.

V>С изменением требований я сталкиваюсь постоянно. Сегодня надо работать с таким источником данных, завтра с ещё одним. Использую описанный подход и не имею особых проблем.
Это значит что у тебя приложения примитивные, возможно однопользовательские. Тогда действительно можно репозитариями пользоваться.
Re[20]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 28.06.16 11:44
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Мой подход — использовать ORM

G>Еще я применяю IOC иногда

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

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

G>И вот такие подходы к архитектуре http://blog.gandjustas.ru/categories/#Архиетктураприложений


Статья про DDD меня расстроила, очень поверхностный взгляд на DDD. В статье про ортогональность проповедуется анемичная модель данных. Остальные скорее вводные статьи по отдельным темам.

Каждому своё, но раз в пару лет надо критически анализировать свои подходы. Раньше, начиная проект, я рано или поздно упирался в стену. Анемичные модели данных, объекты ORM заменяли мне модель предметной области, бизнес-правила были вообще где попало. В начале код писался легко и даже работал, но потом когда возникали новые обстоятельства — старый код становился препятствием на пути реализации новых фич. Мне довольно легко удавалось реализовывать только отдельные изолированные части — что-то вроде библиотек для проекта — какой-нибудь парсер, или алгоритм. А проектирование всей программы как единого целого двигалось со скрипом. Потом, посидев за книжками и статьями, я наконец-то начал понимать роль инкапсуляции в проектировании и важность оформления ядра программы как независимого целого (предметной области). Кстати оказались доклады Роберта Мартина по архитектуре, проектировать программы стало проще.

https://youtu.be/HhNIttd87xs?t=58m15s
Re[16]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 28.06.16 11:58
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>А всё оттого что в кузнице не было гвоздя вначале кто-то решил, что засосать тыщу записей в память, а потом по одной фигачить update на их основе — это нормальная стратегия. А чо — в тестах в single-user моде всё исполняется 10 секунд, что должно бы всех устроить.


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

Да, отполированные знания тонкостей работы какой-то БД важны, но БД — не более, чем деталь реализации.
Re[21]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 28.06.16 12:11
Оценка: 1 (1) +2
Здравствуйте, Vladek, Вы писали:

V>Задача внятной архитектуры — надёжно изолировать технические детали от других частей программы, чтобы впоследствии эти детали можно было независимо менять. Разделяй и властвуй.

Для этого применяю, внимание, функциональную декомпозицию!
Да-да, оказывается можно сделать функцию, которая вызывает другую функцию, а еще можно сделать функцию, которая вызывает несколько функций, передавай результаты одной в другую. Скоро уже 100 лет, как этот метод надежно изолирует части программы друг от друга.
Странно что еще паттерн для этого не придумали.

G>>И вот такие подходы к архитектуре http://blog.gandjustas.ru/categories/#Архиетктураприложений


V>Статья про DDD меня расстроила, очень поверхностный взгляд на DDD. В статье про ортогональность проповедуется анемичная модель данных. Остальные скорее вводные статьи по отдельным темам.

Ну хоть прочитал.

V>Каждому своё, но раз в пару лет надо критически анализировать свои подходы. Раньше, начиная проект, я рано или поздно упирался в стену. Анемичные модели данных, объекты ORM заменяли мне модель предметной области, бизнес-правила были вообще где попало. В начале код писался легко и даже работал, но потом когда возникали новые обстоятельства — старый код становился препятствием на пути реализации новых фич. Мне довольно легко удавалось реализовывать только отдельные изолированные части — что-то вроде библиотек для проекта — какой-нибудь парсер, или алгоритм. А проектирование всей программы как единого целого двигалось со скрипом. Потом, посидев за книжками и статьями, я наконец-то начал понимать роль инкапсуляции в проектировании и важность оформления ядра программы как независимого целого (предметной области). Кстати оказались доклады Роберта Мартина по архитектуре, проектировать программы стало проще.

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

Мне довелось участвовать в проекте, где укушенные DDD люди делали "ядро". Было даже две команды — "ядро" и "не-ядро". Ядро работало прекрасно, как уверяли разработчики, все остальное глючило адски, была проблема с быстродействием. Меня как раз позвали помочь с этим. Выяснилось что из-за "доменной модели" из базы поднимается по 3МБ на каждый запрос. Я с огромным трудом уболтал часть сценариев реализовать через linq запросы с проекциями. Понадобилось часть логики вынести из модели в отдельные функции, зато ключевые сценарии заработали с приемлемой скоростью.

Насколько я знаю техдиректора результат вдохновил, он насильно убрал разделения "ядра" и "не-ядра" и еще часть кода переписали в таком духе, оказавшись от модели.

Очевидно что причиной проблем была модель, но об этом никто ни разу не сказал.
Спустя 3 года я читал Психологию влияния Чалдини, у него есть глава, где он описывает секту. Так вот отношение людей к "ядру" очень напоминало секту.

ЗЫ. Не существует ни одного доказательства, проверяемых данных, что DDD в коде (ака "ядро") хоть как-то улучшает характеристики программы. Зато контр примеров, где наличие "ядра" может ухудшить — полно.
Re[16]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 28.06.16 12:14
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Vladek, Вы писали:



G>>>Кстати что SaveChangesAsync будет в этом случае делать? Как гарантировать в тестах что он вызывается, если мы изначально делаем на List<T>, а потом "подменяем storage"?

V>>Извлекает из сотрудников все нужные данные (в виде DTO) и отправляет их на сохранение в хранилище. Не важное какое хранилище, на входе у него всегда эти DTO.
G>Чего и откуда извлекаем? У нас list<T> — хранилище. Ты для него будет маппинги T на T писать?

Из объекта предметной области надо извлечь данные — для передачи в хранилище. Хранилище на входе получает простые структуры данных DTO, а не объекты предметной области. Как осуществляется маппинг — скорее всего, объект предметной области может вернуть структуру с нужными данными.

G>>>>>2) В случае базы данных вместо одного апдейта в базу улетит 100500?

V>>>>Не обязательно. Скорее всего, не важно.
G>>>Скорее всего очень важно. Примерно на два порядка будет отличаться время работы в зависимости от реализации.
V>>Хранилище на входе получает список DTO со всеми изменёнными данными и вольно делать с ними всё, что пожелает: упаковать в один запрос к БД, в несколько, как угодно. Проблемы с этим решаются на уровне кода хранилища, другой код от вносимых изменений зависеть не будет.
G>Каким образом интересно?
G>Как из набора запросов вида
G>
G>update T set F=F+50 where ID=1
G>update T set F=F+50 where ID=4
G>update T set F=F+50 where ID=7
G>...
G>

G>Получить запрос

G>
G>update T set F=F+50 where Category=x
G>

G>?

V>>На практике у веб-сервиса есть подходящий API для выборки данных, хотя бы простой поиск.

G>Это очень сильное предположение. Я гораздо больше веб-сервисов видел без методов поиска, чем с ним.

Ты тоже выдаёшь мне предположения про ужасы работы с БД, как и другой человек постом выше. Там я уже ответил про это: http://rsdn.ru/forum/design/6484598.1
Автор: Vladek
Дата: 28.06.16


V>>Эффективный код в отношении изменения зарплаты сотрудников должен быть атомарным. Мы это достигаем за счёт аккумулирования всех изменений в одном вызове SaveChangesAsync. Будет он выполняться 50 мс или 10 секунд — дело десятое и легко решаемое, для задачи гораздо важнее гарантированное выполнение или чёткий откат в случае сбоя.

G>Прости, а как ты обеспечишь атомарность для:
G>1) Веб-сервиса, которому нужно PUT на каждый объект
Будет определяться возможностями веб-сервиса. Либо отправлять записи по одной, либо скопом. Делать несколько попыток, применить circuit breaker.

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

G>2) List<T> в памяти?

Ну тут просто и сбоев не будет.
G>Или у тебя контракт репозитария меняется в зависимости от реализации?
Задача репозитория — выбрать нужное множество сотрудников по какому-то критерию, он не отвечает за изменения их данных.

G>>>Не знаю как ты, а я все это уже проходил. За каждым из приведенных выше вопросов стоит косяк в реальной программе, который привел к реальным потерям.

V>>С изменением требований я сталкиваюсь постоянно. Сегодня надо работать с таким источником данных, завтра с ещё одним. Использую описанный подход и не имею особых проблем.
G>Это значит что у тебя приложения примитивные, возможно однопользовательские. Тогда действительно можно репозитариями пользоваться.

Это уже вторая попытка померяться длиной, видимо, записей в БД. Компенсируешь анатомические особенности?
Re[17]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 28.06.16 12:18
Оценка:
Здравствуйте, Vladek, Вы писали:

V>Это аргументы в стиле подкладывания рельсы японской пиле. Да мало ли какую страшную картинку можно нарисовать, какое отношение работа с БД имеет к внятной архитектуре? А что, если изменение зарплат сотрудников на выходе имеет не SQL-запрос, а документ на подпись начальнику? А потом подписанный документ с штрих-кодом сканируется и выполняется нужное действие, опять же не через SQL, а запросом в облако к нереляционному хранилищу?

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

Ты пытаешься уменьшить важность проблемы, тип что повышение ЗП нечастое явление и апдейту предшествует много других операций.
Но ты забыл что повышение ЗП было выбрано как пример, можно найти миллионы операций, которые часто выполняются и для них твоя логика "уменьшения проблемы" просто не сработает.
А еще на практике неоднократно видел, такой стиль работы с базой проникает во все части программы, и тормозит вообще все.

V>Да, отполированные знания тонкостей работы какой-то БД важны, но БД — не более, чем деталь реализации.

БД — серьезное архитектурное ограничение. Изменение БД может поменять архитектуру всей системы.
Знаю один курьезный случай, когда группу разработчиков обученных T-SQL отправили писать код для оракла. Казалось бы в чем проблема, синтаксис выучат и нафигачат код, примерно такой же, как во всех приложениях.
Но выяснилось на ходу, что в оракле индексы работают по-другому и честную сериализуемость оракл не поддерживает. Переписали тогда очень много.
Re[18]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 28.06.16 12:30
Оценка:
Здравствуйте, gandjustas, Вы писали:

V>>Да, отполированные знания тонкостей работы какой-то БД важны, но БД — не более, чем деталь реализации.

G>БД — серьезное архитектурное ограничение. Изменение БД может поменять архитектуру всей системы.

Надоело повторяться, уже понятно, что каждый останется при своём мнении. https://www.youtube.com/watch?v=HhNIttd87xs — смотреть с 01:04:00
Re[17]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 28.06.16 12:43
Оценка: +1
Здравствуйте, Vladek, Вы писали:

V>Из объекта предметной области надо извлечь данные — для передачи в хранилище. Хранилище на входе получает простые структуры данных DTO, а не объекты предметной области. Как осуществляется маппинг — скорее всего, объект предметной области может вернуть структуру с нужными данными.

Я перестаю понимать что ты пишешь. Приведи пример кода репозитория, который list<T> использует.

V>Ты тоже выдаёшь мне предположения про ужасы работы с БД, как и другой человек постом выше. Там я уже ответил про это: http://rsdn.ru/forum/design/6484598.1
Автор: Vladek
Дата: 28.06.16

И ты тоже пытаешься уйти от ответа.

V>>>Эффективный код в отношении изменения зарплаты сотрудников должен быть атомарным. Мы это достигаем за счёт аккумулирования всех изменений в одном вызове SaveChangesAsync. Будет он выполняться 50 мс или 10 секунд — дело десятое и легко решаемое, для задачи гораздо важнее гарантированное выполнение или чёткий откат в случае сбоя.

G>>Прости, а как ты обеспечишь атомарность для:
G>>1) Веб-сервиса, которому нужно PUT на каждый объект
V>Будет определяться возможностями веб-сервиса. Либо отправлять записи по одной, либо скопом. Делать несколько попыток, применить circuit breaker.
В этом и был вопрос. Когда хранилища дают разные гарантии какой должен быть репозитарий? Сможет ли он обеспечить эффективно все сценарии работы?
Ты сказал что SaveChangesAsync должен быть атомарным, но веб-сервис не поддерживает атомарного обновления множества записей. Что ты будешь делать в этом случае?

V>Это определяется требованиями к конкретной программе, а ты меня постоянно пытаешься перетянуть на обсуждение технических деталей реализации примера. Это имеет слабое отношение к архитектуре, отполированные запросы к БД и веб-сервисам мало помогут при невнятной архитектуре. Программа превратится в несопровождаемое нечто и обузу для пользователей. Архитектура про изоляцию технических деталей (типа запросов выше) и управление зависимостями между отдельными частями программы.

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

G>>2) List<T> в памяти?

V>Ну тут просто и сбоев не будет.
G>>Или у тебя контракт репозитария меняется в зависимости от реализации?
V>Задача репозитория — выбрать нужное множество сотрудников по какому-то критерию, он не отвечает за изменения их данных.
Еще раз спрашиваю — как ты добьёшься атомарности SaveChangesAsync для хранилища в виде List<T>?

G>>>>Не знаю как ты, а я все это уже проходил. За каждым из приведенных выше вопросов стоит косяк в реальной программе, который привел к реальным потерям.

V>>>С изменением требований я сталкиваюсь постоянно. Сегодня надо работать с таким источником данных, завтра с ещё одним. Использую описанный подход и не имею особых проблем.
G>>Это значит что у тебя приложения примитивные, возможно однопользовательские. Тогда действительно можно репозитариями пользоваться.
V>Это уже вторая попытка померяться длиной, видимо, записей в БД. Компенсируешь анатомические особенности?
Конечно. Но это не значит, что у тебе не примитивные приложения или вообще есть реальный опыт того, о чем ты пытаешься рассказать. Ты даже на банальные вопросы о репозитории ответить не можешь.
Re[19]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 28.06.16 12:47
Оценка: :)
Здравствуйте, Vladek, Вы писали:

V>Здравствуйте, gandjustas, Вы писали:


V>>>Да, отполированные знания тонкостей работы какой-то БД важны, но БД — не более, чем деталь реализации.

G>>БД — серьезное архитектурное ограничение. Изменение БД может поменять архитектуру всей системы.

V>Надоело повторяться, уже понятно, что каждый останется при своём мнении.


Понимаешь в чем фишка, у меня есть реальный опыт, который подкрепляет мою позицию. А ты ссылаешься на чувака, чье достижение — публикация пары популярных книжек сомнительного содержания. Причем у мартина конкретики еще меньше, чем в местных обсуждениях.
Re[18]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 28.06.16 14:56
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Vladek, Вы писали:


V>>Из объекта предметной области надо извлечь данные — для передачи в хранилище. Хранилище на входе получает простые структуры данных DTO, а не объекты предметной области. Как осуществляется маппинг — скорее всего, объект предметной области может вернуть структуру с нужными данными.

G>Я перестаю понимать что ты пишешь. Приведи пример кода репозитория, который list<T> использует.

Репозиторий использует хранилище для извлечения данных и, например, фабрику для создания объектов предметной области на основе этих данных. Хранилище и репозиторий — тут разные сущности. Трудно представить обёртку над List<T> в качестве хранилища?

V>>Ты тоже выдаёшь мне предположения про ужасы работы с БД, как и другой человек постом выше. Там я уже ответил про это: http://rsdn.ru/forum/design/6484598.1
Автор: Vladek
Дата: 28.06.16

G>И ты тоже пытаешься уйти от ответа.

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

Проблемы с хитроумными запросами к БД у меня много времени не занимали, было достаточно подумать и/или погуглить (намного чаще). Поэтому мне пофиг на локальные технические затруднения, мой опыт говорит об успешном их преодолении, БД там или веб-сервисы. Я тоже могу чемпионить с конкретной проблемой, просто не люблю.

Ситуаций, когда простое добавление новой фичи заставляло меня ковыряться в разных частях программы и переписывать и перепроектировать большие куски кода, к сожалению, возникали намного чаще. Поэтому архитектура с высоты птичьего полёта интересует меня в первую очередь — это залог того, что в будущем, при развитии программы, она не рухнет под собственным весом. А проблемы отдельных методов, ой выполняющихся на 50 мс дольше, я успешно исправлю по ходу дела.

V>>>>Эффективный код в отношении изменения зарплаты сотрудников должен быть атомарным. Мы это достигаем за счёт аккумулирования всех изменений в одном вызове SaveChangesAsync. Будет он выполняться 50 мс или 10 секунд — дело десятое и легко решаемое, для задачи гораздо важнее гарантированное выполнение или чёткий откат в случае сбоя.

G>>>Прости, а как ты обеспечишь атомарность для:
G>>>1) Веб-сервиса, которому нужно PUT на каждый объект
V>>Будет определяться возможностями веб-сервиса. Либо отправлять записи по одной, либо скопом. Делать несколько попыток, применить circuit breaker.
G>В этом и был вопрос. Когда хранилища дают разные гарантии какой должен быть репозитарий? Сможет ли он обеспечить эффективно все сценарии работы?
G>Ты сказал что SaveChangesAsync должен быть атомарным, но веб-сервис не поддерживает атомарного обновления множества записей. Что ты будешь делать в этом случае?

V>>Это определяется требованиями к конкретной программе, а ты меня постоянно пытаешься перетянуть на обсуждение технических деталей реализации примера. Это имеет слабое отношение к архитектуре, отполированные запросы к БД и веб-сервисам мало помогут при невнятной архитектуре. Программа превратится в несопровождаемое нечто и обузу для пользователей. Архитектура про изоляцию технических деталей (типа запросов выше) и управление зависимостями между отдельными частями программы.

G>Ты реально считаешь, что архитектура не определяется требованиям к конкретной программе? Тогда, прости, чем она определяется?
G>И я тебя спрашиваю не про детали, а про процесс проектирования. Ты говоришь что репозиторий должен обладать свойствами, которыми не обладает хранилище — какие твои действия?

Репозитарий использует хранилище. Его задача отбирать множество объектов предметной области по какому-то критерию. Из хранилища он берёт данные, на основе этих данных строит объекты предметной области. Тут тебе рисуется страшная картинка про медленный запрос к БД из твоего великого прошлого (100 гигов данных, 1000 пользователей) и ты пугаешься.

Страшно использовать репозитории — не используй.

G>>>2) List<T> в памяти?

V>>Ну тут просто и сбоев не будет.
G>>>Или у тебя контракт репозитария меняется в зависимости от реализации?
V>>Задача репозитория — выбрать нужное множество сотрудников по какому-то критерию, он не отвечает за изменения их данных.
G>Еще раз спрашиваю — как ты добьёшься атомарности SaveChangesAsync для хранилища в виде List<T>?

Что сложного в заполнении List<T> данными? Сбои практически исключены. Можно просто считать все операции атомарными.

G>>>>>Не знаю как ты, а я все это уже проходил. За каждым из приведенных выше вопросов стоит косяк в реальной программе, который привел к реальным потерям.

V>>>>С изменением требований я сталкиваюсь постоянно. Сегодня надо работать с таким источником данных, завтра с ещё одним. Использую описанный подход и не имею особых проблем.
G>>>Это значит что у тебя приложения примитивные, возможно однопользовательские. Тогда действительно можно репозитариями пользоваться.
V>>Это уже вторая попытка померяться длиной, видимо, записей в БД. Компенсируешь анатомические особенности?
G>Конечно. Но это не значит, что у тебе не примитивные приложения или вообще есть реальный опыт того, о чем ты пытаешься рассказать. Ты даже на банальные вопросы о репозитории ответить не можешь.

У тебя великое прошлое с кучей успешно преодолённых косяков, но внятного описания твоих текущих взглядов на архитектуру не видно. Этот не прав, тот не прав, третий вообще дурак без проектов, вот это приведёт к огромным потерям, и так далее. Удачи тогда, чемпион.
Re[19]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 28.06.16 16:30
Оценка:
Здравствуйте, Vladek, Вы писали:


V>Репозиторий использует хранилище для извлечения данных и, например, фабрику для создания объектов предметной области на основе этих данных. Хранилище и репозиторий — тут разные сущности. Трудно представить обёртку над List<T> в качестве хранилища?

Мне трудно представить себе обертку, которая обладает свойствами, о которых ты пишешь.

V>>>Ты тоже выдаёшь мне предположения про ужасы работы с БД, как и другой человек постом выше. Там я уже ответил про это: http://rsdn.ru/forum/design/6484598.1
Автор: Vladek
Дата: 28.06.16

G>>И ты тоже пытаешься уйти от ответа.

V>Я пишу тут "для поисковика" и не для спора с категоричными оппонентами. Пытаюсь дать направление для дальнейшего размышления тем, кто тут потом будет разбираться с проблемой репозиториев. Я имел такие вопросы раньше, на данный момент мне ближе всего подход Роберта Мартина к архитектуре.

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




V>Ситуаций, когда простое добавление новой фичи заставляло меня ковыряться в разных частях программы и переписывать и перепроектировать большие куски кода, к сожалению, возникали намного чаще. Поэтому архитектура с высоты птичьего полёта интересует меня в первую очередь — это залог того, что в будущем, при развитии программы, она не рухнет под собственным весом. А проблемы отдельных методов, ой выполняющихся на 50 мс дольше, я успешно исправлю по ходу дела.

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

V>Репозитарий использует хранилище. Его задача отбирать множество объектов предметной области по какому-то критерию. Из хранилища он берёт данные, на основе этих данных строит объекты предметной области. Тут тебе рисуется страшная картинка про медленный запрос к БД из твоего великого прошлого (100 гигов данных, 1000 пользователей) и ты пугаешься.

Не уходи от вопросов:
1) Ты сказал что SaveChangesAsync должен быть атомарным, но веб-сервис не поддерживает атомарного обновления множества записей. Что ты будешь делать в этом случае?
2) Ты реально считаешь, что архитектура не определяется требованиям к конкретной программе? Тогда, прости, чем она определяется?
3) Если твой репозиторий обладает свойствами, которыми не обладает хранилище — какие твои действия?


V>Страшно использовать репозитории — не используй.

Это не страшно, это бессмысленно.

V>Что сложного в заполнении List<T> данными? Сбои практически исключены. Можно просто считать все операции атомарными.

Еще раз спрашиваю — как ты добьёшься атомарности SaveChangesAsync для хранилища в виде List<T>?
Приведи код SaveChangesAsync для хранилища в виде List<T>.
Проблема-то не в сбоях, а в том, что ты можешь ограничения модели нарушить, типа уникальности.
Re[17]: Про путаницу с репозиториями и DAO
От: Sinclair Россия https://github.com/evilguest/
Дата: 28.06.16 16:50
Оценка: 6 (1) +2
Здравствуйте, Vladek, Вы писали:

V>Это аргументы в стиле подкладывания рельсы японской пиле.

Да нет, это результаты многолетнего негативного опыта. Когда пять лет трахаешься вот с такой вот реальностью, и уже думаешь что другого выхода и нет, и что 70% роллбэков из-за conflicting change — это нормально. Или что апгрейд с одной версии на другую так и должен идти 30 часов, "а что ж вы хотели — там данных два гигабайта!".
Потом после волшебных пенделей выясняется, что тот же самый апгрейд можно написать в виде пары sql-стейтментов, и он пройдёт на тех же данных за 10 минут.
И вот когда выясняется, что можно писать вот такие эффективные батчи, и при этом не надо отказываться от логики на нормальном современном ЯП, то наступает прямо-таки эйфория.

V> Да мало ли какую страшную картинку можно нарисовать, какое отношение работа с БД имеет к внятной архитектуре?

Прямое. Если у вас архитектура построена на lazy load и change tracking, то ваше приложение — гарантированный тормоз.

V>А что, если изменение зарплат сотрудников на выходе имеет не SQL-запрос, а документ на подпись начальнику?

Главное, чтобы для формирования этого документа не надо было просасывать всю базу в память клиента по тонкому каналу в открытой транзакции.
V>А потом подписанный документ с штрих-кодом сканируется и выполняется нужное действие, опять же не через SQL, а запросом в облако к нереляционному хранилищу?
А, ну вот типичный случай продажи нереляционного хранилища тем, кто так и не научился работать с реляционным хранилищем.
Потому что если после сканирования документа мы опять начинаем грузить в память список сотрудников, потом lazy load на их контракты, и потом опять 200 отдельных update стейтментов в реляционке — это умрёт.

V>Да, отполированные знания тонкостей работы какой-то БД важны, но БД — не более, чем деталь реализации.

Вам выражение "дырявые абстракции" что-нибудь говорит?
Архитекторы, которые считают существенные подробности несущественными, обречены писать плохие решения.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[20]: Про путаницу с репозиториями и DAO
От: Sinclair Россия https://github.com/evilguest/
Дата: 28.06.16 16:55
Оценка:
Здравствуйте, gandjustas, Вы писали:
V>>Что сложного в заполнении List<T> данными? Сбои практически исключены. Можно просто считать все операции атомарными.
G>Еще раз спрашиваю — как ты добьёшься атомарности SaveChangesAsync для хранилища в виде List<T>?
Да очень просто. Он склонирует лист, применит к нему изменения, а при успехе заменит _currentList = newList.
При сбое newList просто соберётся сборщиком мусора. Вот тебе и вся атомарность
Вон, говорят во всяких монгах используется единственный мутекс уровня базы для обеспечения атомарности — и ничо, не жужжат
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[21]: Про путаницу с репозиториями и DAO
От: Sinclair Россия https://github.com/evilguest/
Дата: 28.06.16 17:01
Оценка:
Здравствуйте, Vladek, Вы писали:

V>Задача внятной архитектуры — надёжно изолировать технические детали от других частей программы, чтобы впоследствии эти детали можно было независимо менять. Разделяй и властвуй.

Ну так вот выбор правильной гранулярности этой изоляции и определяет все дальнейшие успехи.
Иначе оказывается, что разработчики из кожи вон лезут, чтобы эту изоляцию обойти — т.к. в реальных сценариях выбранная архитектором-астронавтом изоляция не даёт ничего сделать.
Я могу прямо тут накидать примеров, где произвольно проведённая граница изоляции убъёт систему. Вот вы там упоминали про то, что "у веб сервиса как правило есть метод поиска".
Ну так это же и есть Архитектура! Если вы забыли приделать к сервису метод поиска — то всё, туши свет, сливай воду.
А если в методе поиска нет возможности постраничного отъёма, то наоборот — жги газеты, лей из шланга. Потому что эти несущественные подробности на определённых объёмах приводят к тому, что ваша транзакция, обязанная по ТЗ быть атомарной, не заканчивается никогда — не хватает стабильности соединения между компонентами, чтобы вычитать все полтора миллиона billing detail records в один присест.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[21]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 28.06.16 17:27
Оценка:
Здравствуйте, Sinclair, Вы писали:

G>>Еще раз спрашиваю — как ты добьёшься атомарности SaveChangesAsync для хранилища в виде List<T>?

S>Да очень просто. Он склонирует лист, применит к нему изменения, а при успехе заменит _currentList = newList.
S>При сбое newList просто соберётся сборщиком мусора. Вот тебе и вся атомарность
S>Вон, говорят во всяких монгах используется единственный мутекс уровня базы для обеспечения атомарности — и ничо, не жужжат

Я придумал optimistic locking через Interlocked.CompareExchange.
Но всетаки хотел, чтобы собеседник свой вариант озвучил, интересно было бы посмотреть на реализацию repository, который обладает всеми заявленными свойствами.
Re[18]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 28.06.16 18:20
Оценка:
Здравствуйте, Sinclair, Вы писали:

V>> Да мало ли какую страшную картинку можно нарисовать, какое отношение работа с БД имеет к внятной архитектуре?

S>Прямое. Если у вас архитектура построена на lazy load и change tracking, то ваше приложение — гарантированный тормоз.

На этом построена архитектура ORM — приложение от него вообще не должно зависеть, оно должно его использовать. Грубо говоря, классы для ORM должны лежать в отдельной сборке с атрибутом internal.

V>>Да, отполированные знания тонкостей работы какой-то БД важны, но БД — не более, чем деталь реализации.

S>Вам выражение "дырявые абстракции" что-нибудь говорит?
S>Архитекторы, которые считают существенные подробности несущественными, обречены писать плохие решения.

А может вам стать администраторами баз данных — там SQL, батчи, скорость, ветер, свист в ушах, а? Никаких репозиториев, классов, абстракций.
Re[22]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 28.06.16 18:24
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Vladek, Вы писали:


V>>Задача внятной архитектуры — надёжно изолировать технические детали от других частей программы, чтобы впоследствии эти детали можно было независимо менять. Разделяй и властвуй.

S>Ну так вот выбор правильной гранулярности этой изоляции и определяет все дальнейшие успехи.
S>Иначе оказывается, что разработчики из кожи вон лезут, чтобы эту изоляцию обойти — т.к. в реальных сценариях выбранная архитектором-астронавтом изоляция не даёт ничего сделать.
S>Я могу прямо тут накидать примеров, где произвольно проведённая граница изоляции убъёт систему. Вот вы там упоминали про то, что "у веб сервиса как правило есть метод поиска".
S>Ну так это же и есть Архитектура! Если вы забыли приделать к сервису метод поиска — то всё, туши свет, сливай воду.
S>А если в методе поиска нет возможности постраничного отъёма, то наоборот — жги газеты, лей из шланга. Потому что эти несущественные подробности на определённых объёмах приводят к тому, что ваша транзакция, обязанная по ТЗ быть атомарной, не заканчивается никогда — не хватает стабильности соединения между компонентами, чтобы вычитать все полтора миллиона billing detail records в один присест.



Если тебе нужна быстрая выборка из бд — ты берёшь и придумываешь для этого внятную модель, и тут же реализовываешь её за 5 минут. Зачем тебе пробуждать всю модель предметной области для простого чтения?
Re[23]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 28.06.16 18:55
Оценка:
Здравствуйте, Vladek, Вы писали:

V>Если тебе нужна быстрая выборка из бд — ты берёшь и придумываешь для этого внятную модель, и тут же реализовываешь её за 5 минут. Зачем тебе пробуждать всю модель предметной области для простого чтения?


А зачем придумывать? Она уже есть — модель базы данных, которая генерируется из таблиц и связей или наоборот набор классов, из которых создается БД.
Простое чтение — 98% работы приложения, непонятно зачем для остальных 2% еще какая-то модель нужна.
Re[24]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 28.06.16 20:32
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Vladek, Вы писали:


V>>Если тебе нужна быстрая выборка из бд — ты берёшь и придумываешь для этого внятную модель, и тут же реализовываешь её за 5 минут. Зачем тебе пробуждать всю модель предметной области для простого чтения?


G>А зачем придумывать? Она уже есть — модель базы данных, которая генерируется из таблиц и связей или наоборот набор классов, из которых создается БД.

G>Простое чтение — 98% работы приложения, непонятно зачем для остальных 2% еще какая-то модель нужна.

Вот пример:

https://www.youtube.com/watch?v=uyfeERk3Ta4
Re[20]: Про путаницу с репозиториями и DAO
От: Vladek Россия Github
Дата: 28.06.16 20:32
Оценка:
Здравствуйте, gandjustas, Вы писали:

V>>Я пишу тут "для поисковика" и не для спора с категоричными оппонентами. Пытаюсь дать направление для дальнейшего размышления тем, кто тут потом будет разбираться с проблемой репозиториев. Я имел такие вопросы раньше, на данный момент мне ближе всего подход Роберта Мартина к архитектуре.

G>У роберта мартина подхода практически нет. У него несколько идей и приемов, которым он даже не автор, внятно сформулировать не может, которые в некоторых контекстах банально ошибочные.

Можно почитать критику?

V>>Ситуаций, когда простое добавление новой фичи заставляло меня ковыряться в разных частях программы и переписывать и перепроектировать большие куски кода, к сожалению, возникали намного чаще. Поэтому архитектура с высоты птичьего полёта интересует меня в первую очередь — это залог того, что в будущем, при развитии программы, она не рухнет под собственным весом. А проблемы отдельных методов, ой выполняющихся на 50 мс дольше, я успешно исправлю по ходу дела.

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

Я не всегда пишу веб-морды к базам данных.

V>>Репозитарий использует хранилище. Его задача отбирать множество объектов предметной области по какому-то критерию. Из хранилища он берёт данные, на основе этих данных строит объекты предметной области. Тут тебе рисуется страшная картинка про медленный запрос к БД из твоего великого прошлого (100 гигов данных, 1000 пользователей) и ты пугаешься.

G>Не уходи от вопросов:
G>1) Ты сказал что SaveChangesAsync должен быть атомарным, но веб-сервис не поддерживает атомарного обновления множества записей. Что ты будешь делать в этом случае?

Атомарные операции — операции, выполняющиеся как единое целое, либо не выполняющиеся вовсе.

Буду обновлять записи по одной, если будет сбой — по одной верну записи к прежнему виду. Ага, теперь спроси меня про "быстродействие".

G>2) Ты реально считаешь, что архитектура не определяется требованиям к конкретной программе? Тогда, прости, чем она определяется?

Типом программы. Игры, демоны, веб-приложения, операционные системы, системы реального времени, и другие типы программ — все имеют разные подходы к построению архитектуры. Требованиями определяются отдельные конкретные части программы.
G>3) Если твой репозиторий обладает свойствами, которыми не обладает хранилище — какие твои действия?

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

V>>Страшно использовать репозитории — не используй.

G>Это не страшно, это бессмысленно.

Тогда что ты пытаешься сказать нового? Может у тебя приложения под высокой нагрузкой с кучей пользователей были простыми веб-мордами к базе данных и никакой модели данных там не требовалось? Тупо прочитать из БД, тупо записать с простенькой валидацией, никаких бизнес-правил помимо чтения и сохранения данных.

Я именно так проектировал читалку RSS или клиента к какому-нибудь сайту. Независимое чтение из бд для отображения на экране, независимая запись в бд при обновлении, никакой модели данных — только DTO, модели для записи и чтения данных. Предметная область не нужна и репозиториев соответственно нет, тем не менее, доступ к БД скрыт за интерфейсами хранилища, которое имеет на входе и выходе модели для записи и чтения.

V>>Что сложного в заполнении List<T> данными? Сбои практически исключены. Можно просто считать все операции атомарными.

G>Еще раз спрашиваю — как ты добьёшься атомарности SaveChangesAsync для хранилища в виде List<T>?
G>Приведи код SaveChangesAsync для хранилища в виде List<T>.

IEnumerable<EmployeeData> empDetails = employees.Select(e => e.ToDetails()).ToArray();
gateway.SaveEmployees(empDetails);


EmployeeData — содержит изменённые данные, предназначены для сохранения, простой DTO. Хранилище (gateway) на основе List<T> просто сравнит его со своим списком и заменит прежние версии. Новые записи, которых не окажется в списке — могут быть просто добавлены или расценены как сбой (тогда произойдёт откат, в список вернутся прежние версии).

G>Проблема-то не в сбоях, а в том, что ты можешь ограничения модели нарушить, типа уникальности.
Re[16]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 28.06.16 21:34
Оценка:
Здравствуйте, gandjustas, Вы писали:

_>>На мой взгляд, в юнит тестах вызов такого метода (сохраняющего в базу) проверять не надо.

G>Тогда ценность юнит-теста падает до нуля. Зеленый тест перестает показывать что метод работает.

Понятно.

JFYI
Дело не в цвете, а в том, что вы, т.о., по сути, проверяете внутренний алгоритм. Оптимальнее будет проверять сценарии работы. При этом допускается, что Х.З. как именно реализован метод объекта, сохраняющий данные (внутри он может дергать SaveChangesAsync, SaveChanges, или еще что-то). Юнит-тестами проверяется сценарий, интеграционными проверяется связка (что данные прошли и записались). Момент с разработчиками не выносит критики: если слишком долго, пилится на тесты "по-меньше" или CI.
Отредактировано 28.06.2016 21:38 another_coder . Предыдущая версия .
Re[17]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 28.06.16 23:44
Оценка: +1
Здравствуйте, another_coder, Вы писали:

_>Дело не в цвете, а в том, что вы, т.о., по сути, проверяете внутренний алгоритм. Оптимальнее будет проверять сценарии работы. При этом допускается, что Х.З. как именно реализован метод объекта, сохраняющий данные (внутри он может дергать SaveChangesAsync, SaveChanges, или еще что-то). Юнит-тестами проверяется сценарий, интеграционными проверяется связка (что данные прошли и записались). Момент с разработчиками не выносит критики: если слишком долго, пилится на тесты "по-меньше" или CI.


Это все теория.

Вот у тебя есть метод — делает выборку, обрабатывает, сохраняет. Это твой сценарий.
Для теста ты репозиторий подменяешь банальной реализацией на list<t>, когда метод просто отдает список. И проверяешь что данные в списке поменялись.
Ты написал код, который делает обработку, а SaveChangesAsync забыл. Тест проходит. При запуске не работает, тупо ничего не происходит.

У тебя два варианта:
1) Делать реальный транзакционный inmemory storage. Но я таких не видел за 10 лет (слава богу в EFCore его сделали).
2) Проверять integration-тестом, но с ними проблем еще больше — медленно, тестовые данные нужны, писать тесты сложнее.

На практике получается еще хуже.
У меня был случай, когда программа уже была написана и покрыта юнит-тестами. Покрытие методов БЛ было 100%. И тут провели рефакторинг, в процессе случайно выпилили SaveChanges в нескольких методах. Ничего не упало, ни тесты, ни код, но приложение перестало работать. Интеграционных тестов не было ибо юнит тесты покрывают 100% и так.

После этого я бросил писать юнит-тесты для бизнес-приложений. Интеграционные тесты делаю если руками проверять дольше. Поэтому отпала потребность подменять хранилище, и стал не нужен паттерн repository.
Re[19]: Про путаницу с репозиториями и DAO
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.06.16 04:37
Оценка:
Здравствуйте, Vladek, Вы писали:

V>На этом построена архитектура ORM — приложение от него вообще не должно зависеть, оно должно его использовать. Грубо говоря, классы для ORM должны лежать в отдельной сборке с атрибутом internal.

Это опасное заблуждение. Есть такая порода архитекторов, которая искренне полагает, что можно придумать такую архитектуру приложения, которая позволяет хранить данные сегодня в одном XML-файле, завтра — в SQL-ной RDBMS, а послезавтра — в cloud storage в стиле S3. И типа всё, что надо будет для этого сделать — подправить config.xml.
Увы, устройство хранилища невозможно игнорировать. Точно так же, как не получится запустить SQL Server поверх ленточного стримера вместо блочного устройства.
Несмотря на то, что в обоих случаях работа идёт через одну и ту же абстракцию "файл".

V>А может вам стать администраторами баз данных — там SQL, батчи, скорость, ветер, свист в ушах, а? Никаких репозиториев, классов, абстракций.

С чего бы это? Я вам пытаюсь объяснить, что архитектор обязан не только парить в эмпиреях, но и учитывать существенные свойства компонентов системы.
Вот, например, лет двадцать тому назад бытовало мнение, что RPC — это круто. Типа давайте пренебрежём расположением вызываемого компонента. Бёрем, сериализуем сообщение, десериализуем ответ — и вуаля!
Объекты в соседнем процессе так же доступны, как и объекты на другой стороне интернета, и оба доступны так же, как и объекты в нашем собственном адресном пространстве.
А потом внезапно оказывается, что этим пренебрегать никак не получается — потому что объект в нашем адресном пространстве намного доступнее. И приложения, спроектированные без учёта этих особенностей, просто не живут в реальном мире.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[23]: Про путаницу с репозиториями и DAO
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.06.16 04:39
Оценка: +1
Здравствуйте, Vladek, Вы писали:

V>Если тебе нужна быстрая выборка из бд — ты берёшь и придумываешь для этого внятную модель, и тут же реализовываешь её за 5 минут. Зачем тебе пробуждать всю модель предметной области для простого чтения?

Проблема в том, что "быстрая выборка из БД" работает совсем не так, как "быстрая выборка из локального файла", и не совсем так, как "быстрая выборка из удалённого веб-сервиса".
И это такая забавная деталь реализации, которой невозможно пренебрегать при построении модели.
Чем именно по-вашему занимается архитектор? Как он собирается что-то проектировать в иллюзии что "всё что угодно можно заменить чем угодно"?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[20]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 29.06.16 04:50
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Vladek, Вы писали:


V>>На этом построена архитектура ORM — приложение от него вообще не должно зависеть, оно должно его использовать. Грубо говоря, классы для ORM должны лежать в отдельной сборке с атрибутом internal.

S>Это опасное заблуждение. Есть такая порода архитекторов, которая искренне полагает, что можно придумать такую архитектуру приложения, которая позволяет хранить данные сегодня в одном XML-файле, завтра — в SQL-ной RDBMS, а послезавтра — в cloud storage в стиле S3. И типа всё, что надо будет для этого сделать — подправить config.xml.
S>Увы, устройство хранилища невозможно игнорировать. Точно так же, как не получится запустить SQL Server поверх ленточного стримера вместо блочного устройства.
S>Несмотря на то, что в обоих случаях работа идёт через одну и ту же абстракцию "файл".

V>>А может вам стать администраторами баз данных — там SQL, батчи, скорость, ветер, свист в ушах, а? Никаких репозиториев, классов, абстракций.

S>С чего бы это? Я вам пытаюсь объяснить, что архитектор обязан не только парить в эмпиреях, но и учитывать существенные свойства компонентов системы.
S>Вот, например, лет двадцать тому назад бытовало мнение, что RPC — это круто. Типа давайте пренебрежём расположением вызываемого компонента. Бёрем, сериализуем сообщение, десериализуем ответ — и вуаля!
S>Объекты в соседнем процессе так же доступны, как и объекты на другой стороне интернета, и оба доступны так же, как и объекты в нашем собственном адресном пространстве.
S>А потом внезапно оказывается, что этим пренебрегать никак не получается — потому что объект в нашем адресном пространстве намного доступнее. И приложения, спроектированные без учёта этих особенностей, просто не живут в реальном мире.

Думаю, что предполагать о заменяемости типа хранилища сейчас моветон. А вот об изолированности от него во время тестов — это то, что нужно. Совсем отрицать, что "ORM должны лежать в отдельной сборке с атрибутом internal." так же глупо, как и думать, что все абсолютно можно спрятать за ширмой абстракции. Но важно подразумевать, что в один момент кто-то захочет переписать и выкинуть ваш кусок кода и это должно быть легко и просто.
Re[21]: Про путаницу с репозиториями и DAO
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.06.16 04:54
Оценка:
Здравствуйте, Vladek, Вы писали:

V>Я честно не понимаю этого вопроса. Код не высекается в граните, его можно править. Если твой веб-сервис чего-то не может, поправь его. То же относится и к репозиторию, и к хранилищу. Хорошая архитектура обеспечивает лёгкое редактирование.

Под "лёгкостью редактирования" обычно понимают возможность локализовать исправления. Вот эта идея "поправить веб-сервис" уже показывает, что реализация нужного требования затрагивает как минимум два места — сервис и его клиента. В чём легкость редактирования, если вам приходится совместно править компоненты? В чём суть архитектуры, если вы меняете её на ходу по каждому чиху?


V>
V>IEnumerable<EmployeeData> empDetails = employees.Select(e => e.ToDetails()).ToArray();
V>gateway.SaveEmployees(empDetails);
V>


V>EmployeeData — содержит изменённые данные, предназначены для сохранения, простой DTO. Хранилище (gateway) на основе List<T> просто сравнит его со своим списком и заменит прежние версии. Новые записи, которых не окажется в списке — могут быть просто добавлены или расценены как сбой (тогда произойдёт откат, в список вернутся прежние версии).


Отлично. Мы видим, что архитектор учёл при проектировании протокола тот факт, что бывают пакетные изменения. Это позволяет ему при реализации gateway обеспечить приемлемые нефункциональные характеристики независимо от расположения хранилища. Хорошо, что вы сразу пропустили первый шаг, который обычно выполняют неопытные архитекторы — gateway.SaveEmployee(EmployeeData e).
За это вы получаете 4 балла. 5 баллов вам не дадут потому, что SaveEmployees имеет скрытое требование в контракте — формально запрошен IEnumerable, а на самом деле ему нужен конечный список. Автор кода клиента об этом знал, потому что написал .ToArray(). Если он забудет написать ToArray(), то последствия могут быть непредсказуемыми. Я уж не говорю про
private IEnumerable<EmployeeData> feedUp()
{
  while(true) yield return new EmployeeData();
}

...

gateway.SaveEmployees(feedUp()); // MWA-HA-HAA!


Ок, усложним задачу — нам нужно назначить Васю на должность начальника отдела. Атомарно изменяем Position у Employee, который представляет Васю, и Head у Department, который представляет транспортный отдел.
Как нам реализовать это, оставаясь в рамках вашей чудесной модели gateway? По-прежнему помним, что gateway мы родили ровно для того, чтобы "бизнес-код" не знал, как устроено хранение данных. То есть есть хороший шанс, что у нас есть две (или больше) реализации интерфейса IGateway().
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[18]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 29.06.16 05:11
Оценка:
Здравствуйте, gandjustas.

Вы делаете вывод о нужности юнит-тестов, руководствуясь частным случаем и основываясь на последствиях. Конечно, практика вносит свои корректировки: непрофессионализм девелоперов, ошибки проектирования и пр., которые следует учитывать. Но то кол-во случаев на каждом IF или SWITСH, которое можно проверить unit-tests вы никогда не проверите интеграционными. Либо это превратится в такой головняк с интеграционными, когда написание тестов жрет не меньше временеи, чем сам функционал.
То о чем вы говорите стандартные заблуждения. Если бы были интеграционные тесты, то ошибки были бы замечены. Но какой-то человек решил их не делать. А вывод, почему-то, делается не о его профессионализме, а о ненужности unit-test, которые свою работу сделали на отлично, как мне кажется. Как долго вы искали проблему?

G>Вот у тебя есть метод — делает выборку, обрабатывает, сохраняет. Это твой сценарий.

G>Для теста ты репозиторий подменяешь банальной реализацией на list<t>, когда метод просто отдает список. И проверяешь что данные в списке поменялись.
G>Ты написал код, который делает обработку, а SaveChangesAsync забыл. Тест проходит. При запуске не работает, тупо ничего не происходит.

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

G>У тебя два варианта:

G>1) Делать реальный транзакционный inmemory storage. Но я таких не видел за 10 лет (слава богу в EFCore его сделали).

Сделать можно, но не надо.

G>2) Проверять integration-тестом, но с ними проблем еще больше — медленно, тестовые данные нужны, писать тесты сложнее.

Корень проблемы не в интеграционных тестах, а в людях, которые не умеют их писать. Как обычно, впрочем. Скорее всего, описанные тесты у вас больше похожи на end-to-end тесты, когда вы запускаете целиков всё (ВСЁ). Такие действильно очень тяжелые во всех отношениях. Тут необходимо помнить, что задача тестов (любых): проверить работу известного сценария. Приэтом, юнит — алгоритмы проверяет, а интеграционный только связку. Если перемешивать, то возникают проблемы.

G>На практике получается еще хуже.

G>У меня был случай, когда программа уже была написана и покрыта юнит-тестами. Покрытие методов БЛ было 100%. И тут провели рефакторинг, в процессе случайно выпилили SaveChanges в нескольких методах. Ничего не упало, ни тесты, ни код, но приложение перестало работать. Интеграционных тестов не было ибо юнит тесты покрывают 100% и так.

G>После этого я бросил писать юнит-тесты для бизнес-приложений. Интеграционные тесты делаю если руками проверять дольше. Поэтому отпала потребность подменять хранилище, и стал не нужен паттерн repository.

Сколько людей на проекте?? Один-два?.. Риторический вопрос.
Re[21]: Про путаницу с репозиториями и DAO
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.06.16 05:22
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Думаю, что предполагать о заменяемости типа хранилища сейчас моветон. А вот об изолированности от него во время тестов — это то, что нужно. Совсем отрицать, что "ORM должны лежать в отдельной сборке с атрибутом internal." так же глупо, как и думать, что все абсолютно можно спрятать за ширмой абстракции. Но важно подразумевать, что в один момент кто-то захочет переписать и выкинуть ваш кусок кода и это должно быть легко и просто.

Ок, давайте разбираться. У нас есть совершенно разные требования, типа
— возможность заменить природу хранилища (не нужно)
— тестируемость (требует исследования, т.к. для разных видов тестов нужно изолировать разные компоненты)
— возможность локализовать изменения кода при изменении требований (у нас должно быть представление о том, какого рода ожидаются изменения)
По факту получается, что самое важное требование — последнее. И разные архитектуры по-разному его поддерживают. В частности, всякие table gateway, репозитории, и хранимки вида sp_AddProduct, sp_GetProduct, sp_DeleteProduct, sp_UpdateProduct — это просто мусор, который осложняет процесс разработки, не давая никаких преимуществ.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[22]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 29.06.16 05:31
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, another_coder, Вы писали:


_>>Думаю, что предполагать о заменяемости типа хранилища сейчас моветон. А вот об изолированности от него во время тестов — это то, что нужно. Совсем отрицать, что "ORM должны лежать в отдельной сборке с атрибутом internal." так же глупо, как и думать, что все абсолютно можно спрятать за ширмой абстракции. Но важно подразумевать, что в один момент кто-то захочет переписать и выкинуть ваш кусок кода и это должно быть легко и просто.

S>Ок, давайте разбираться. У нас есть совершенно разные требования, типа
S>- возможность заменить природу хранилища (не нужно)
S>- тестируемость (требует исследования, т.к. для разных видов тестов нужно изолировать разные компоненты)
S>- возможность локализовать изменения кода при изменении требований (у нас должно быть представление о том, какого рода ожидаются изменения)
S>По факту получается, что самое важное требование — последнее. И разные архитектуры по-разному его поддерживают. В частности, всякие table gateway, репозитории, и хранимки вида sp_AddProduct, sp_GetProduct, sp_DeleteProduct, sp_UpdateProduct — это просто мусор, который осложняет процесс разработки, не давая никаких преимуществ.

Мне не понятны требования. Они слишком общие. Хотя бы надо или нет заменить хранилище? Полагаю, что если вы будете придерживаться SOLID при дальнейшем проектировании и рефакторинге, то проблемы постепенно уменьшатся, т.к. вы получите изолированность, и, как следствие, возможность заменить хранилище, тестировать.
Опять же, стоит четко понимать зачем тесты и какие в каждом случае необходимы и достаточны.
Отредактировано 29.06.2016 5:32 another_coder . Предыдущая версия . Еще …
Отредактировано 29.06.2016 5:31 another_coder . Предыдущая версия .
Re[18]: Про путаницу с репозиториями и DAO
От: Gattaka Россия  
Дата: 29.06.16 06:44
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>2) Проверять integration-тестом, но с ними проблем еще больше — медленно, тестовые данные нужны, писать тесты сложнее.

Ну писать то их наоборот легче. Вы берете класс фасада пихаете ему параметры, там внутри куча всего происходит, проверяете. Нет моков, которые сковывают рефакторинг и которые еще писать надо.
Вот что сложнее, так это понять что конкретно сломалось.
Re[19]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 29.06.16 06:55
Оценка:
Здравствуйте, Gattaka, Вы писали:

G>Здравствуйте, gandjustas, Вы писали:


G>>2) Проверять integration-тестом, но с ними проблем еще больше — медленно, тестовые данные нужны, писать тесты сложнее.

G>Ну писать то их наоборот легче. Вы берете класс фасада пихаете ему параметры, там внутри куча всего происходит, проверяете. Нет моков, которые сковывают рефакторинг и которые еще писать надо.
G>Вот что сложнее, так это понять что конкретно сломалось.

Хотя и в теории, но если написано все нормально... При зеленых юнит-тестах, но красном интеграционном, должно быть на 100% понятно где и что упало.
Re[23]: Про путаницу с репозиториями и DAO
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.06.16 07:17
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Мне не понятны требования. Они слишком общие. Хотя бы надо или нет заменить хранилище?

На мой взгляд, это требование а) ненужно б) невозможно обеспечить.

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

Придерживаться можно чего угодно, хоть кашрута, хоть шариата. Но я пока не видел такой абстракции "хранилища", чтобы она подходила к чему угодно — от файла на ленте до NoSQL key-value хранилищ.
_>Опять же, стоит четко понимать зачем тесты и какие в каждом случае необходимы и достаточны.
Воот.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[19]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 29.06.16 08:24
Оценка:
Здравствуйте, Gattaka, Вы писали:

G>Здравствуйте, gandjustas, Вы писали:


G>>2) Проверять integration-тестом, но с ними проблем еще больше — медленно, тестовые данные нужны, писать тесты сложнее.

G>Ну писать то их наоборот легче. Вы берете класс фасада пихаете ему параметры, там внутри куча всего происходит, проверяете. Нет моков, которые сковывают рефакторинг и которые еще писать надо.
G>Вот что сложнее, так это понять что конкретно сломалось.

Хм... а ты писал такие тесты? В тестах 80% работы — подготовка, 10% исполнение и 10% проверка. Когда гоняешь тесты на реальной базе есть одна маленькая проблема — база хранит состояние. А для разных тестов нужны разные данные. Записывать эталонные данные каждый раз — код тестов распухает в разы, не записывать — тесты начинают зависеть от порядка запуска.
Re[20]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 29.06.16 08:24
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Хотя и в теории, но если написано все нормально... При зеленых юнит-тестах, но красном интеграционном, должно быть на 100% понятно где и что упало.


И зачем тогда нужны юнит-тесты, если в реальности проверка делается только интеграционным?
Re[20]: Про путаницу с репозиториями и DAO
От: Gattaka Россия  
Дата: 29.06.16 08:40
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Хм... а ты писал такие тесты? В тестах 80% работы — подготовка, 10% исполнение и 10% проверка. Когда гоняешь тесты на реальной базе есть одна маленькая проблема — база хранит состояние. А для разных тестов нужны разные данные. Записывать эталонные данные каждый раз — код тестов распухает в разы, не записывать — тесты начинают зависеть от порядка запуска.


Перед запуском теста база чистится. Создается все заново, запускается кейс. Для создания базы есть что-то вроде мастеров создания, код создания базы у большинства тестов одинаковый, различается незначительно. Либо более радикальное решение, вы для теста храните бекап базы. Это если у вас миграция версий настроена...
Писать как раз таки очень легко, отлаживать тяжело и понимать что конкретно упало.
Из минусов еще — работает медленно, но т.к. запускается в фоне не критично.
Re[19]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 29.06.16 08:51
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Здравствуйте, gandjustas.


_>Вы делаете вывод о нужности юнит-тестов, руководствуясь частным случаем и основываясь на последствиях. Конечно, практика вносит свои корректировки: непрофессионализм девелоперов, ошибки проектирования и пр., которые следует учитывать. Но то кол-во случаев на каждом IF или SWITСH, которое можно проверить unit-tests вы никогда не проверите интеграционными. Либо это превратится в такой головняк с интеграционными, когда написание тестов жрет не меньше временеи, чем сам функционал.

Ты подменяешь понятия. Если можно написать юнит тест, это вовсе не означает, что им можно что-то проверить. Я несколько раз видел программы с тестовым покрытием близким к 100%, естественно юнит-тестами. В них были баги в огромном количестве.

_>То о чем вы говорите стандартные заблуждения. Если бы были интеграционные тесты, то ошибки были бы замечены. Но какой-то человек решил их не делать. А вывод, почему-то, делается не о его профессионализме, а о ненужности unit-test, которые свою работу сделали на отлично, как мне кажется. Как долго вы искали проблему?

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

G>>Вот у тебя есть метод — делает выборку, обрабатывает, сохраняет. Это твой сценарий.

G>>Для теста ты репозиторий подменяешь банальной реализацией на list<t>, когда метод просто отдает список. И проверяешь что данные в списке поменялись.
G>>Ты написал код, который делает обработку, а SaveChangesAsync забыл. Тест проходит. При запуске не работает, тупо ничего не происходит.

_>Я бы распилил на несколько методов, которые по-отдельности можно протестировать. Т.е. выборка проверялась бы интеграционным тестом, обработка — юнит тестом, сохранение — интеграционным. При этом в юнит тесте выборку и сохранение я бы замокал.

По-моему ничего бы не изменилось.

Код был примерно такой:
void F(Repo repo)
{
    var xs = repo.GetXsByXXX();
    foreach(var x in xs)
    {
        x.y+=1;
    }
    // repo.SaveChanges(); // эту строку потеряли при рефакторинге
}

[Test]
void Test()
{
    var mock = new Mock();
    var xs = new [] { ... };
    mock.Xs = xs;
    F(mock);
    Assert.IsTrue(xs.All(...));
}


Тест зеленый, код не работает.

Ты предлагаешь покрыть GetXsByXXX интеграционным тестом, что не имеет смысла, там примитивный запрос. Покрыть SaveChanges интеграционным тестом, что тоже не имеет смылса и написать юнит-тест для F, что не дает фактически проверки.

G>>У тебя два варианта:

G>>1) Делать реальный транзакционный inmemory storage. Но я таких не видел за 10 лет (слава богу в EFCore его сделали).
_>Сделать можно, но не надо.
Когда есть готовый, то не надо. А раньше пытался такое избразить, но как-то дофига сложно получилось.

G>>2) Проверять integration-тестом, но с ними проблем еще больше — медленно, тестовые данные нужны, писать тесты сложнее.

_>Корень проблемы не в интеграционных тестах, а в людях, которые не умеют их писать. Как обычно, впрочем. Скорее всего, описанные тесты у вас больше похожи на end-to-end тесты, когда вы запускаете целиков всё (ВСЁ). Такие действильно очень тяжелые во всех отношениях. Тут необходимо помнить, что задача тестов (любых): проверить работу известного сценария. Приэтом, юнит — алгоритмы проверяет, а интеграционный только связку. Если перемешивать, то возникают проблемы.
Это словоблудие. Известный сценарий я привел, как его юнит-тестом проверить?

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

Я этой задачей троллю апологетов юнит-тестов.
Re[21]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 29.06.16 08:54
Оценка:
Здравствуйте, Gattaka, Вы писали:

G>Здравствуйте, gandjustas, Вы писали:


G>>Хм... а ты писал такие тесты? В тестах 80% работы — подготовка, 10% исполнение и 10% проверка. Когда гоняешь тесты на реальной базе есть одна маленькая проблема — база хранит состояние. А для разных тестов нужны разные данные. Записывать эталонные данные каждый раз — код тестов распухает в разы, не записывать — тесты начинают зависеть от порядка запуска.


G>Перед запуском теста база чистится. Создается все заново, запускается кейс.

Код тестов распухает в разы. Потому что сделать нормальные проверки на одних и тех же эталонных данных невозможно. Тебе как минимум надо проверять три случая — "0,1,n".
Ну или просто смириться, что покрытие тестами будет от силы 20%.

G>Для создания базы есть что-то вроде мастеров создания, код создания базы у большинства тестов одинаковый, различается незначительно. Либо более радикальное решение, вы для теста храните бекап базы. Это если у вас миграция версий настроена...

ОМГ
Re[12]: Про путаницу с репозиториями и DAO
От: Baudolino  
Дата: 29.06.16 10:00
Оценка: :)
Здравствуйте, IT, Вы писали:

IT>Это у кого как и смотря что мы под этим понимаем.

IT>Нет. Функция изоляции переехала в LINQ. А сам DAL больше нафиг не нужен.
Большие дяди живут в мире придуманных ими определений?

MSDN:

LINQ is a set of features that extends powerful query capabilities to the language syntax of C#. LINQ introduces standard, easily-learned patterns for querying and updating data, and the technology can be extended to support potentially any kind of data store. The .NET Framework includes LINQ provider assemblies that enable the use of LINQ with .NET Framework collections, SQL Server databases, ADO.NET Datasets, and XML documents.

Re[20]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 29.06.16 11:11
Оценка:
Здравствуйте, gandjustas.

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

Вы писали:

G>Ты подменяешь понятия. Если можно написать юнит тест, это вовсе не означает, что им можно что-то проверить. Я несколько раз видел программы с тестовым покрытием близким к 100%, естественно юнит-тестами. В них были баги в огромном количестве.


G>Если у бабушки кое-что было, то она была бы не совсем бабушкой. Не делали интеграционные тесты по причине, описанной тобой выше. Покрыть интеграционными тестами значимую часть программы с разумными затратами невозможно. А юнит тестами можно. Вот только юнит-тесты не проверяют.


Я не подменяю понятия. Если гнаться только за покрытие кода тестами, то понятно, почему юнит-тесты ничего не проверяют. UT должно быть легко выкинуть, переписать, написать с нуля и запустить. Покрытие — это очень косвенный показатель. Юнит тесты необходимо проверять так же, как и остальной код, чтобы не было написания тестов ради самих тестов. По сути, это спасательные якоря скалолаза (самого разработчика), если образно. Их может быть не много, но там где надо.

G>>>Вот у тебя есть метод — делает выборку, обрабатывает, сохраняет. Это твой сценарий.

G>>>Для теста ты репозиторий подменяешь банальной реализацией на list<t>, когда метод просто отдает список. И проверяешь что данные в списке поменялись.
G>>>Ты написал код, который делает обработку, а SaveChangesAsync забыл. Тест проходит. При запуске не работает, тупо ничего не происходит.

_>>Я бы распилил на несколько методов, которые по-отдельности можно протестировать. Т.е. выборка проверялась бы интеграционным тестом, обработка — юнит тестом, сохранение — интеграционным. При этом в юнит тесте выборку и сохранение я бы замокал.

G>По-моему ничего бы не изменилось.

G>Код был примерно такой:

G>
G>void F(Repo repo)
G>{
G>    var xs = repo.GetXsByXXX();
G>    foreach(var x in xs)
G>    {
G>        x.y+=1;
G>    }
G>    // repo.SaveChanges(); // эту строку потеряли при рефакторинге
G>}

G>[Test]
G>void Test()
G>{
G>    var mock = new Mock();
G>    var xs = new [] { ... };
G>    mock.Xs = xs;
G>    F(mock);
G>    Assert.IsTrue(xs.All(...));
G>}
G>


G>Тест зеленый, код не работает.


G>Ты предлагаешь покрыть GetXsByXXX интеграционным тестом, что не имеет смысла, там примитивный запрос. Покрыть SaveChanges интеграционным тестом, что тоже не имеет смылса и написать юнит-тест для F, что не дает фактически проверки.


Тут чтобы правильно ответить надо еще вопросов позадовать. Если интересно, то расскажите, в чем главная цель этого метода F (от этого зависит как он должен быть написан и какие тесты для него писать)? А так же интересно, ваш реп должен хранить состояние и беферизировать данные, или просто явлется прослойкой между ORM (EF, например) и BO?

G>У меня еще интереснее сценарий есть:

G>В приложении календарь. При создании нового события надо проверить, что событие не пересекается с существующим. Событий много, тянуть все в память нельзя, проверять надо запросом к базе. И важное условие — тебе повезло, ты не можешь использовать Linq, обязательно текстовые запросы.
G>Напиши юнит-тест для проверки.

Для проверки чего? Хранимки, динамического стейтмента, условий проверки? Описание не достаточно, чтобы нормально ответить вопрос.

G>Я этой задачей троллю апологетов юнит-тестов.


Можем попробовать по разбираться. Кому-то точно в + итоги будут. Если без попыток убедить и навязать свое мнение
Re[21]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 29.06.16 11:14
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, another_coder, Вы писали:


_>>Хотя и в теории, но если написано все нормально... При зеленых юнит-тестах, но красном интеграционном, должно быть на 100% понятно где и что упало.


G>И зачем тогда нужны юнит-тесты, если в реальности проверка делается только интеграционным?


Не только, а совокупностью. Т.е. например, ты знаешь, что метод Save у тебе не покрыт тестов. Случай, когда все юниты зеленые, но интеграционный нет говорит, в общем случае, о том, что логика правильна, а вот момент с записью не работает. А ведь может быть так, что интеграционный зеленый, а 5 из 20 юнит тестов красный (даже 1).
Если вы считаете, что интеграционный должен быть красным всегда, когда хотя бы один юнит тест красный, то вы заблуждаетесь. Связка двух систем у вас может работать нормально.
Re[22]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 29.06.16 11:17
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Gattaka, Вы писали:


G>>Здравствуйте, gandjustas, Вы писали:


G>>>Хм... а ты писал такие тесты? В тестах 80% работы — подготовка, 10% исполнение и 10% проверка. Когда гоняешь тесты на реальной базе есть одна маленькая проблема — база хранит состояние. А для разных тестов нужны разные данные. Записывать эталонные данные каждый раз — код тестов распухает в разы, не записывать — тесты начинают зависеть от порядка запуска.


G>>Перед запуском теста база чистится. Создается все заново, запускается кейс.

G>Код тестов распухает в разы. Потому что сделать нормальные проверки на одних и тех же эталонных данных невозможно. Тебе как минимум надо проверять три случая — "0,1,n".
G>Ну или просто смириться, что покрытие тестами будет от силы 20%.

G>>Для создания базы есть что-то вроде мастеров создания, код создания базы у большинства тестов одинаковый, различается незначительно. Либо более радикальное решение, вы для теста храните бекап базы. Это если у вас миграция версий настроена...

G>ОМГ

В тестах можно использовать TransactionScope с минимальной изоляцией. Конечно, это не отменяет внимания к тому, чтобы не возникало зависимостей между тестами, но сильно облегчает создание необходимых данных.
Re[22]: Про путаницу с репозиториями и DAO
От: Gattaka Россия  
Дата: 29.06.16 12:01
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Gattaka, Вы писали:


G>>Здравствуйте, gandjustas, Вы писали:


G>>>Хм... а ты писал такие тесты? В тестах 80% работы — подготовка, 10% исполнение и 10% проверка. Когда гоняешь тесты на реальной базе есть одна маленькая проблема — база хранит состояние. А для разных тестов нужны разные данные. Записывать эталонные данные каждый раз — код тестов распухает в разы, не записывать — тесты начинают зависеть от порядка запуска.


G>>Перед запуском теста база чистится. Создается все заново, запускается кейс.

G>Код тестов распухает в разы. Потому что сделать нормальные проверки на одних и тех же эталонных данных невозможно. Тебе как минимум надо проверять три случая — "0,1,n".
G>Ну или просто смириться, что покрытие тестами будет от силы 20%.
Есть еще подход, где "двигают" данные. Положим у вас система, где в вашей предметной область есть некоторая лицензия, позволяющая создать объекты. Так вот вы загружаете лицензию, затем создаете первый объект на котором проверяете тест. Затем не загружая лицензии и не удаляя первый объект создаете второй объект и на нем проверяете второй тест. Но лично я не практиковал такой подход.

G>>Для создания базы есть что-то вроде мастеров создания, код создания базы у большинства тестов одинаковый, различается незначительно. Либо более радикальное решение, вы для теста храните бекап базы. Это если у вас миграция версий настроена...

G>ОМГ
А что такого? Тесты это тот же код, и там работают те же правила что и при разработке софта...
Re[13]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 29.06.16 13:29
Оценка:
Здравствуйте, Baudolino, Вы писали:

IT>>Нет. Функция изоляции переехала в LINQ. А сам DAL больше нафиг не нужен.

B>Большие дяди живут в мире придуманных ими определений?

Большие дяди живут прежде всего в мире здравого смысла.

А маленькие дяди (или большие дети?) не различают в огороде бузину, а в Киеве дядьку.

B>MSDN:

B>

B>LINQ is a set of features that extends powerful query capabilities to the language syntax of C#. LINQ introduces standard, easily-learned patterns for querying and updating data, and the technology can be extended to support potentially any kind of data store. The .NET Framework includes LINQ provider assemblies that enable the use of LINQ with .NET Framework collections, SQL Server databases, ADO.NET Datasets, and XML documents.


И что? С чем ты здесь не согласен и как это противоречит моим "придуманным" определениям?
Если нам не помогут, то мы тоже никого не пощадим.
Re[21]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 30.06.16 00:18
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Я не подменяю понятия. Если гнаться только за покрытие кода тестами, то понятно, почему юнит-тесты ничего не проверяют. UT должно быть легко выкинуть, переписать, написать с нуля и запустить. Покрытие — это очень косвенный показатель. Юнит тесты необходимо проверять так же, как и остальной код, чтобы не было написания тестов ради самих тестов. По сути, это спасательные якоря скалолаза (самого разработчика), если образно. Их может быть не много, но там где надо.

Тогда возникает резонный вопрос — стоят ли юнит-тесты затрачиваемых на них усилий?
И следом второй вопрос — надо ла обязательно обвешивать программу абстракциями для UT, если затраты на UT превышают пользу?

G>>Ты предлагаешь покрыть GetXsByXXX интеграционным тестом, что не имеет смысла, там примитивный запрос. Покрыть SaveChanges интеграционным тестом, что тоже не имеет смылса и написать юнит-тест для F, что не дает фактически проверки.


_>Тут чтобы правильно ответить надо еще вопросов позадовать. Если интересно, то расскажите, в чем главная цель этого метода F (от этого зависит как он должен быть написан и какие тесты для него писать)? А так же интересно, ваш реп должен хранить состояние и беферизировать данные, или просто явлется прослойкой между ORM (EF, например) и BO?

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

_>Для проверки чего? Хранимки, динамического стейтмента, условий проверки? Описание не достаточно, чтобы нормально ответить вопрос.

Для проверки метода.
Псевдокод такой:
bool F(Event evt)
{
    string query = buildQuery(evt);
    ResultSet rs = db.query(query);
    if(rs.Count>0) 
    {
        return false; 
    } 
    else 
    {
        db.addEvent(evt);
        return true;
    }
}


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

Зеленый UT должен показывать что код отработает корректно в продакшене, если база будет доступна.

G>>Я этой задачей троллю апологетов юнит-тестов.

_>Можем попробовать по разбираться. Кому-то точно в + итоги будут. Если без попыток убедить и навязать свое мнение
Re[22]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 30.06.16 00:21
Оценка:
Здравствуйте, another_coder, Вы писали:

_>Здравствуйте, gandjustas, Вы писали:


G>>Здравствуйте, another_coder, Вы писали:


_>>>Хотя и в теории, но если написано все нормально... При зеленых юнит-тестах, но красном интеграционном, должно быть на 100% понятно где и что упало.


G>>И зачем тогда нужны юнит-тесты, если в реальности проверка делается только интеграционным?


_>Не только, а совокупностью. Т.е. например, ты знаешь, что метод Save у тебе не покрыт тестов. Случай, когда все юниты зеленые, но интеграционный нет говорит, в общем случае, о том, что логика правильна, а вот момент с записью не работает. А ведь может быть так, что интеграционный зеленый, а 5 из 20 юнит тестов красный (даже 1).

_>Если вы считаете, что интеграционный должен быть красным всегда, когда хотя бы один юнит тест красный, то вы заблуждаетесь. Связка двух систем у вас может работать нормально.
Ты ушел от ответа на вопрос. Зачем вообще нужны юнит-тесты, какова их ценность, если UT могут быть зелеными, а интеграционный — красным.
От ответа на этот вопрос зависит и другой вопрос — надо ли вообще код приложения изолировать от деталей работы с хранилищем? Ведь мы прекрасно понимает, что в реальности приложение, работающее с базой, не начнет работать с веб-сервисом после простой замены репозитория. Поэтому можем попробовать найти другую причину появления такой абстракции.
Re[23]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 30.06.16 00:23
Оценка:
Здравствуйте, another_coder, Вы писали:

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

Это после того, как несколько человек утверждали, что репозиторий создается чтобы изолировать приложение от "деталей" доступа к данным? Вдруг у нас "на другом" конце вообще веб-сервис, состояние которого мы не можем напрямую контролировать?
Re[23]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 30.06.16 03:26
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, another_coder, Вы писали:


_>>Здравствуйте, gandjustas, Вы писали:


G>>>Здравствуйте, another_coder, Вы писали:


_>>>>Хотя и в теории, но если написано все нормально... При зеленых юнит-тестах, но красном интеграционном, должно быть на 100% понятно где и что упало.


G>>>И зачем тогда нужны юнит-тесты, если в реальности проверка делается только интеграционным?


_>>Не только, а совокупностью. Т.е. например, ты знаешь, что метод Save у тебе не покрыт тестов. Случай, когда все юниты зеленые, но интеграционный нет говорит, в общем случае, о том, что логика правильна, а вот момент с записью не работает. А ведь может быть так, что интеграционный зеленый, а 5 из 20 юнит тестов красный (даже 1).

_>>Если вы считаете, что интеграционный должен быть красным всегда, когда хотя бы один юнит тест красный, то вы заблуждаетесь. Связка двух систем у вас может работать нормально.
G>Ты ушел от ответа на вопрос. Зачем вообще нужны юнит-тесты, какова их ценность, если UT могут быть зелеными, а интеграционный — красным.
G>От ответа на этот вопрос зависит и другой вопрос — надо ли вообще код приложения изолировать от деталей работы с хранилищем? Ведь мы прекрасно понимает, что в реальности приложение, работающее с базой, не начнет работать с веб-сервисом после простой замены репозитория. Поэтому можем попробовать найти другую причину появления такой абстракции.

Я на него ответил. Смысл в том, что юнит-тесты показывают правильность использованных алгоритмов. Если они зеленные и правильно написаны, то ты можешь быть уверен, что в известных сценариях у тебя все нормально. Но всегда есть доля неизвестного и тут ничего не поделаешь. Интеграционные тесты проверяют не алгоритмы, а связку между системами. Т.е. можно не заниматься подготовкой запуска всего-всего, а, например, проинициализировать данные перед сохранением и проверить, что это отработало. Это два типа тестов, не зависимые друг от друга и показывающие общую картину в совокупности.
Тесты end-to-end, которые многие путают с интеграционными, нужны в очень ограниченном кол-ве, достаточном для проверки работоспособности системы в целом.
Re[24]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 30.06.16 03:28
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, another_coder, Вы писали:


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

G>Это после того, как несколько человек утверждали, что репозиторий создается чтобы изолировать приложение от "деталей" доступа к данным? Вдруг у нас "на другом" конце вообще веб-сервис, состояние которого мы не можем напрямую контролировать?

Как я понял, тут речь шла об интеграционных тестах. В них не изолироваться.
Re[22]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 30.06.16 04:04
Оценка:
Здравствуйте, gandjustas.

И так, суммирую:
1) репозиторий просто прослойка (facade) к EF.
2) необходимо проверить генерацию запросов (стейтментов)

Оригинальный псевдокод:
bool F(Event evt)
{
    string query = buildQuery(evt);
    ResultSet rs = db.query(query);
    if(rs.Count>0) 
    {
        return false; 
    } 
    else 
    {
        db.addEvent(evt);
        return true;
    }
}


G>Зеленый UT должен показывать что код отработает корректно в продакшене, если база будет доступна.

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

Что я сделал бы...

1) необходимо иметь возможность мокать:
db.query(query)
db.addEvent(evt)
Не знаю что из себя представляет db. Это интерфейс? Как создается/инжектится?

2) buildQuery(evt) необходимо передать другой сущности, которая занимается его построением. Тогда:
— можно будет отдельно протестировать
— как следствие, можно будет замокать в тестах
Например, вынесем в другой class QueryBuilder : IQueryBuilder {}

На этом этапе код может, предположительно, выглядеть так, после изменений:
bool F(IDb db, IQueryBuilder qBuilder, Event evt)
{
    string query = qBuilder.buildQuery(evt);
    ResultSet rs = db.query(query);
    if(rs.Count>0) 
    {
        return false; 
    } 
    else 
    {
        db.addEvent(evt);
        return true;
    }
}


3) В этом случае на метод F можно написать такие юнит тесты (проверяем ветви алгоритма):
— должен возвращать false, если rs.Count>0
— должен возвращать true, если rs.Count<=0
— должен вызвать addEvent с переданным evt, если rs.Count<=0

4) Отдельно можно протестировать генерацию запросов в методе buildQuery класса QueryBuilder. Не знаю деталей, но девелопер же написал алгоритм генерации, значит может, подавая на вход разные evt, проверить построение соответствующих для них строк. Получится несколько юнит тестов.

5) Я полагаю, что в db.query только сам механизм вызова БД. Поэтому, в интеграционном тесте проверяем, что переданный запрос в db.query вернул то, что требовалось запросом. Этот тест покажет, что этот метод получает данные из базы и адекватно выполняет запросы, что и требуется.

В итоге получилось UT 3+ и IT 1 = 4 или более тестов.

Теперь представим, что вносили какие-то изменения. Может оказаться так, что все зеленное, но почему-то не работает на продакшене. Это проблема не тестов, а тестового окружения. Предположим, там лочится таблица, метод query падает. Тут понятно что делать. У вас все проверяется, кроме этого момента. Значит, специальным интеграционным тестом необходимо проверить этот момент, а метод buildQuery проапдейтить так, чтобы, например, он ставил (nolock).

Мысль понятна? Какие вопросы, несогласия?
Отредактировано 30.06.2016 6:03 another_coder . Предыдущая версия . Еще …
Отредактировано 30.06.2016 6:02 another_coder . Предыдущая версия .
Отредактировано 30.06.2016 6:01 another_coder . Предыдущая версия .
Re[24]: Про путаницу с репозиториями и DAO
От: IT Россия linq2db.com
Дата: 30.06.16 04:50
Оценка:
Здравствуйте, another_coder, Вы писали:

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

_>Тесты end-to-end, которые многие путают с интеграционными, нужны в очень ограниченном кол-ве, достаточном для проверки работоспособности системы в целом.

Если ты не в курсе, то система в целом — это и есть система. Почитай хотя бы теорию систем. Отдельные части системы не определяют её сложность, а лишь обозначают её. Сложность системы определяется связями между её компонентами. Интеграционные тесты как раз тестируют свяь компонентов системы, как бы ты их не называл.
Если нам не помогут, то мы тоже никого не пощадим.
Re[25]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 30.06.16 05:17
Оценка:
Здравствуйте, IT.

IT>Интеграционные тесты как раз тестируют свяь компонентов системы


Вы правы.
Re[23]: Про путаницу с репозиториями и DAO
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 30.06.16 14:37
Оценка:
Здравствуйте, another_coder, Вы писали:

_>1) необходимо иметь возможность мокать:

_>db.query(query)
_>db.addEvent(evt)
_>Не знаю что из себя представляет db. Это интерфейс? Как создается/инжектится?
Неважно, говорю же мокать можно все.



_>2) buildQuery(evt) необходимо передать другой сущности, которая занимается его построением. Тогда:

_>- можно будет отдельно протестировать
_>- как следствие, можно будет замокать в тестах
_>Например, вынесем в другой class QueryBuilder : IQueryBuilder {}
Зачем? Это чистая функция, её протестировать можно и так. Но тест этот ничего не дает без отправки запроса в базу.


_>3) В этом случае на метод F можно написать такие юнит тесты (проверяем ветви алгоритма):

_>- должен возвращать false, если rs.Count>0
_>- должен возвращать true, если rs.Count<=0
_>- должен вызвать addEvent с переданным evt, если rs.Count<=0
Тестировать if — это сильно.

_>4) Отдельно можно протестировать генерацию запросов в методе buildQuery класса QueryBuilder. Не знаю деталей, но девелопер же написал алгоритм генерации, значит может, подавая на вход разные evt, проверить построение соответствующих для них строк. Получится несколько юнит тестов.

Само по себе это тестирование ничего не дает. Запросто может просто название поля поменяться.

_>5) Я полагаю, что в db.query только сам механизм вызова БД. Поэтому, в интеграционном тесте проверяем, что переданный запрос в db.query вернул то, что требовалось запросом. Этот тест покажет, что этот метод получает данные из базы и адекватно выполняет запросы, что и требуется.

db — внешний код его тестировать не надо.

_>В итоге получилось UT 3+ и IT 1 = 4 или более тестов.

Из которых один не нужен, один тестирует очевидные вещи, остальные не дают гарантий.
Re[24]: Про путаницу с репозиториями и DAO
От: another_coder Россия  
Дата: 30.06.16 22:26
Оценка:
Здравствуйте, gandjustas, Вы писали:

_>>2) buildQuery(evt) необходимо передать другой сущности, которая занимается его построением. Тогда:

_>>- можно будет отдельно протестировать
_>>- как следствие, можно будет замокать в тестах
_>>Например, вынесем в другой class QueryBuilder : IQueryBuilder {}
G>Зачем? Это чистая функция, её протестировать можно и так. Но тест этот ничего не дает без отправки запроса в базу.

Её конечно можно протестировать. Через вызов Application.Run например тоже можно протестироват её же. И еще кучу других.
Но зачем так усложнять, если можно сделать проще? Выгода: проще понять код, легче модифицировать/выкинуть, в тестах пример использования.
А вопрос про базу и про что дает ниже...

_>>3) В этом случае на метод F можно написать такие юнит тесты (проверяем ветви алгоритма):

_>>- должен возвращать false, если rs.Count>0
_>>- должен возвращать true, если rs.Count<=0
_>>- должен вызвать addEvent с переданным evt, если rs.Count<=0
G>Тестировать if — это сильно.

IF часть алгоритма, поэтому необходимо. Вы еще скажите, например, что расчет факториала можно проверить только одним тестом.

_>>4) Отдельно можно протестировать генерацию запросов в методе buildQuery класса QueryBuilder. Не знаю деталей, но девелопер же написал алгоритм генерации, значит может, подавая на вход разные evt, проверить построение соответствующих для них строк. Получится несколько юнит тестов.

G>Само по себе это тестирование ничего не дает. Запросто может просто название поля поменяться.

Именно, что дает. Ваш метод генерилка ни что иное, как y = F(x). x и y определены, а девелопер пишет F. Зачем y отправлять в базу, если это часть требований?
С полем все просто: это часть требований. Поменялись требования к методу, необходимо это отразить в коде.

_>>5) Я полагаю, что в db.query только сам механизм вызова БД. Поэтому, в интеграционном тесте проверяем, что переданный запрос в db.query вернул то, что требовалось запросом. Этот тест покажет, что этот метод получает данные из базы и адекватно выполняет запросы, что и требуется.

G>db — внешний код его тестировать не надо.
У вас был тест на Save в описанном вами случае? Полагаю, с таким тестом вы не пропустили бы проблему и легко нашли бы её.

Главная идея тестов: разбиение кода по принципу SRP, определение требований к полученым кускам и кодирование этих требований в виде UT, IT и пр. Тесты меняются, выкидываются, пишутся новые — не надо этого бояться. TDD форева! )

оффтоп: по вашим коментам выглядит так, словно девелоперы у вас пишут "во тьме": без требований, без понимания того, как оно должно быть. Такое возможно, напрмиер в R&D и там тесты реально не всегда столь эфективны, иногда даже пустая трата времени. Но на то это и исследование/прототипирование.
Отредактировано 30.06.2016 22:29 another_coder . Предыдущая версия . Еще …
Отредактировано 30.06.2016 22:28 another_coder . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.