Как стать автором
Обновить

Комментарии 17

Эта статья посвящена созданию модели данных, которая красиво ложилась бы на SQL и содержала в себе «правильное» ООП наследование.

А что такое ""правильное" ООП-наследование"?


Что ж, весьма оптимально, надо признать. Единственное, что смущает – это специфичные имена таблиц.

Дайте я угадаю, вы используете model-first, не code-first?


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

Зачем вам в этой схеме дискриминатор? А без дискриминатора это та же первая схема, только с нормальными именами таблиц, и называется это table-per-type.


И уж точно нет никакого смысла делать дискриминатор отдельной таблицей.

А что такое "«правильное» ООП-наследование"?
Я имею в виду, что структура классов должна более-менее соответствовать структуре таблиц, и всё это должно подчиняться логике ООП. А не «всё в одной таблице», как в варианте classification.
Дайте я угадаю, вы используете model-first, не code-first?
Да, в 4-х enterprise-проектах с Entity Framework использовался именно model-first.
Зачем вам в этой схеме дискриминатор? А без дискриминатора это та же первая схема, только с нормальными именами таблиц, и называется это table-per-type.

И уж точно нет никакого смысла делать дискриминатор отдельной таблицей.
Поясню:
1) Совсем без дискриминатора делать не вариант, потому что иногда приходится читать просто таблицу A. А как узнать «класс» записей в ней без дополнительных join-ов?
2) Дискриминатор и триггеры позволяют избежать ситуации, когда одной записи в таблице А соответствуют записи сразу в нескольких дочерних таблицах (например, B, C и F). Это вполне может быть некоторым оригинальным архитектурным решением, но с точки зрения ООП такая ситуация является ошибкой (если быть более точным, то это подмена наследования композицией).
3) В большом проекте отдельная таблица дискриминатора может использоваться для тестов. Пример из реального проекта: тест проверял, чтобы всем классам из структуры были проставлены корректные атрибуты (DataContact, использовавшийся WCF, и прочие, поддерживавшие инфраструктуру), перечисление (ClassEnum в тексте статьи) содержало все необходимые значения из базы и наоборот, а таблицы в БД соответствовали классам уровня Business Logic.
Я имею в виду, что структура классов должна более-менее соответствовать структуре таблиц

Это же не имеет никакого отношения к ООП.


всё это должно подчиняться логике ООП

Что "все это"? Структура таблиц не может подчиняться логике ООП, потому что это таблицы. Остаются классы. И что же вы имеете в виду?


А не «всё в одной таблице», как в варианте classification.

Это называется table-per-hierarchy, и, на самом деле, никак ООП не противоречит.


Совсем без дискриминатора делать не вариант, потому что иногда приходится читать просто таблицу A. А как узнать «класс» записей в ней без дополнительных join-ов?

То есть исключительно из соображений производительности? И тут немедленно возникает вопрос: а как в случае вашего решения EF строит запрос? У вас именно наследование (и вы убедили EF объединить дискриминаторы и TPT), или референсы?


Ну и да, теперь у вас две параллельных структуры классов (для хранения и для бизнес-логики). Что у них с областями видимости и применения?


В большом проекте отдельная таблица дискриминатора может использоваться для тестов. Пример из реального проекта: тест проверял, чтобы всем классам из структуры были проставлены корректные атрибуты (DataContact, использовавшийся WCF, и прочие, поддерживавшие инфраструктуру), перечисление (ClassEnum в тексте статьи) содержало все необходимые значения из базы и наоборот, а таблицы в БД соответствовали классам уровня Business Logic.

Для этого не нужна таблица дискриминатора. Для этого, прямо скажем, вообще дискриминатор не нужен, для этого достаточно прочитать метаданные из модели и сопоставить их с БД.

М-м-м, чем пахнет? По-моему, священной войной. Структура таблиц в БД может как соответствовать структуре классов на одном из уровней, так и не соответствовать. Я подчёркиваю: в статье я перечислил подходы, с помощью которых одно умышленно пыталось быть приведённым в соответствие со вторым в разных проектах (и разных организациях) на протяжении 10 лет. Причин так делать тоже было много и разных, в том числе навязанных дядьками предпенсионного возраста с другой половины глобуса.
Ну и да, теперь у вас две параллельных структуры классов (для хранения и для бизнес-логики). Что у них с областями видимости и применения?
Я наблюдал и больше, чем две параллельные структуры для одного и того же набора сущностей одновременно. Всё определяется требованиями.
М-м-м, чем пахнет? По-моему, священной войной.

Неа, пахнет некорректно сформулированной задачей.


Сначала вы пишете, что "Я имею в виду, что структура классов должна более-менее соответствовать структуре таблиц, и всё это должно подчиняться логике ООП", а потом вы пишете, что "причины навязаны". Так вот, есть большая разница между "я так делаю, потому что этого требует ООП" и "я так делаю, потому что этого требуют условия ТЗ, которые мне неподконтрольны".


