Здравствуйте, gandjustas, Вы писали:
G>Мой подход — использовать ORM G>Еще я применяю IOC иногда
Легче найти тех, кто их не использует. Но это не более чем технические детали реализации, если они не поставлены во главу угла и их уши не торчат в каждом файле (объекты от ORM размазаны тонким слоем по всем слоям, контейнер-помойка доступен отовсюду и может вернуть любой объект).
Задача внятной архитектуры — надёжно изолировать технические детали от других частей программы, чтобы впоследствии эти детали можно было независимо менять. Разделяй и властвуй.
G>И вот такие подходы к архитектуре http://blog.gandjustas.ru/categories/#Архиетктураприложений
Статья про DDD меня расстроила, очень поверхностный взгляд на DDD. В статье про ортогональность проповедуется анемичная модель данных. Остальные скорее вводные статьи по отдельным темам.
Каждому своё, но раз в пару лет надо критически анализировать свои подходы. Раньше, начиная проект, я рано или поздно упирался в стену. Анемичные модели данных, объекты ORM заменяли мне модель предметной области, бизнес-правила были вообще где попало. В начале код писался легко и даже работал, но потом когда возникали новые обстоятельства — старый код становился препятствием на пути реализации новых фич. Мне довольно легко удавалось реализовывать только отдельные изолированные части — что-то вроде библиотек для проекта — какой-нибудь парсер, или алгоритм. А проектирование всей программы как единого целого двигалось со скрипом. Потом, посидев за книжками и статьями, я наконец-то начал понимать роль инкапсуляции в проектировании и важность оформления ядра программы как независимого целого (предметной области). Кстати оказались доклады Роберта Мартина по архитектуре, проектировать программы стало проще.
Здравствуйте, Sinclair, Вы писали:
S>А всё оттого что в кузнице не было гвоздя вначале кто-то решил, что засосать тыщу записей в память, а потом по одной фигачить update на их основе — это нормальная стратегия. А чо — в тестах в single-user моде всё исполняется 10 секунд, что должно бы всех устроить.
Это аргументы в стиле подкладывания рельсы японской пиле. Да мало ли какую страшную картинку можно нарисовать, какое отношение работа с БД имеет к внятной архитектуре? А что, если изменение зарплат сотрудников на выходе имеет не SQL-запрос, а документ на подпись начальнику? А потом подписанный документ с штрих-кодом сканируется и выполняется нужное действие, опять же не через SQL, а запросом в облако к нереляционному хранилищу?
Да, отполированные знания тонкостей работы какой-то БД важны, но БД — не более, чем деталь реализации.
Здравствуйте, Vladek, Вы писали:
V>Задача внятной архитектуры — надёжно изолировать технические детали от других частей программы, чтобы впоследствии эти детали можно было независимо менять. Разделяй и властвуй.
Для этого применяю, внимание, функциональную декомпозицию!
Да-да, оказывается можно сделать функцию, которая вызывает другую функцию, а еще можно сделать функцию, которая вызывает несколько функций, передавай результаты одной в другую. Скоро уже 100 лет, как этот метод надежно изолирует части программы друг от друга.
Странно что еще паттерн для этого не придумали.
G>>И вот такие подходы к архитектуре http://blog.gandjustas.ru/categories/#Архиетктураприложений
V>Статья про DDD меня расстроила, очень поверхностный взгляд на DDD. В статье про ортогональность проповедуется анемичная модель данных. Остальные скорее вводные статьи по отдельным темам.
Ну хоть прочитал.
V>Каждому своё, но раз в пару лет надо критически анализировать свои подходы. Раньше, начиная проект, я рано или поздно упирался в стену. Анемичные модели данных, объекты ORM заменяли мне модель предметной области, бизнес-правила были вообще где попало. В начале код писался легко и даже работал, но потом когда возникали новые обстоятельства — старый код становился препятствием на пути реализации новых фич. Мне довольно легко удавалось реализовывать только отдельные изолированные части — что-то вроде библиотек для проекта — какой-нибудь парсер, или алгоритм. А проектирование всей программы как единого целого двигалось со скрипом. Потом, посидев за книжками и статьями, я наконец-то начал понимать роль инкапсуляции в проектировании и важность оформления ядра программы как независимого целого (предметной области). Кстати оказались доклады Роберта Мартина по архитектуре, проектировать программы стало проще.
Мне кажется что ты факты подменяешь ощущениями. Я вообще заметил что ощущение "праведности" помогает переживать любые страдания, в том числе дорогую поддержку, глючность программ и поднобные неприятности.
Мне довелось участвовать в проекте, где укушенные DDD люди делали "ядро". Было даже две команды — "ядро" и "не-ядро". Ядро работало прекрасно, как уверяли разработчики, все остальное глючило адски, была проблема с быстродействием. Меня как раз позвали помочь с этим. Выяснилось что из-за "доменной модели" из базы поднимается по 3МБ на каждый запрос. Я с огромным трудом уболтал часть сценариев реализовать через linq запросы с проекциями. Понадобилось часть логики вынести из модели в отдельные функции, зато ключевые сценарии заработали с приемлемой скоростью.
Насколько я знаю техдиректора результат вдохновил, он насильно убрал разделения "ядра" и "не-ядра" и еще часть кода переписали в таком духе, оказавшись от модели.
Очевидно что причиной проблем была модель, но об этом никто ни разу не сказал.
Спустя 3 года я читал Психологию влияния Чалдини, у него есть глава, где он описывает секту. Так вот отношение людей к "ядру" очень напоминало секту.
ЗЫ. Не существует ни одного доказательства, проверяемых данных, что DDD в коде (ака "ядро") хоть как-то улучшает характеристики программы. Зато контр примеров, где наличие "ядра" может ухудшить — полно.
Здравствуйте, 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
V>>Эффективный код в отношении изменения зарплаты сотрудников должен быть атомарным. Мы это достигаем за счёт аккумулирования всех изменений в одном вызове SaveChangesAsync. Будет он выполняться 50 мс или 10 секунд — дело десятое и легко решаемое, для задачи гораздо важнее гарантированное выполнение или чёткий откат в случае сбоя. G>Прости, а как ты обеспечишь атомарность для: G>1) Веб-сервиса, которому нужно PUT на каждый объект
Будет определяться возможностями веб-сервиса. Либо отправлять записи по одной, либо скопом. Делать несколько попыток, применить circuit breaker.
Это определяется требованиями к конкретной программе, а ты меня постоянно пытаешься перетянуть на обсуждение технических деталей реализации примера. Это имеет слабое отношение к архитектуре, отполированные запросы к БД и веб-сервисам мало помогут при невнятной архитектуре. Программа превратится в несопровождаемое нечто и обузу для пользователей. Архитектура про изоляцию технических деталей (типа запросов выше) и управление зависимостями между отдельными частями программы.
G>2) List<T> в памяти?
Ну тут просто и сбоев не будет. G>Или у тебя контракт репозитария меняется в зависимости от реализации?
Задача репозитория — выбрать нужное множество сотрудников по какому-то критерию, он не отвечает за изменения их данных.
G>>>Не знаю как ты, а я все это уже проходил. За каждым из приведенных выше вопросов стоит косяк в реальной программе, который привел к реальным потерям. V>>С изменением требований я сталкиваюсь постоянно. Сегодня надо работать с таким источником данных, завтра с ещё одним. Использую описанный подход и не имею особых проблем. G>Это значит что у тебя приложения примитивные, возможно однопользовательские. Тогда действительно можно репозитариями пользоваться.
Это уже вторая попытка померяться длиной, видимо, записей в БД. Компенсируешь анатомические особенности?
Здравствуйте, Vladek, Вы писали:
V>Это аргументы в стиле подкладывания рельсы японской пиле. Да мало ли какую страшную картинку можно нарисовать, какое отношение работа с БД имеет к внятной архитектуре? А что, если изменение зарплат сотрудников на выходе имеет не SQL-запрос, а документ на подпись начальнику? А потом подписанный документ с штрих-кодом сканируется и выполняется нужное действие, опять же не через SQL, а запросом в облако к нереляционному хранилищу?
В конце концов в базе данных должны произойти изменения, должен выполниться тот самый апдейт.
Ты пытаешься уменьшить важность проблемы, тип что повышение ЗП нечастое явление и апдейту предшествует много других операций.
Но ты забыл что повышение ЗП было выбрано как пример, можно найти миллионы операций, которые часто выполняются и для них твоя логика "уменьшения проблемы" просто не сработает.
А еще на практике неоднократно видел, такой стиль работы с базой проникает во все части программы, и тормозит вообще все.
V>Да, отполированные знания тонкостей работы какой-то БД важны, но БД — не более, чем деталь реализации.
БД — серьезное архитектурное ограничение. Изменение БД может поменять архитектуру всей системы.
Знаю один курьезный случай, когда группу разработчиков обученных T-SQL отправили писать код для оракла. Казалось бы в чем проблема, синтаксис выучат и нафигачат код, примерно такой же, как во всех приложениях.
Но выяснилось на ходу, что в оракле индексы работают по-другому и честную сериализуемость оракл не поддерживает. Переписали тогда очень много.
Здравствуйте, gandjustas, Вы писали:
V>>Да, отполированные знания тонкостей работы какой-то БД важны, но БД — не более, чем деталь реализации. G>БД — серьезное архитектурное ограничение. Изменение БД может поменять архитектуру всей системы.
Здравствуйте, Vladek, Вы писали:
V>Из объекта предметной области надо извлечь данные — для передачи в хранилище. Хранилище на входе получает простые структуры данных DTO, а не объекты предметной области. Как осуществляется маппинг — скорее всего, объект предметной области может вернуть структуру с нужными данными.
Я перестаю понимать что ты пишешь. Приведи пример кода репозитория, который list<T> использует.
V>Ты тоже выдаёшь мне предположения про ужасы работы с БД, как и другой человек постом выше. Там я уже ответил про это: http://rsdn.ru/forum/design/6484598.1
И ты тоже пытаешься уйти от ответа.
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>Это уже вторая попытка померяться длиной, видимо, записей в БД. Компенсируешь анатомические особенности?
Конечно. Но это не значит, что у тебе не примитивные приложения или вообще есть реальный опыт того, о чем ты пытаешься рассказать. Ты даже на банальные вопросы о репозитории ответить не можешь.
Здравствуйте, Vladek, Вы писали:
V>Здравствуйте, gandjustas, Вы писали:
V>>>Да, отполированные знания тонкостей работы какой-то БД важны, но БД — не более, чем деталь реализации. G>>БД — серьезное архитектурное ограничение. Изменение БД может поменять архитектуру всей системы.
V>Надоело повторяться, уже понятно, что каждый останется при своём мнении.
Понимаешь в чем фишка, у меня есть реальный опыт, который подкрепляет мою позицию. А ты ссылаешься на чувака, чье достижение — публикация пары популярных книжек сомнительного содержания. Причем у мартина конкретики еще меньше, чем в местных обсуждениях.
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, Vladek, Вы писали:
V>>Из объекта предметной области надо извлечь данные — для передачи в хранилище. Хранилище на входе получает простые структуры данных DTO, а не объекты предметной области. Как осуществляется маппинг — скорее всего, объект предметной области может вернуть структуру с нужными данными. G>Я перестаю понимать что ты пишешь. Приведи пример кода репозитория, который list<T> использует.
Репозиторий использует хранилище для извлечения данных и, например, фабрику для создания объектов предметной области на основе этих данных. Хранилище и репозиторий — тут разные сущности. Трудно представить обёртку над List<T> в качестве хранилища?
V>>Ты тоже выдаёшь мне предположения про ужасы работы с БД, как и другой человек постом выше. Там я уже ответил про это: http://rsdn.ru/forum/design/6484598.1
Я пишу тут "для поисковика" и не для спора с категоричными оппонентами. Пытаюсь дать направление для дальнейшего размышления тем, кто тут потом будет разбираться с проблемой репозиториев. Я имел такие вопросы раньше, на данный момент мне ближе всего подход Роберта Мартина к архитектуре.
Проблемы с хитроумными запросами к БД у меня много времени не занимали, было достаточно подумать и/или погуглить (намного чаще). Поэтому мне пофиг на локальные технические затруднения, мой опыт говорит об успешном их преодолении, БД там или веб-сервисы. Я тоже могу чемпионить с конкретной проблемой, просто не люблю.
Ситуаций, когда простое добавление новой фичи заставляло меня ковыряться в разных частях программы и переписывать и перепроектировать большие куски кода, к сожалению, возникали намного чаще. Поэтому архитектура с высоты птичьего полёта интересует меня в первую очередь — это залог того, что в будущем, при развитии программы, она не рухнет под собственным весом. А проблемы отдельных методов, ой выполняющихся на 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>Конечно. Но это не значит, что у тебе не примитивные приложения или вообще есть реальный опыт того, о чем ты пытаешься рассказать. Ты даже на банальные вопросы о репозитории ответить не можешь.
У тебя великое прошлое с кучей успешно преодолённых косяков, но внятного описания твоих текущих взглядов на архитектуру не видно. Этот не прав, тот не прав, третий вообще дурак без проектов, вот это приведёт к огромным потерям, и так далее. Удачи тогда, чемпион.
V>Репозиторий использует хранилище для извлечения данных и, например, фабрику для создания объектов предметной области на основе этих данных. Хранилище и репозиторий — тут разные сущности. Трудно представить обёртку над List<T> в качестве хранилища?
Мне трудно представить себе обертку, которая обладает свойствами, о которых ты пишешь.
V>>>Ты тоже выдаёшь мне предположения про ужасы работы с БД, как и другой человек постом выше. Там я уже ответил про это: http://rsdn.ru/forum/design/6484598.1
G>>И ты тоже пытаешься уйти от ответа.
V>Я пишу тут "для поисковика" и не для спора с категоричными оппонентами. Пытаюсь дать направление для дальнейшего размышления тем, кто тут потом будет разбираться с проблемой репозиториев. Я имел такие вопросы раньше, на данный момент мне ближе всего подход Роберта Мартина к архитектуре.
У роберта мартина подхода практически нет. У него несколько идей и приемов, которым он даже не автор, внятно сформулировать не может, которые в некоторых контекстах банально ошибочные.
V>Ситуаций, когда простое добавление новой фичи заставляло меня ковыряться в разных частях программы и переписывать и перепроектировать большие куски кода, к сожалению, возникали намного чаще. Поэтому архитектура с высоты птичьего полёта интересует меня в первую очередь — это залог того, что в будущем, при развитии программы, она не рухнет под собственным весом. А проблемы отдельных методов, ой выполняющихся на 50 мс дольше, я успешно исправлю по ходу дела.
Я и говорю, что у тебя не было приложений, работающих с серьезными объемами под нагрузкой.
V>Репозитарий использует хранилище. Его задача отбирать множество объектов предметной области по какому-то критерию. Из хранилища он берёт данные, на основе этих данных строит объекты предметной области. Тут тебе рисуется страшная картинка про медленный запрос к БД из твоего великого прошлого (100 гигов данных, 1000 пользователей) и ты пугаешься.
Не уходи от вопросов:
1) Ты сказал что SaveChangesAsync должен быть атомарным, но веб-сервис не поддерживает атомарного обновления множества записей. Что ты будешь делать в этом случае?
2) Ты реально считаешь, что архитектура не определяется требованиям к конкретной программе? Тогда, прости, чем она определяется?
3) Если твой репозиторий обладает свойствами, которыми не обладает хранилище — какие твои действия?
V>Страшно использовать репозитории — не используй.
Это не страшно, это бессмысленно.
V>Что сложного в заполнении List<T> данными? Сбои практически исключены. Можно просто считать все операции атомарными.
Еще раз спрашиваю — как ты добьёшься атомарности SaveChangesAsync для хранилища в виде List<T>?
Приведи код SaveChangesAsync для хранилища в виде List<T>.
Проблема-то не в сбоях, а в том, что ты можешь ограничения модели нарушить, типа уникальности.
Здравствуйте, Vladek, Вы писали:
V>Это аргументы в стиле подкладывания рельсы японской пиле.
Да нет, это результаты многолетнего негативного опыта. Когда пять лет трахаешься вот с такой вот реальностью, и уже думаешь что другого выхода и нет, и что 70% роллбэков из-за conflicting change — это нормально. Или что апгрейд с одной версии на другую так и должен идти 30 часов, "а что ж вы хотели — там данных два гигабайта!".
Потом после волшебных пенделей выясняется, что тот же самый апгрейд можно написать в виде пары sql-стейтментов, и он пройдёт на тех же данных за 10 минут.
И вот когда выясняется, что можно писать вот такие эффективные батчи, и при этом не надо отказываться от логики на нормальном современном ЯП, то наступает прямо-таки эйфория.
V> Да мало ли какую страшную картинку можно нарисовать, какое отношение работа с БД имеет к внятной архитектуре?
Прямое. Если у вас архитектура построена на lazy load и change tracking, то ваше приложение — гарантированный тормоз.
V>А что, если изменение зарплат сотрудников на выходе имеет не SQL-запрос, а документ на подпись начальнику?
Главное, чтобы для формирования этого документа не надо было просасывать всю базу в память клиента по тонкому каналу в открытой транзакции. V>А потом подписанный документ с штрих-кодом сканируется и выполняется нужное действие, опять же не через SQL, а запросом в облако к нереляционному хранилищу?
А, ну вот типичный случай продажи нереляционного хранилища тем, кто так и не научился работать с реляционным хранилищем.
Потому что если после сканирования документа мы опять начинаем грузить в память список сотрудников, потом lazy load на их контракты, и потом опять 200 отдельных update стейтментов в реляционке — это умрёт.
V>Да, отполированные знания тонкостей работы какой-то БД важны, но БД — не более, чем деталь реализации.
Вам выражение "дырявые абстракции" что-нибудь говорит?
Архитекторы, которые считают существенные подробности несущественными, обречены писать плохие решения.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, gandjustas, Вы писали: V>>Что сложного в заполнении List<T> данными? Сбои практически исключены. Можно просто считать все операции атомарными. G>Еще раз спрашиваю — как ты добьёшься атомарности SaveChangesAsync для хранилища в виде List<T>?
Да очень просто. Он склонирует лист, применит к нему изменения, а при успехе заменит _currentList = newList.
При сбое newList просто соберётся сборщиком мусора. Вот тебе и вся атомарность
Вон, говорят во всяких монгах используется единственный мутекс уровня базы для обеспечения атомарности — и ничо, не жужжат
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Vladek, Вы писали:
V>Задача внятной архитектуры — надёжно изолировать технические детали от других частей программы, чтобы впоследствии эти детали можно было независимо менять. Разделяй и властвуй.
Ну так вот выбор правильной гранулярности этой изоляции и определяет все дальнейшие успехи.
Иначе оказывается, что разработчики из кожи вон лезут, чтобы эту изоляцию обойти — т.к. в реальных сценариях выбранная архитектором-астронавтом изоляция не даёт ничего сделать.
Я могу прямо тут накидать примеров, где произвольно проведённая граница изоляции убъёт систему. Вот вы там упоминали про то, что "у веб сервиса как правило есть метод поиска".
Ну так это же и есть Архитектура! Если вы забыли приделать к сервису метод поиска — то всё, туши свет, сливай воду.
А если в методе поиска нет возможности постраничного отъёма, то наоборот — жги газеты, лей из шланга. Потому что эти несущественные подробности на определённых объёмах приводят к тому, что ваша транзакция, обязанная по ТЗ быть атомарной, не заканчивается никогда — не хватает стабильности соединения между компонентами, чтобы вычитать все полтора миллиона billing detail records в один присест.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
G>>Еще раз спрашиваю — как ты добьёшься атомарности SaveChangesAsync для хранилища в виде List<T>? S>Да очень просто. Он склонирует лист, применит к нему изменения, а при успехе заменит _currentList = newList. S>При сбое newList просто соберётся сборщиком мусора. Вот тебе и вся атомарность S>Вон, говорят во всяких монгах используется единственный мутекс уровня базы для обеспечения атомарности — и ничо, не жужжат
Я придумал optimistic locking через Interlocked.CompareExchange.
Но всетаки хотел, чтобы собеседник свой вариант озвучил, интересно было бы посмотреть на реализацию repository, который обладает всеми заявленными свойствами.
Здравствуйте, Sinclair, Вы писали:
V>> Да мало ли какую страшную картинку можно нарисовать, какое отношение работа с БД имеет к внятной архитектуре? S>Прямое. Если у вас архитектура построена на lazy load и change tracking, то ваше приложение — гарантированный тормоз.
На этом построена архитектура ORM — приложение от него вообще не должно зависеть, оно должно его использовать. Грубо говоря, классы для ORM должны лежать в отдельной сборке с атрибутом internal.
V>>Да, отполированные знания тонкостей работы какой-то БД важны, но БД — не более, чем деталь реализации. S>Вам выражение "дырявые абстракции" что-нибудь говорит? S>Архитекторы, которые считают существенные подробности несущественными, обречены писать плохие решения.
А может вам стать администраторами баз данных — там SQL, батчи, скорость, ветер, свист в ушах, а? Никаких репозиториев, классов, абстракций.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, Vladek, Вы писали:
V>>Задача внятной архитектуры — надёжно изолировать технические детали от других частей программы, чтобы впоследствии эти детали можно было независимо менять. Разделяй и властвуй. S>Ну так вот выбор правильной гранулярности этой изоляции и определяет все дальнейшие успехи. S>Иначе оказывается, что разработчики из кожи вон лезут, чтобы эту изоляцию обойти — т.к. в реальных сценариях выбранная архитектором-астронавтом изоляция не даёт ничего сделать. S>Я могу прямо тут накидать примеров, где произвольно проведённая граница изоляции убъёт систему. Вот вы там упоминали про то, что "у веб сервиса как правило есть метод поиска". S>Ну так это же и есть Архитектура! Если вы забыли приделать к сервису метод поиска — то всё, туши свет, сливай воду. S>А если в методе поиска нет возможности постраничного отъёма, то наоборот — жги газеты, лей из шланга. Потому что эти несущественные подробности на определённых объёмах приводят к тому, что ваша транзакция, обязанная по ТЗ быть атомарной, не заканчивается никогда — не хватает стабильности соединения между компонентами, чтобы вычитать все полтора миллиона billing detail records в один присест.
Если тебе нужна быстрая выборка из бд — ты берёшь и придумываешь для этого внятную модель, и тут же реализовываешь её за 5 минут. Зачем тебе пробуждать всю модель предметной области для простого чтения?
Здравствуйте, Vladek, Вы писали:
V>Если тебе нужна быстрая выборка из бд — ты берёшь и придумываешь для этого внятную модель, и тут же реализовываешь её за 5 минут. Зачем тебе пробуждать всю модель предметной области для простого чтения?
А зачем придумывать? Она уже есть — модель базы данных, которая генерируется из таблиц и связей или наоборот набор классов, из которых создается БД.
Простое чтение — 98% работы приложения, непонятно зачем для остальных 2% еще какая-то модель нужна.
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, Vladek, Вы писали:
V>>Если тебе нужна быстрая выборка из бд — ты берёшь и придумываешь для этого внятную модель, и тут же реализовываешь её за 5 минут. Зачем тебе пробуждать всю модель предметной области для простого чтения?
G>А зачем придумывать? Она уже есть — модель базы данных, которая генерируется из таблиц и связей или наоборот набор классов, из которых создается БД. G>Простое чтение — 98% работы приложения, непонятно зачем для остальных 2% еще какая-то модель нужна.
Здравствуйте, 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>.
EmployeeData — содержит изменённые данные, предназначены для сохранения, простой DTO. Хранилище (gateway) на основе List<T> просто сравнит его со своим списком и заменит прежние версии. Новые записи, которых не окажется в списке — могут быть просто добавлены или расценены как сбой (тогда произойдёт откат, в список вернутся прежние версии).
G>Проблема-то не в сбоях, а в том, что ты можешь ограничения модели нарушить, типа уникальности.
Здравствуйте, gandjustas, Вы писали:
_>>На мой взгляд, в юнит тестах вызов такого метода (сохраняющего в базу) проверять не надо. G>Тогда ценность юнит-теста падает до нуля. Зеленый тест перестает показывать что метод работает.
Понятно.
JFYI
Дело не в цвете, а в том, что вы, т.о., по сути, проверяете внутренний алгоритм. Оптимальнее будет проверять сценарии работы. При этом допускается, что Х.З. как именно реализован метод объекта, сохраняющий данные (внутри он может дергать SaveChangesAsync, SaveChanges, или еще что-то). Юнит-тестами проверяется сценарий, интеграционными проверяется связка (что данные прошли и записались). Момент с разработчиками не выносит критики: если слишком долго, пилится на тесты "по-меньше" или CI.