UnitOfWork нарушает принцип Single Responsibility?
От: Docker Канада  
Дата: 14.11.13 07:07
Оценка:
Коллеги, поделитесь мнением и мудростью!

Не нарушает ли Фаулеровское определение Unit Of Work принцип Single Responsibility из принципов ООД сформулированных Р. Мартином еще лет 20 назад. Вопрос абсолютно не о сферических конях в вакууме, конкретную проблему смотрите ниже.

Немного теории

У Фаулера UoW определяется как:

"Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems."

У Мартина Single Responsibility:
"A class should have one, and only one, reason to change."

Т.е. получается, если слепо следуем Фоулеру, нарушаем принцип Мартина, так как если меняется метод отслеживания изменений — меняем UofW, если меняется координация записывания изменений — опять меняем UoW.

Пример из практики

Смотрим на взаимодействие репозитория и Unit Of Work. Пусть у нас есть один репозиторий реализующий IUserRepository, который может работать с каким-то хранилищем данных (база, мок объекты, файловая система, компонента написанная PupkinSoft Inc., неважно). Ну, и допустим мы хотим сделать такое отслеживание изменений о котором пишет Фаулер.

Внимание вопрос, если какая-то из реализаций репозитория уже делает “list of objects affected by a business transaction”, зачем мне еще раз это реализовывать в UnitOfWork? Но Фаулер это жестко запихивает в UnitOfWork. Это на самом порождает еще больше проблем. Например если я отслеживаю изменения в UnitOfWork, то если я меняю данные, а потом зачитываю новые данные, то кто-то еще должен проверить не содержатся ли зачитанные данные в списке измененных и т.д.

Чешем затылок, мыслим по Мартину. По нему должны быть классы которые:

1. Coordinates the writing out of changes and the resolution of concurrency problems (IUnitOfCoordinationWork)
2. Repository (чтобы он там не делал, не о нем вопрос) (IUserRepository)
3. Maintains a list of objects affected by a business transaction (реализует ITrackChanges)

Тогда все просто и красиво.

1. UnitOfWork реализует IUnitOfCoordinationWork.
2. Методы хранилища реализуют IRepository
3. Если конкретная реализация репозитория реализует “list of objects affected by a business transaction” то оборачиваем эту функциональность в ITrackChanges, если нет, то пишем сами. Т.е. если функциональность уже есть в репозитории, то не лопатим тонны кода делающего тоже самое в UnitOfWork, изобретая велосипед, как это было если б делали по Фаулеру, а используем готовое.


Да и вообще, но это другая тема, “Maintains a list of objects affected by a business transaction” может быть нафиг не нужна на многих проектах. Зачем добавлять это требование в такой “краеугольный камень” как UnitOfWork?

Вот такие мысли. Поскольку я “замахнулся” на великого Фаулера, то мне интересно, что думаете вы.

Спасибо!
Re: UnitOfWork нарушает принцип Single Responsibility?
От: Sinix  
Дата: 14.11.13 07:51
Оценка: 22 (2) +2
D>Вот такие мысли. Поскольку я “замахнулся” на великого Фаулера, то мне интересно, что думаете вы.
1. Не принимать решения об архитектуре на основе абстрактных паттернов, принципов и личных предпочтений. Арохитектура не для того создаётся, чтобы вот тут затащить репозитарий а вот там — UOW.
2. Не возводить Фаулера в абсолют.
3. Изучать матчасть, уметь выделять типовые проблемы и знать, как их можно решить в рамках конкретного проекта.
4. Очень критично относиться к культу карго в методологиях.

Про паттерны очень хорошо
Автор: XopoSHiy
Дата: 14.04.10
написал Horishiy. Эх, перевели бы в своё время банду четырёх как "_приёмы_ проектирования" — насколько легче бы было

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

Десктоп или веб? Нужно ли разделять состояние, или каждая из частей системы работает со своей копией данных? Нужны ли транзакции в бизнес-коде? Нужна ли возможность получить состояние объекта до изменений? Нужна ли передача (с сохранением состояния) по сети? Есть ли уже готовое решение, подходящее под наши запросы? Если нет — во что обойдётся допилить/написать с 0?

Если ответить на эти вопросы — выбор будет очевиден и без Фаулера. Затраты на универсальный change-tracking и интеграцию его с DAL достаточно большие, готовых решений (включая разнообразные ORM) — как повезёт с выбранным языком/фреймворком, выбирайте

Если исходить из "просто и красиво" — нужно смотреть не на "а тут мы заиспользуем change tracker", а о том, чтобы большинство сценариев использования было интуитивно понятным. Не, ну серьёзно, если нужно поправить описание пользователю — зачем придумывать что-то кроме
  db.DoInTransaction(()=>
  {
    var user = db.Users.FindOrCreate("Alex")
    user.Description = "...";
  });
Re[2]: UnitOfWork нарушает принцип Single Responsibility?
От: Docker Канада  
Дата: 15.11.13 04:04
Оценка: 12 (1) +1
Здравствуйте, Sinix, Вы писали:

D>>Вот такие мысли. Поскольку я “замахнулся” на великого Фаулера, то мне интересно, что думаете вы.

S>1. Не принимать решения об архитектуре на основе абстрактных паттернов, принципов и личных предпочтений. Арохитектура не для того создаётся, чтобы вот тут затащить репозитарий а вот там — UOW.
S>2. Не возводить Фаулера в абсолют.
S>3. Изучать матчасть, уметь выделять типовые проблемы и знать, как их можно решить в рамках конкретного проекта.
S>4. Очень критично относиться к культу карго в методологиях.

Со всем абсолютно согласен, нечего возразить.

S>Про паттерны очень хорошо
Автор: XopoSHiy
Дата: 14.04.10
написал Horishiy. Эх, перевели бы в своё время банду четырёх как "_приёмы_ проектирования" — насколько легче бы было


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

S>Если вернуться к исходному вопросу — надо смотреть на язык/фреймворк/инфраструктуру/сложившуюся архитектуру с одной стороны, и на бизнес-требования — с другой.


S>Десктоп или веб? Нужно ли разделять состояние, или каждая из частей системы работает со своей копией данных? Нужны ли транзакции в бизнес-коде? Нужна ли возможность получить состояние объекта до изменений? Нужна ли передача (с сохранением состояния) по сети? Есть ли уже готовое решение, подходящее под наши запросы? Если нет — во что обойдётся допилить/написать с 0?


S>Если ответить на эти вопросы — выбор будет очевиден и без Фаулера. Затраты на универсальный change-tracking и интеграцию его с DAL достаточно большие, готовых решений (включая разнообразные ORM) — как повезёт с выбранным языком/фреймворком, выбирайте


Считайте, что начинаем систему с нуля. Платформа — Windows, .NET, язык — C#. Приложение — веб. Части систем работают с данным из одного хранилища. Транзакции нужны. Состояние до изменений не надо, передача по сети (с сохранением состояния) не нужна. Решение есть, но это просто набор классов однозначно соответствующий таблицам в базе в которых есть методы работы с базой; решение абсолютно нормально работает, но думаю можно сделать лучше. Допилить/дописать, а скорее переписать с нуля — директива руководства. Стоп! Знаю возражение — это довольно тупо, ведь все и так абсолютно нормально работает и поддерживается без проблем, но помните (!) директива руководства, не моя

Итого... это все не важно, мне честно, тут все ясно. Нафиг мне этот трэкер нужен, да и не только мне, бизнесу в общем тоже. Но, как это объяснить "суперзвездам" программистам, начитавшимся теории. Ведь трекер это круто, и в будущем может пригодится, да и Фаулер сказал...

В общем-то для этого я и придумал этот "теоретическо/практический" пример. Чтобы просто отвечать на аргументы типа "Ты что Фаулера не уважаешь"? Если мой Фаулер vs Мартин конфликт верен, то от меня последкет что-то вроде "А ты что Мартина не уважаешь?", дальше дело техники.

S>Если исходить из "просто и красиво" — нужно смотреть не на "а тут мы заиспользуем change tracker", а о том, чтобы большинство сценариев использования было интуитивно понятным. Не, ну серьёзно, если нужно поправить описание пользователю — зачем придумывать что-то кроме

S>
S>  db.DoInTransaction(()=>
S>  {
S>    var user = db.Users.FindOrCreate("Alex")
S>    user.Description = "...";
S>  });
S>


Как всегда, абсолютно согласен.

Но мой теоритический вопрос все еще в силе. Противоречит ли Фаулер Мартину? Почему мне это надо, смотрите выше Дабы стряхнуть упырей со спины
Re[3]: UnitOfWork нарушает принцип Single Responsibility?
От: Sinix  
Дата: 15.11.13 06:13
Оценка: 6 (1)
Здравствуйте, Docker, Вы писали:

Собрал основные вопросы:

D>решение абсолютно нормально работает, но думаю можно сделать лучше. Допилить/дописать, а скорее переписать с нуля — директива руководства. Стоп! Знаю возражение — это довольно тупо, ведь все и так абсолютно нормально работает и поддерживается без проблем, но помните (!) директива руководства, не моя