Я подчёркиваю: в статье я перечислил подходы, с помощью которых одно умышленно пыталось быть приведённым в соответствие со вторым в разных проектах (и разных организациях) на протяжении 10 лет.

… и за эти десять лет никто не попробовал взять другой инструмент, у которого нет описанных вами проблем?


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

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


Понимаете ли, между решениями "БД отображена напрямую на домен" и "БД отображена на DTO, которые отображены на домен" есть фундаментальная архитектурная разница, а вы легко и непринужденно в третьем решении подменили одно на другое.

Попробую ответить покороче. Эта статья есть суммирование тех подходов, которые использовались для максимально близкого и простого сопряжения ООП, EF и SQL на примерно пяти совершенно разных проектах, разрабатывавшихся разными командами на протяжении примерно десяти лет. И требования там тоже разные были. И DTO был, и без него тоже обходились. Другими словами, я рассказал про цвета и формы ручек молотков, которыми мы забивали гвозди, (и, чего греха таить, иногда даже шурупы с болтами). И я очень не хочу спорить на тему того, что десять лет назад мне нужно было всё это делать отвёрткой, включая забивание гвоздей и закручивание гаек.

Ну значит ваши выводы в конце статьи неверны: вы сравниваете решения разных задач.

Тогда я предлагаю Вам, как очевидно более умному и опытному, сформулировать более точные выводы, которые больше подошли бы для этой статьи.

А нет там выводов. "Такую-то задачу мы решали так-то, такую-то задачу мы решали так-то". И это, кстати, соответствует вашему же утверждению "Эта статья есть суммирование тех подходов". Чтобы можно было делать выводы, нужно решать одну и ту же задачу разными средствами и сравнивать их.

Задача, озвученная в первом предложении:
Эта статья посвящена созданию модели данных, которая красиво ложилась бы на SQL и содержала в себе «правильное» ООП наследование.

Потом перечислены четыре подхода, как это делалось, со словами «плюс данного подхода» и «минус данного подхода». Последний (мой собственный) подход сравнивается с остальными (более общепринятыми).

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

Одна задача, разные способы, сравнение. Что ещё надо? Ах да, флейм впустую развести забыл! Ан нет, всё нормально, комментаторы за меня справятся.
Задача, озвученная в первом предложении:

Обсудили же: непонятно, что такое "модель данных, которая содержит в себе "правильное" наследование".


Одна задача, разные способы, сравнение. Что ещё надо?

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

Ошибка в слове Polymophic
Спасибо, поправил.
Хорошая подробная статья, интересно с точки зрения реализации, спасибо. Тем не менее встречный вопрос — а давало ли какие-либо преимущества натягивание ООП модели на реляционную БД?
Ни в коем разе не повторяюсь комментариям выше, именно хочу узнать каков профит получился. Последние тенденции вроде активно призывают минимизировать наследование, а уж внедрять его в реляционные БД совсем выглядит чужеродным.
Тем не менее встречный вопрос — а давало ли какие-либо преимущества натягивание ООП модели на реляционную БД?

Это стандартное обсуждение в сторону использования-ORM-вообще. И на него есть стандартный же ответ: чем ближе модель, с которой работает программист, к его (программиста) идеальной модели, тем этому программисту легче работать. Соответственно, если где-то есть идеальный инструмент, который позволяет навалять в коде ОО-модель с интерфейсами и наследованием, а потом эффективно слить ее в БД и строить по ней запросы — программист будет тратить меньше времени на эту инфраструктуру и больше на собственно бизнес-задачи.

а давало ли какие-либо преимущества натягивание ООП модели на реляционную БД?

Проект представлял из себя многокомпонентную систему, в которой доступ к данным быстро и без головной боли делался с помощью Entity Framework («быстро» было очень важно). Всех всё устраивало. Так как с самого начала было понятно, что разработка и сопровождение растянутся на 5+ лет, то структуру данных решено было делать максимально простой и прозрачной, чтобы уменьшить головную боль себе и коллегам, которые приступят к доработке спустя пару лет. То есть, максимально использовать принцип KISS, в том числе и заставить таблицы в БД соответствовать классам уровней бизнес-логики (их было 3, если я ничего не путаю).
хочу узнать каков профит получился.

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

Наследование — не более чем инструмент. Есть места, где он уместен, и есть, где он выглядит как седло на корове. Естественно, не все проекты, над которыми я работал, активно использовали наследование. Если сейчас в силу каких-либо новшеств становится всё больше проектов, где наследование скорее вредит, то вполне естественно, что его будут использовать меньше. И я его не буду пихать куда угодно просто в силу какой-то извращённой любви. Если я шутки ради (ну и в качестве курсового проекта, чего греха таить) написал (сильно упрощая) полиморфный вирус на T-SQL, это ни разу не означает, что T-SQL — это идеальный язык для написания полиморфных вирусов. Каждому инструменту — своё место на полке.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории