Здравствуйте, gandjustas, Вы писали:
G>Да вот у меня как-то не было примеров, чтобы одна и та же программа могла работать с любым источником данных. Скорее обратное. Даже небольшие различия в разных движках БД приводили к различному коду, даже с применением ORM.
У тебя есть приложение, которое берёт данные из корпоративной бд. Ваша контора слилась с другой конторой, приложение должно теперь обслуживать и её, однако данные та другая контора хранила в каком-то облаке с доступом по wep api и что-то менять будет дорого, поэтому приложение теперь должно уметь общаться с тем веб-сервисом, а бизнес-процесс менять не надо.
Код доступа к бд через ORM менять не надо, надо просто добавить ещё один.
Ох сколько вопросов сразу:
1) В случае List<T> надо все синхронные операции в async\await оборачивать?
2) В случае базы данных вместо одного апдейта в базу улетит 100500?
3) Откуда вообще у веб-сервиса метод с фильтрами по категории или будут тянуться все сотрудники, а потом фильтр пойдет на клиенте?
У вас получился заведомо неэффективный код для каждого из случаев И сразу же отрублены все возможности улучшить ситуацию.
Здравствуйте, Vladek, Вы писали:
V>Здравствуйте, gandjustas, Вы писали:
G>>Да вот у меня как-то не было примеров, чтобы одна и та же программа могла работать с любым источником данных. Скорее обратное. Даже небольшие различия в разных движках БД приводили к различному коду, даже с применением ORM.
V>У тебя есть приложение, которое берёт данные из корпоративной бд. Ваша контора слилась с другой конторой, приложение должно теперь обслуживать и её, однако данные та другая контора хранила в каком-то облаке с доступом по wep api и что-то менять будет дорого, поэтому приложение теперь должно уметь общаться с тем веб-сервисом, а бизнес-процесс менять не надо.
Тупо загружается из облако все в базу и проблема на этом кончается.
И, как это ни странно, я как раз такой сценарий реализовывал на практике при слиянии компаний.
V>Код доступа к бд через ORM менять не надо, надо просто добавить ещё один.
Никто такой фигней не занимается. Проще перелить данные в один источник, чем заниматься таким геморроем.
Здравствуйте, Sharov, Вы писали:
G>>Архитектура это не тогда, когда много паттернов, а когда сделано ровно столько, сколько нужно, чтобы решить задачу и не изобретать велосипеды.
S>Для этого и придуманы паттерны. Современная архитектура -- это взаимодействие паттерноподобных сущностей, абстракций. А гофосккие паттерны -- платоновский идеал.
Паттерны носят описательный характер. Они описывают то, что уже есть в коде, никто их отдельно не придумывал и вообще вменяемый разработчик переизобретает их сам, даже не читая книжек про паттерны. Хорошая архитектура не та, в которой много паттернов, а та, которую легко описать.
Здравствуйте, IT, Вы писали:
IT>Попробуй. Хорошо писать как бог на душу положит мало у кого получается. Это вывсшее дао программирования. Я пока таких людей не встречал.
У кода две функции — выполнять поставленную задачу, быть готовым к изменениям. Любой код первую функцию выполняет худо-бедно, человек освоивший программирование такой код напишет. Это код можно написать как угодно.
Проблемы возникают со второй функцией кода — быть готовым к изменениям. Для этого и нужна архитектура.
Подход, когда код сначала пишется с наскока, а потом, будучи испытанным в деле, рефакторится в нечто вменяемое, работает довольно неплохо если с самого начала проектирование архитектуры буксовало.
Здравствуйте, gandjustas, Вы писали:
G>Ох сколько вопросов сразу: G>1) В случае List<T> надо все синхронные операции в async\await оборачивать?
Тривиальная задача. G>2) В случае базы данных вместо одного апдейта в базу улетит 100500?
Не обязательно. Скорее всего, не важно. G>3) Откуда вообще у веб-сервиса метод с фильтрами по категории или будут тянуться все сотрудники, а потом фильтр пойдет на клиенте?
Метод с фильтрами по категории у нашего репозитория, а как он реализован — дело десятое. Главное, код работы с зарплатой от этого никак не зависит.
G>У вас получился заведомо неэффективный код для каждого из случаев И сразу же отрублены все возможности улучшить ситуацию.
Что такое "эффективный код" в случае изменения важной информации о сотрудниках предприятия?
Здравствуйте, gandjustas, Вы писали:
V>>У тебя есть приложение, которое берёт данные из корпоративной бд. Ваша контора слилась с другой конторой, приложение должно теперь обслуживать и её, однако данные та другая контора хранила в каком-то облаке с доступом по wep api и что-то менять будет дорого, поэтому приложение теперь должно уметь общаться с тем веб-сервисом, а бизнес-процесс менять не надо.
G>Тупо загружается из облако все в базу и проблема на этом кончается. G>И, как это ни странно, я как раз такой сценарий реализовывал на практике при слиянии компаний.
И руководство компаний тебе просто доверилось? А потом сразу упразднили во второй компании старый процесс и перешли на новый? Почему слиянием БД занимался программист, а не команда администраторов БД и бизнес-аналитиков?
V>>Код доступа к бд через ORM менять не надо, надо просто добавить ещё один. G>Никто такой фигней не занимается. Проще перелить данные в один источник, чем заниматься таким геморроем.
Занимаются те, для кого это совсем не гемморой, кто хорошо изолировал свой код от источников данных.
Здравствуйте, 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>Что такое "эффективный код" в случае изменения важной информации о сотрудниках предприятия?
Эффективный код, который не делает лишней работы или соотношение лишней или доля лишней работы крайне мала. Чем он занимается — неважно, потому что имеется тенденция распространения стиля на все приложение. То что кажется в одном месте маленькой неэффективностью копируется в другое место, где появляются тормоза на несколько десятков секунд.
Не знаю как ты, а я все это уже проходил. За каждым из приведенных выше вопросов стоит косяк в реальной программе, который привел к реальным потерям.
Здравствуйте, Vladek, Вы писали:
G>>Тупо загружается из облако все в базу и проблема на этом кончается. G>>И, как это ни странно, я как раз такой сценарий реализовывал на практике при слиянии компаний. V>И руководство компаний тебе просто доверилось? А потом сразу упразднили во второй компании старый процесс и перешли на новый? Почему слиянием БД занимался программист, а не команда администраторов БД и бизнес-аналитиков?
Примерно так было. Было две CRM, с одной стороны самописная, с другой нет. Из самописной выгрузили послезные данные и загрузили в рабочую.
Не просто доверилось, а когда посчитали затраты на доработку самописной + такую архитектуру как ты предлагаешь, так сразу просили перелить данные побыстрее.
G>>Никто такой фигней не занимается. Проще перелить данные в один источник, чем заниматься таким геморроем. V>Занимаются те, для кого это совсем не гемморой, кто хорошо изолировал свой код от источников данных.
У меня начали закрадываться подозрения, что ты ни одного реального приложения не сделал. Чтобы сотня ГБ данных и хотя бы 1000 пользователей была.
Здравствуйте, 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>Не знаю как ты, а я все это уже проходил. За каждым из приведенных выше вопросов стоит косяк в реальной программе, который привел к реальным потерям.
С изменением требований я сталкиваюсь постоянно. Сегодня надо работать с таким источником данных, завтра с ещё одним. Использую описанный подход и не имею особых проблем.
Здравствуйте, IT, Вы писали:
IT>Нет. Основная функция DAL — изоляция работы с БД от остальной части приложения. То, что это обладает ещё какими-то другими свойствами — это побочный эффект. Например, ту же обстракцию БД вполне хорошо, даже лучше чем DAL выполняет LINQ, но принципиально по-другому.
Два момента:
1. LINQ не "выполняет абстракцию БД", потому что это интерфейс доступа к наборам произвольных данных (и первый же пример из MSDN об этом, поскольку использует LINQ на обычном списке).
2. DAL в случае с LINQ никуда не девается, просто всю работу за вас делает вендор ORM-решения, которое и представляет собой тот самый DAL, абстрагирующий вас от особенностей работы с БД с помощью LINQ.
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, Vladek, Вы писали:
G>>>Тупо загружается из облако все в базу и проблема на этом кончается. G>>>И, как это ни странно, я как раз такой сценарий реализовывал на практике при слиянии компаний. V>>И руководство компаний тебе просто доверилось? А потом сразу упразднили во второй компании старый процесс и перешли на новый? Почему слиянием БД занимался программист, а не команда администраторов БД и бизнес-аналитиков? G>Примерно так было. Было две CRM, с одной стороны самописная, с другой нет. Из самописной выгрузили послезные данные и загрузили в рабочую. G>Не просто доверилось, а когда посчитали затраты на доработку самописной + такую архитектуру как ты предлагаешь, так сразу просили перелить данные побыстрее.
Тогда пример не подходит. Вернее, твой опыт про другое — внутренний проект закрыли, данные импортировали в сторонний продукт. Нужна гибкая архитектура, способная подстроиться под новый источник данных. Репозитории как способ общения ядра программы с внешним миром — тут и пригождаются.
G>>>Никто такой фигней не занимается. Проще перелить данные в один источник, чем заниматься таким геморроем. V>>Занимаются те, для кого это совсем не гемморой, кто хорошо изолировал свой код от источников данных. G>У меня начали закрадываться подозрения, что ты ни одного реального приложения не сделал. Чтобы сотня ГБ данных и хотя бы 1000 пользователей была.
Лучше опиши свой подход к архитектуре, которая однако благополучно переживала потрясения типа описанных выше.
Здравствуйте, Baudolino, Вы писали:
B>1. LINQ не "выполняет абстракцию БД", потому что это интерфейс доступа к наборам произвольных данных (и первый же пример из MSDN об этом, поскольку использует LINQ на обычном списке).
Это у кого как и смотря что мы под этим понимаем.
B>2. DAL в случае с LINQ никуда не девается, просто всю работу за вас делает вендор ORM-решения, которое и представляет собой тот самый DAL, абстрагирующий вас от особенностей работы с БД с помощью LINQ.
Нет. Функция изоляции переехала в LINQ. А сам DAL больше нафиг не нужен.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Vladek, Вы писали:
V>Тогда пример не подходит. Вернее, твой опыт про другое — внутренний проект закрыли, данные импортировали в сторонний продукт. Нужна гибкая архитектура, способная подстроиться под новый источник данных. Репозитории как способ общения ядра программы с внешним миром — тут и пригождаются.
Никому твоя архитектура не нужна. Нужно чтобы быстро работало и быстро вносились изменения.
Самый быстрый способ в случае объединения двух систем — перелить данные одной в другую.
G>>>>Никто такой фигней не занимается. Проще перелить данные в один источник, чем заниматься таким геморроем. V>>>Занимаются те, для кого это совсем не гемморой, кто хорошо изолировал свой код от источников данных. G>>У меня начали закрадываться подозрения, что ты ни одного реального приложения не сделал. Чтобы сотня ГБ данных и хотя бы 1000 пользователей была. V>Лучше опиши свой подход к архитектуре, которая однако благополучно переживала потрясения типа описанных выше.
Здравствуйте, gandjustas, Вы писали:
G>Кстати что SaveChangesAsync будет в этом случае делать? Как гарантировать в тестах что он вызывается, если мы изначально делаем на List<T>, а потом "подменяем storage"?
Вы действительно проверяете в тесте вызовы методов? Если да, то зачем именно этот и как?
На мой взгляд, в юнит тестах вызов такого метода (сохраняющего в базу) проверять не надо. И в интеграциогнных тестах не следует проверять его вызов, а только наличие данных в хранилище после "действий". Поэтому не понимаю вопрос про проверку.
Здравствуйте, another_coder, Вы писали:
_>Здравствуйте, gandjustas, Вы писали:
G>>Кстати что SaveChangesAsync будет в этом случае делать? Как гарантировать в тестах что он вызывается, если мы изначально делаем на List<T>, а потом "подменяем storage"?
_>Вы действительно проверяете в тесте вызовы методов? Если да, то зачем именно этот и как?
Затем что был проект, покрытый юнит тестами и они не падали, но не работало. Оказалось, что тест проверял только добавление в коллекцию объекта, а метод сохранения не вызывался.
Проверяется банально моками.
_>На мой взгляд, в юнит тестах вызов такого метода (сохраняющего в базу) проверять не надо.
Тогда ценность юнит-теста падает до нуля. Зеленый тест перестает показывать что метод работает.
_>И в интеграциогнных тестах не следует проверять его вызов, а только наличие данных в хранилище после "действий". Поэтому не понимаю вопрос про проверку.
В интеграционных да, но разработчики часто не прогоняют интеграционные тесты, а юнит-тесты, особенно с ReSharper прогоняются всегда.
V>Эффективный код в отношении изменения зарплаты сотрудников должен быть атомарным. Мы это достигаем за счёт аккумулирования всех изменений в одном вызове SaveChangesAsync. Будет он выполняться 50 мс или 10 секунд — дело десятое и легко решаемое, для задачи гораздо важнее гарантированное выполнение или чёткий откат в случае сбоя.
Это вам так кажется потому, что вы не представляете себе масштабов проблемы. Дело в том, что тормоза в подобных приложениях имеют многостадийный эффект.
То есть сначала просто всё работает чуть медленнее, чем хочется. Это нефункциональная проблема, и нам наплевать.
Потом оказывается, что с ростом нагрузки на базу транзакции, внутри которых происходит общение с клиентом, начинают тормозить всё сильнее — потому, что каждый update застревает на чуть-чуть в ожидании отпускания локов.
Транзакции становятся всё дольше, и удерживают локи на всё более длинное время. Тормоза стремительно прогрессируют.
Затем тормоза начинают становиться фейлами, потому что периодически происходят дедлоки. И наша атомарность оборачивается тем, что мы получаем "чёткий откат" вместо внесения изменений.
Начинаются вот эти вот знаменитые "девочки, все выйдите из терминала, я отгрузку провожу".
А всё оттого что в кузнице не было гвоздя вначале кто-то решил, что засосать тыщу записей в память, а потом по одной фигачить update на их основе — это нормальная стратегия. А чо — в тестах в single-user моде всё исполняется 10 секунд, что должно бы всех устроить.
V>С изменением требований я сталкиваюсь постоянно. Сегодня надо работать с таким источником данных, завтра с ещё одним. Использую описанный подход и не имею особых проблем.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
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>С изменением требований я сталкиваюсь постоянно. Сегодня надо работать с таким источником данных, завтра с ещё одним. Использую описанный подход и не имею особых проблем.
Это значит что у тебя приложения примитивные, возможно однопользовательские. Тогда действительно можно репозитариями пользоваться.