D>В общем-то для этого я и придумал этот "теоретическо/практический" пример. Чтобы просто отвечать на аргументы типа "Ты что Фаулера не уважаешь"? Если мой Фаулер vs Мартин конфликт верен, то от меня последкет что-то вроде "А ты что Мартина не уважаешь?", дальше дело техники.
D>Но, как это объяснить "суперзвездам" программистам, начитавшимся теории. Ведь трекер это круто, и в будущем может пригодится, да и Фаулер сказал...

Ответы

1. Тут непонятна ситуация: если и шеф и девелоперы дружно исполняют "ждём перемен" — может, действительно есть повод? С чего-то такое желание возникло ведь?


2. Если нет объективных причин, то на вопросы аля "Вы не сочувствуете детям Германии?" ((с) Собачье сердце) единственный правильный ответ — "Равнодушен" (оттуда же). Все остальные доводы не работают, т.к. обычно у людей в голове обустроился свой маленький личный Фаулер, к реальному отношения не имеющий. Ну убедите вы человека, что он неправ, дальше что?

Если уж нужны ссылки на авторитеты — я бы в первую очередь ссылался на Framework Design Guidelines, которая в отличие от местами сфероконичного Фаулера насквозь конкретна и не допускает лишних толкований. Буквально с первых страниц:

Framework Design Principle:
Frameworks must be designed starting from a set of usage scenarios and code samples implementing these scenarios.


Если готовые сценарии есть — то тогда вопрос обычно отпадает сам собой: собираем набор реальных бизнес-проблем и проверяем как хорошо они ложатся на уже написанное решение, на что-то готовое (тот же EF/BLToolkit никто не отменял), или на то что вы собираетесь писать. Оцениваем затраты, выбираем.


3. По моему (глубоко личному) убеждению, мухи и котлеты должны жить отдельно: за отслеживание изменений (что добавила, поменяли, удалили) должна отвечать инфраструктура и делать это максимально прозрачным для бизнес-логики способом. В идеале — это отслеживание должно позволять просто сказать Save() и не мучаться с ручной вознёй с DAL. Лучшее, что я видел в этом плане (увы, только в этом плане) — это датасеты: всё, включая биндинг к детейлам, просто работает.

Как добиться такой прозрачной реализации, явно разделяя DAL (IUnitOfCoordinationWork), данные (IUserRepository) и собственно трекер (ITrackChanges) — я не представляю. Не, точнее примерно представляю, но это потребует такой возни с AOP/DI, что сама исходная проблема покажется детским садом.

Более того, зачем биз-коду вообще нужно знать о наличии всех трёх составляющих? Его дело — превратить логику в последовательность вызовов вашего API (ну, как в примере ниже), а как уж API будет устроено внутри — это его глубоко личные проблемы.

Начать можно с классики: добавление заказа по известному покупателю и списку товаров — попросите ваших разработчиков набросать примерный код с использованием тру (по их мнению)-подхода. Если там будет что то кроме

var customer = ...
var goods = ...

var order = db.NewOrder();
order.Customer = customer;
order.Items.Addrange(goods);

— то у меня для них плохие новости
Re[4]: UnitOfWork нарушает принцип Single Responsibility?
От: Docker Канада  
Дата: 18.11.13 06:02
Оценка:
Здравствуйте, Sinix, Вы писали:

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


S>Собрал основные вопросы:


D>>решение абсолютно нормально работает, но думаю можно сделать лучше. Допилить/дописать, а скорее переписать с нуля — директива руководства. Стоп! Знаю возражение — это довольно тупо, ведь все и так абсолютно нормально работает и поддерживается без проблем, но помните (!) директива руководства, не моя

D>>В общем-то для этого я и придумал этот "теоретическо/практический" пример. Чтобы просто отвечать на аргументы типа "Ты что Фаулера не уважаешь"? Если мой Фаулер vs Мартин конфликт верен, то от меня последкет что-то вроде "А ты что Мартина не уважаешь?", дальше дело техники.
D>>Но, как это объяснить "суперзвездам" программистам, начитавшимся теории. Ведь трекер это круто, и в будущем может пригодится, да и Фаулер сказал...

S>Ответы


S>1. Тут непонятна ситуация: если и шеф и девелоперы дружно исполняют "ждём перемен" — может, действительно есть повод? С чего-то такое желание возникло ведь?


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

Но желания шефа + "изменения неплохо бы внести" просто чтобы сделать посовременнее, вполне достаточно.

S>2. Если нет объективных причин, то на вопросы аля "Вы не сочувствуете детям Германии?" ((с) Собачье сердце) единственный правильный ответ — "Равнодушен" (оттуда же). Все остальные доводы не работают, т.к. обычно у людей в голове обустроился свой маленький личный Фаулер, к реальному отношения не имеющий. Ну убедите вы человека, что он неправ, дальше что?


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

По моему опыту, если убедить супер программиста в том что он неправ, да еще при свидетелях, то потом гораздо проще с ним разговаривать

S>Если уж нужны ссылки на авторитеты — я бы в первую очередь ссылался на Framework Design Guidelines, которая в отличие от местами сфероконичного Фаулера насквозь конкретна и не допускает лишних толкований. Буквально с первых страниц:

S>

Framework Design Principle:
S> Frameworks must be designed starting from a set of usage scenarios and code samples implementing these scenarios.


S>Если готовые сценарии есть — то тогда вопрос обычно отпадает сам собой: собираем набор реальных бизнес-проблем и проверяем как хорошо они ложатся на уже написанное решение, на что-то готовое (тот же EF/BLToolkit никто не отменял), или на то что вы собираетесь писать. Оцениваем затраты, выбираем.


О, за ссылку спасибо Все не прочитал еще, но интересно.

S>3. По моему (глубоко личному) убеждению, мухи и котлеты должны жить отдельно: за отслеживание изменений (что добавила, поменяли, удалили) должна отвечать инфраструктура и делать это максимально прозрачным для бизнес-логики способом. В идеале — это отслеживание должно позволять просто сказать Save() и не мучаться с ручной вознёй с DAL. Лучшее, что я видел в этом плане (увы, только в этом плане) — это датасеты: всё, включая биндинг к детейлам, просто работает.


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

S>Как добиться такой прозрачной реализации, явно разделяя DAL (IUnitOfCoordinationWork), данные (IUserRepository) и собственно трекер (ITrackChanges) — я не представляю. Не, точнее примерно представляю, но это потребует такой возни с AOP/DI, что сама исходная проблема покажется детским садом.


S>Более того, зачем биз-коду вообще нужно знать о наличии всех трёх составляющих? Его дело — превратить логику в последовательность вызовов вашего API (ну, как в примере ниже), а как уж API будет устроено внутри — это его глубоко личные проблемы.


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


S>
S>var customer = ...
S>var goods = ...

S>var order = db.NewOrder();
S>order.Customer = customer;
S>order.Items.Addrange(goods);
S>

S>- то у меня для них плохие новости


А дальше, что? Или после последней строки все уже в базе? Как бы Вы сделали?

У некоторых разработчиков было бы еще:

var unitOfWork = ...

unitOfWork.MarkNew(order);
unitOfWork.Commit();
Re[5]: UnitOfWork нарушает принцип Single Responsibility?
От: Sinix  
Дата: 18.11.13 09:01
Оценка:
Здравствуйте, Docker, Вы писали:

D>Но желания шефа + "изменения неплохо бы внести" просто чтобы сделать посовременнее, вполне достаточно.

Ну, если вся команда единодушна — проще поддержать, чем сдерживать) Вдруг что хорошее получится?


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

D>По моему опыту, если убедить супер программиста в том что он неправ, да еще при свидетелях, то потом гораздо проще с ним разговаривать

Ок, тогда так: и у Фаулера, и у GoF и у Мартина (да у кого угодно) есть внешне противоречивые утверждения. На практике, если покопаться — проблема, как всегда, в неявном контексте. То есть если полагаться на опыт (и типовые проблемы) Фаулера — "правильным" будет одно, Мартина — другое, Бека — третье и т.д..

Например, лично у меня Мартин ("Clean Code" кажется) вообще вызывает внутреннее отторжение, в основном из-за излишней категоричности и неявном протаскивании рецептов, скорее характерных для явы, чем для других языков. Сейчас спорных моментов не помню, но где-то с середины книги я скорее скептически пролистывал, чем вчитывался. Тем не менее, если кому-то он нравится и по Мартину получается хороший и поддерживаемый код — это однозначно говорит что книга не то чтобы совсем уж плоха и место на полке она заслужила. Хотя бы для расширения кругозора

Короче, спорить надо не о "Заборет ли Мартин Фаулера", а о том, чей опыт будет полезнее в каждом конкретном случае. Раз уж у нас речь о дотнете — я бы в первую очередь ориентировался на официальную матчасть + рекомендации (читай, Рихтер и Design Guidelines). С известной долей скептицизма конечно


D>Тут да, датасеты вопрос спорный Но насчет отслеживания изменений, тоже про похожие механизмы сейчас думаю, если вдруг отслеживание понадобится.

Оно в любом случае понадобится, если только не держать "длинные" транзакции или не изобретать что-то в духе TransactionScript по Фаулеру. Вопрос в том — будет ли он явным (т.е. информация о добавленных/изменённых/удалённых записях будет записываться вручную), или прозрачным для бизнес-логики.

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


D>А дальше, что? Или после последней строки все уже в базе? Как бы Вы сделали?

А это зависит от типовых сценариев использования. Самый простой в использовании вариант — явное закидывание изменений через db.Commit();

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

Попробуйте расписать ваши типовые сценарии так, как если бы у вас уже был удобный API для работы с данными. Станет примерно понятно, что нужно, а что нет.

D>У некоторых разработчиков было бы еще:

D>unitOfWork.MarkNew(order);

MarkNew() выглядит каким-то издевательством.
Во-первых, мы _уже_ создали объект, зачем подтверждать, что объект создан?
В-третьих, это тупо лишний код, который повышает наши шансы сделать что-либо не так. Что если мы забудем MarkNew() — как отслеживать подобные ошибки?
Re[6]: UnitOfWork нарушает принцип Single Responsibility?
От: Docker Канада  
Дата: 19.11.13 06:20
Оценка: +1
Здравствуйте, Sinix, Вы писали:

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


D>>Но желания шефа + "изменения неплохо бы внести" просто чтобы сделать посовременнее, вполне достаточно.

S>Ну, если вся команда единодушна — проще поддержать, чем сдерживать) Вдруг что хорошее получится?

Абсолютно согласен, этим путем и идем. Хорошее точно получится.

Единственный момент, новый шеф своего человека привел, который почему-то считает, что раз его привели, то он тут самуй умный, и вот теперь-то есть возможность развернуться Но это лечится путем "опускания", желательно при свидетелях, пару раз. Болезненный, но самый быстрый способ, увы. А вопрос насчет MarkNew и отслеживания изменений и возник потому, что я это у него в коде прототипа увидел, в котором нафиг это не надо. Но, чувак авторитетов уважает сильно

S>Ок, тогда так: и у Фаулера, и у GoF и у Мартина (да у кого угодно) есть внешне противоречивые утверждения. На практике, если покопаться — проблема, как всегда, в неявном контексте. То есть если полагаться на опыт (и типовые проблемы) Фаулера — "правильным" будет одно, Мартина — другое, Бека — третье и т.д..


Ок, спасибо, так я на этот вопрос не смотрел...

S>Например, лично у меня Мартин ("Clean Code" кажется) вообще вызывает внутреннее отторжение, в основном из-за излишней категоричности и неявном протаскивании рецептов, скорее характерных для явы, чем для других языков. Сейчас спорных моментов не помню, но где-то с середины книги я скорее скептически пролистывал, чем вчитывался. Тем не менее, если кому-то он нравится и по Мартину получается хороший и поддерживаемый код — это однозначно говорит что книга не то чтобы совсем уж плоха и место на полке она заслужила. Хотя бы для расширения кругозора


И тут спасибо. Честно, книгу я скорее пролистал кое где, но не читал. Почитаю всю, для расширения кругозора она точно нужна

S>Короче, спорить надо не о "Заборет ли Мартин Фаулера", а о том, чей опыт будет полезнее в каждом конкретном случае. Раз уж у нас речь о дотнете — я бы в первую очередь ориентировался на официальную матчасть + рекомендации (читай, Рихтер и Design Guidelines). С известной долей скептицизма конечно


А что у Рихтера посоветуете? И почему Рихтер такой авторитет в .NET . Нет, книга по программированию в Windows у него была конечно вечной классикой, но про его .NET книги никогда не слышал...

S>Оно в любом случае понадобится, если только не держать "длинные" транзакции или не изобретать что-то в духе TransactionScript по Фаулеру. Вопрос в том — будет ли он явным (т.е. информация о добавленных/изменённых/удалённых записях будет записываться вручную), или прозрачным для бизнес-логики.


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


Тут есть моменты. В зависимости от задачи, но MarkNew в UnitOfWork для этого смотриться для меня, мягко говоря, не совсем верно. Хотя наверное есть задачи в которых это удобно.

D>>А дальше, что? Или после последней строки все уже в базе? Как бы Вы сделали?

S>А это зависит от типовых сценариев использования. Самый простой в использовании вариант — явное закидывание изменений через db.Commit();

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


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

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


D>>У некоторых разработчиков было бы еще:

D>>unitOfWork.MarkNew(order);

S>MarkNew() выглядит каким-то издевательством.

S>Во-первых, мы _уже_ создали объект, зачем подтверждать, что объект создан?
S>В-третьих, это тупо лишний код, который повышает наши шансы сделать что-либо не так. Что если мы забудем MarkNew() — как отслеживать подобные ошибки?


И тут мне просто нечего сказать. Думаю если Фаулер привел такой пример, значит где-то такое использование имеет место быть, но... как-то не могу понять где кроме демо примеров

Да... и все время забываю сказать, спасибо за беседу
Re[4]: UnitOfWork нарушает принцип Single Responsibility?
От: Enomay  
Дата: 19.11.13 06:29
Оценка:
S>на вопросы аля "Вы не сочувствуете детям Германии?" ((с) Собачье сердце) единственный правильный ответ — "Равнодушен" (оттуда же).

увы, но профессор сказал что "сочувствует" просто не хочет платить червонец
Re[7]: UnitOfWork нарушает принцип Single Responsibility?
От: Sinix  
Дата: 19.11.13 07:13
Оценка: 27 (2)
Здравствуйте, Docker, Вы писали:

D>Абсолютно согласен, этим путем и идем. Хорошее точно получится.

Ок, удачи

D>Единственный момент, новый шеф своего человека привел, который почему-то считает, что раз его привели, то он тут самуй умный, и вот теперь-то есть возможность развернуться Но это лечится путем "опускания", желательно при свидетелях, пару раз. Болезненный, но самый быстрый способ, увы. А вопрос насчет MarkNew и отслеживания изменений и возник потому, что я это у него в коде прототипа увидел, в котором нафиг это не надо. Но, чувак авторитетов уважает сильно


Ох, это самый тяжёлый случай, может выйти или обида на всю жизнь, или человек наконец научится работать в команде Я бы попросил непосредственного начальника новичка поговорить с новичком открыто и поставить перед фактом: для команды не важна крутизна по Фаулеру. В первую очередь от инфраструктуры хотят скучных и проверяемых вещей: простота использования (на наборе типовых сценариев конечно), лёгкость изучения, простота изменения как самого DAL, так и бизнес-кода, ну и т.д и т.п.

Не, вполне возможно что MarkNew() нужен и полезен, но это должно подтверждаться на тех самых типовых сценариях, иначе проблема так и останется на уровне "моё слово против твоего"

Если товарищ не согласен или сам не может обеспечить эти требования — то формальное руководство (в части разработки DAL) и принятие архитектурных решений остаются за ним, но результат на каждой итерации должен обсуждаться с остальной командой (или тимлидом). И обязательное условие, которое нарушать нельзя никогда. Реально, это пожалуй единственное правило за всё время работы, для которого нет никаких извинений:

Разработчик API обязан сам пользоваться своим кодом.

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

S>>Короче, спорить надо не о "Заборет ли Мартин Фаулера", а о том, чей опыт будет полезнее в каждом конкретном случае. Раз уж у нас речь о дотнете — я бы в первую очередь ориентировался на официальную матчасть + рекомендации (читай, Рихтер и Design Guidelines). С известной долей скептицизма конечно

D>А что у Рихтера посоветуете? И почему Рихтер такой авторитет в .NET . Нет, книга по программированию в Windows у него была конечно вечной классикой, но про его .NET книги никогда не слышал...

А у Рихтера из более-менее актуального только одна CLR via c#, зато в 4 (или уже в 5?) изданиях (кстати, вру — появилась Windows Runtime via C#, надо будет поглядеть). Почему CLR via c# классика?
1. Вышла, когда ничего подобного и рядом не было.
2. Рассматривается практический минимум того, что нужно универсальному разработчику для продуктивной разработки под дотнет. Не, если засесть в какой-то нише, то многое можно и пропустить. Если работать в нескольких проектах/темах, то со временем пригодится практически всё.
3. Книга очень толково написана. По сути, после Рихтера для изучения дотнета остаются профильные книги (WPF, WCF, etc), узкоспециализированная .NET Pro Performance от Sasha Goldshtein (и то она во многом дополняет Рихтера и без него пользы не даст) и конечно Framework Design Guidelines. Дальше — только самообразование + блоги, книги дают очень мало (если сравнивать полезное содержание/объём).

D>Тут есть моменты. В зависимости от задачи, но MarkNew в UnitOfWork для этого смотриться для меня, мягко говоря, не совсем верно. Хотя наверное есть задачи в которых это удобно.

Угу. Почему думаешь я с самого начала про типовые сценарии говорю? У вас уже есть опыт и (как я понимаю готовый код) на старом DAL, его можно (и нужно) использовать для сравнения "стало лучше/хуже".

D>И тут мне просто нечего сказать. Думаю если Фаулер привел такой пример, значит где-то такое использование имеет место быть, но... как-то не могу понять где кроме демо примеров

Та же фигня. Я бы делал скидку на время, когда писалось большинство примеров. Ява была молодая, тропы в грабельном поле ещё не хожены и хоть какое-то сработавшее решение уже имело ценность
Re[5]: UnitOfWork нарушает принцип Single Responsibility?
От: Sinix  
Дата: 19.11.13 07:22
Оценка:
Здравствуйте, Enomay, Вы писали:

E>увы, но профессор сказал что "сочувствует" просто не хочет платить червонец

А это от издания зависит
Re[6]: UnitOfWork нарушает принцип Single Responsibility?
От: Enomay  
Дата: 19.11.13 12:09
Оценка: +1
E>>увы, но профессор сказал что "сочувствует" просто не хочет платить червонец
S>А это от издания зависит

А! могу быть не прав еще потому, что сужу по фильму. уж больно мне нравится экранизация.
Re[8]: UnitOfWork нарушает принцип Single Responsibility?
От: Docker Канада  
Дата: 20.11.13 05:57
Оценка: 11 (1)
Здравствуйте, Sinix, Вы писали:

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


D>>Абсолютно согласен, этим путем и идем. Хорошее точно получится.

S>Ок, удачи



S>Разработчик API обязан сам пользоваться своим кодом.


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


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

S>А у Рихтера из более-менее актуального только одна CLR via c#, зато в 4 (или уже в 5?) изданиях (кстати, вру — появилась Windows Runtime via C#, надо будет поглядеть). Почему CLR via c# классика?

S>1. Вышла, когда ничего подобного и рядом не было.
S>2. Рассматривается практический минимум того, что нужно универсальному разработчику для продуктивной разработки под дотнет. Не, если засесть в какой-то нише, то многое можно и пропустить. Если работать в нескольких проектах/темах, то со временем пригодится практически всё.
S>3. Книга очень толково написана. По сути, после Рихтера для изучения дотнета остаются профильные книги (WPF, WCF, etc), узкоспециализированная .NET Pro Performance от Sasha Goldshtein (и то она во многом дополняет Рихтера и без него пользы не даст) и конечно Framework Design Guidelines. Дальше — только самообразование + блоги, книги дают очень мало (если сравнивать полезное содержание/объём).

Хм, спасибо, посмотрел немного на amazonе, Рихтер похоже как и в старые добрые времена копает глубже чем остальные, так что думаю будет интересно. Спасибо!

D>>Тут есть моменты. В зависимости от задачи, но MarkNew в UnitOfWork для этого смотриться для меня, мягко говоря, не совсем верно. Хотя наверное есть задачи в которых это удобно.

S>Угу. Почему думаешь я с самого начала про типовые сценарии говорю? У вас уже есть опыт и (как я понимаю готовый код) на старом DAL, его можно (и нужно) использовать для сравнения "стало лучше/хуже".

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

D>>И тут мне просто нечего сказать. Думаю если Фаулер привел такой пример, значит где-то такое использование имеет место быть, но... как-то не могу понять где кроме демо примеров

S>Та же фигня. Я бы делал скидку на время, когда писалось большинство примеров. Ява была молодая, тропы в грабельном поле ещё не хожены и хоть какое-то сработавшее решение уже имело ценность

Смотри, вот статья в MSDN 2009 год. Не самое новье, но урожай грабель уже собран. Автор "Jeremy Miller, a Microsoft MVP for C#, is also the author of the open-source StructureMap tool for Dependency Injection", ну т.е. тоже полный фарш. И тем не менее та же фигня — те же MarkNew и MarkDirty в IUnitOfWork, более того он еще и каждый метод репозитория(?) в отдельный класс засовывает. Т.е. и чувак вроде толковый, и статья вроде как претендует на нечто обобщенное... поэтому когда я такое вижу, у меня такое ощущение, что я чего-то конкретно не понял
Re[9]: UnitOfWork нарушает принцип Single Responsibility?
От: Sinix  
Дата: 20.11.13 10:39
Оценка: 6 (1)
Здравствуйте, Docker, Вы писали:

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

Ок, давай — всегда интересно посмотреть на возражения, особенно если они аргументированы

D> понятие "стало лучше/хуже" иногда вещь субьективная.


Не, если сравнение лучше/хуже субъективно — у нас или нет репрезентативных сценариев использования, или все варианты примерно одинаковы.

Ну, как пример: "для создания бизнес-класса нужно вызвать несколько методов" vs "создание бизнес-класса через обычный конструктор(через метод в контейнере-владельце".

Помимо "второй круче" можно привести объективные критерии:
1. Частота использования. Часто используемые операции должны быть максимально простыми — иначе полкода вместо собственно логики будет заниматься ублажением нашего API
2. Очевидность. Контракт первого варианта не задокументирован в API, т.е. научиться его использовать можно или через заучивание документации, или через ловлю ошибок под отладчиком. Learn through pain — не наш метод
3. Комбинирование. Если мы создаём одновременно два бизнес-объекта — важен ли порядок MarkNew? Будут ли побочные эффекты?
ну и так далее

На мой взгляд, есть два универсальных критерия правильного дизайна:
1. Количество WTF/сек при изучении API и при решении похожих задач.
2. Количество хелперов к API
Если оба пункта зашкаливают — у нас проблемы


D>... поэтому когда я такое вижу, у меня такое ощущение, что я чего-то конкретно не понял

1-в-1. Две версии:
1. Для автора сценарии с использованием IUnitOfWork некритичны, основная часть сводится к CRUD. Такое характерно для легковесных сайтов и да, там что-то более громоздкое не особо нужно. Поскольку для подобных вещей уже есть BLToolkit и аналоги, изобретать что-то своё я не вижу никакого смысла.
2. Автор показал только костяк, а в реальном коде прячет его внутрь репозитария. Непохоже конечно, но мало ли
Re[10]: UnitOfWork нарушает принцип Single Responsibility?
От: Docker Канада  
Дата: 21.11.13 05:56
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Не, если сравнение лучше/хуже субъективно — у нас или нет репрезентативных сценариев использования, или все варианты примерно одинаковы.


S>Ну, как пример: "для создания бизнес-класса нужно вызвать несколько методов" vs "создание бизнес-класса через обычный конструктор(через метод в контейнере-владельце".


S>Помимо "второй круче" можно привести объективные критерии:

S>1. Частота использования. Часто используемые операции должны быть максимально простыми — иначе полкода вместо собственно логики будет заниматься ублажением нашего API
S>2. Очевидность. Контракт первого варианта не задокументирован в API, т.е. научиться его использовать можно или через заучивание документации, или через ловлю ошибок под отладчиком. Learn through pain — не наш метод
S>3. Комбинирование. Если мы создаём одновременно два бизнес-объекта — важен ли порядок MarkNew? Будут ли побочные эффекты?
S>ну и так далее

По пунктам 1,2 мне тоже кажется, что MarkNew и т.п. это перебор. Но посмотрим, что парень скажет.
Насчет 3, это только одна из непоняток. Еще:

1. Что если эта функциональность уже реализована в каком-то "провайдере" репозитория, зачем городить огород. Если уже делать что-то такое, так это должно быть нечно относящееся к репозиторию, а не UoW
2. Если мы что-то изменили и пометили MarkNew, а потом в этой же транзакции (или в том же UofW неважно) читаем данные которые включают в себя измененные, чего делать? Сливать их из UoW и результатов репозитория? Что если это не CRUD, а что-то посерьезней, где какая-нибудь простецкая логика зашита в метод репозитория, что тогда?

На второй я вообще не понимаю как можно ответить. Но посмотрим Есть у кого спросить

S>На мой взгляд, есть два универсальных критерия правильного дизайна:

S>1. Количество WTF/сек при изучении API и при решении похожих задач.
S>2. Количество хелперов к API
S>Если оба пункта зашкаливают — у нас проблемы

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

D>>... поэтому когда я такое вижу, у меня такое ощущение, что я чего-то конкретно не понял

S>1-в-1. Две версии:
S>1. Для автора сценарии с использованием IUnitOfWork некритичны, основная часть сводится к CRUD. Такое характерно для легковесных сайтов и да, там что-то более громоздкое не особо нужно. Поскольку для подобных вещей уже есть BLToolkit и аналоги, изобретать что-то своё я не вижу никакого смысла.
S>2. Автор показал только костяк, а в реальном коде прячет его внутрь репозитария. Непохоже конечно, но мало ли

Будем надеятся, наш новый программист хотя бы частично мне это объяснит

Спасибо
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.