Есть проедметная область с двумя сущностями "проект документа" и "утвержденный документ".
Обе сущности представляют собой по сути один и тот же документ в разных состояниях.
"Проект документа" — это набор невалидированных полей на которые никто не ссылается, кроме "заявки на рассмотрение проекта документа" (еще одна сущность предметной области, содержит поля: дата подачи, дата рассмотрения, статус).
"Утвержденный документ" — это тот же проект, но в валидном состоянии, плюс иногда может отличаться от проекта парочкой полей. На него ссылается пол базы, просто так его не удалить (отдельный вопрос в p.s.).
(иногда, потому что есть не только проекты документов, но и проекты других сущностей).
Собственно вопрос в том, как это реализовать в коде и реляционной базе.
Инструментарий: .NET, NHibernate, DI.
Я вижу два варианта:
1. сделать практический одинаковый набор классов и таблиц отдельно для проекта и отдельно для утвердженного документа.
Такой вариант мне не очень нравится, т.к. у документов есть множество агрегируемых сущностей — в итоге в базе будет два больших почти одинаковых графа объектов.
2. унаследовать классы от абстрактного "документа" и хранить все в одном наборе таблиц в базе. проект от утвержденного документа отличается столбцом.
То же не нравится, т.к. сущности могут начать расходиться по набору полей и рефакторить потом прилично.
Кто-нибудь знает примеры реализации подобного в крупных или известных системах?
p.s. Дополнительный вопрос в том, как удалять сущности, на которые ссылается половина базы? Я слышал что в 1С это сделано не физическим удалением из базы, а пометкой записи как удаленной. Я сам склоняюсь к такому варианту, но аргументом противников такого метода может быть необходимость в большинстве запросов все время проверять этот статус. Есть еще вариант переносить содержимое удаленной записи в другую таблицу/базу (сложный, на мой взгляд, вариант).
Здравствуйте, Ocenochka, Вы писали:
O> Есть проедметная область с двумя сущностями "проект документа" и "утвержденный документ". O> Обе сущности представляют собой по сути один и тот же документ в разных состояниях.
А зачем плодить сущности? Почему бы не сделать одну сущность с состоянием?
O> p.s. Дополнительный вопрос в том, как удалять сущности, на которые ссылается половина базы?
Смотря чего хочется получить по ТЗ от удаления.
Физическое отсутствие данных обеспечит вариант с разрыванием ссылок либо с удалением ссылающихся сущностей.
Историю и отслеживание последовательности действий обеспечит наличие признака удаления. Перенос в др. таблицы оптимизирует работу БД, но логика при этом усложняется, т.к. одна и та же сущность будет лежать в нескольких таблицах.
Здравствуйте, akasoft, Вы писали:
O>> Есть проедметная область с двумя сущностями "проект документа" и "утвержденный документ". O>> Обе сущности представляют собой по сути один и тот же документ в разных состояниях. A>А зачем плодить сущности? Почему бы не сделать одну сущность с состоянием?
А в базе тоже хранить в одной таблице? Если да, то я вижу несколько проблем:
1. Для получения всех "утвержденных документов" нужно проверять что документ не был исключен после включения, при этом запрос к БД усложняется до хранимой процедуры либо если хотим логику в коде, то придется доставать всю таблицу и на клиенте с ней работать.
2. Если в "утвержденном документе" есть ссылки и свойства, которых нет в "проекте документа", то сущности уже не так похожи. А если 30% свойств будет отличаться? А если 50%? Тут вроде как здравый смысл рулит, но он у каждого свой. Вот интересно кто что по этому поводу думает.
3. В одной таблице не получится поставить констрейнты на данные (не критично, но все же)
Здравствуйте, Ocenochka, Вы писали:
O> А в базе тоже хранить в одной таблице?
А почему нет.
O> Если да, то я вижу несколько проблем:
Пока я вижу такую проблему, что ты ТЗ знаешь, но мне его не говоришь. Поэтому в ответах мне придётся прибегать к боевой телепатии.
O> 1. Для получения всех "утвержденных документов" нужно проверять что документ не был исключен после включения, при этом запрос к БД усложняется до хранимой процедуры либо если хотим логику в коде, то придется доставать всю таблицу и на клиенте с ней работать.
По мне это всего-лишь поле типа один бит, т.е.
SELECT * FROM document WHERE approved = true
O> 2. Если в "утвержденном документе" есть ссылки и свойства, которых нет в "проекте документа", то сущности уже не так похожи. А если 30% свойств будет отличаться? А если 50%? Тут вроде как здравый смысл рулит, но он у каждого свой. Вот интересно кто что по этому поводу думает.
Здравый смысл говорит, что сначала появляется проект, потом он обрастает свойствами, потом его обсуждают и в конце концов утверждают, а когда утвердили, то уже добавлять/убирать нельзя, только исполнять. Не может проект документа отличаться от самого себя, но наполнение его в моменты времени между началом проекта и утверждением может быть разным.
O> 3. В одной таблице не получится поставить констрейнты на данные (не критично, но все же)
Здравствуйте, akasoft, Вы писали:
O>> Если да, то я вижу несколько проблем: A>Пока я вижу такую проблему, что ты ТЗ знаешь, но мне его не говоришь. Поэтому в ответах мне придётся прибегать к боевой телепатии.
Это неизбежно в беседах такого рода — все ТЗ я сообщить не могу по понятным причинам. Могу лишь абстрактно обрисовать проблему — есть две сущности предметной области, но они на столько похожи данными и логикой, что вроде бы являются единым.
O>> 1. Для получения всех "утвержденных документов" нужно проверять что документ не был исключен после включения, при этом запрос к БД усложняется до хранимой процедуры либо если хотим логику в коде, то придется доставать всю таблицу и на клиенте с ней работать. A>По мне это всего-лишь поле типа один бит, т.е. A>
A>SELECT * FROM document WHERE approved = true
A>
Тут моя вина. Забыл указать, что ТЗ подразумевает хранение истории по утверждению/отклонению документов. Один и тот же документ может утверждаться, потом отклоняться, потом опять утверждаться но уже с некоторыми измененными полями и так много раз. Сейчас это сделано одной записью в таблице на каждое изменение. Простой проверкой статуса не обойтись. Может быть историю нужно было делать по другому, но других красивых вариантов я не нашел.
O>> 2. Если в "утвержденном документе" есть ссылки и свойства, которых нет в "проекте документа", то сущности уже не так похожи. А если 30% свойств будет отличаться? А если 50%? Тут вроде как здравый смысл рулит, но он у каждого свой. Вот интересно кто что по этому поводу думает. A>Здравый смысл говорит, что сначала появляется проект, потом он обрастает свойствами, потом его обсуждают и в конце концов утверждают, а когда утвердили, то уже добавлять/убирать нельзя, только исполнять.
К сожалению по моему ТЗ документ могут отклонить (перевести в проект) после утверждения, а потом опять утвердить его же, но слегка изменив.
A>Не может проект документа отличаться от самого себя, но наполнение его в моменты времени между началом проекта и утверждением может быть разным.
Логика работы с проектом отличается от лигики работы с утвержденным документом. Эти различия меня и смущают. Т.е. Вроде и один документ, но логика работы с ним довольно сильно расходится когда он утверджен и когда нет.
O>> 3. В одной таблице не получится поставить констрейнты на данные (не критично, но все же) A>Это замечание моя боевая телепатия не осилила.
В базе можно указать обязательный набор полей и при вставке новой записи это будет проверяться. Наборы обязательных полей у проекта и утвержденного документа разные. Одна и таже таблица для хранения обоих документов не подойдет. Хотя валидация в базе не столь критична.
Давайте не будем углубляться в предметную область, а рассмотрим гипотетический пример, где одна и та же сущность может находится в двух разных состояниях выраженным разным набором полей и разной логикой. Хотя при такой постановке задачи я сам вижу, что придется городить отдельные таблицы в базе и отдельные классы в домене. Другого варианта не вижу.
зы Спасибо за помощь, не смотря на необходимость в телепатии )
Здравствуйте, Ocenochka, Вы писали:
O>Здравствуйте, akasoft, Вы писали:
O>>> Если да, то я вижу несколько проблем: A>>Пока я вижу такую проблему, что ты ТЗ знаешь, но мне его не говоришь. Поэтому в ответах мне придётся прибегать к боевой телепатии. O> Это неизбежно в беседах такого рода — все ТЗ я сообщить не могу по понятным причинам. Могу лишь абстрактно обрисовать проблему — есть две сущности предметной области, но они на столько похожи данными и логикой, что вроде бы являются единым.
O>>> 1. Для получения всех "утвержденных документов" нужно проверять что документ не был исключен после включения, при этом запрос к БД усложняется до хранимой процедуры либо если хотим логику в коде, то придется доставать всю таблицу и на клиенте с ней работать. A>>По мне это всего-лишь поле типа один бит, т.е. A>>
A>>SELECT * FROM document WHERE approved = true
A>>
O> Тут моя вина. Забыл указать, что ТЗ подразумевает хранение истории по утверждению/отклонению документов. Один и тот же документ может утверждаться, потом отклоняться, потом опять утверждаться но уже с некоторыми измененными полями и так много раз. Сейчас это сделано одной записью в таблице на каждое изменение. Простой проверкой статуса не обойтись. Может быть историю нужно было делать по другому, но других красивых вариантов я не нашел.
O>>> 2. Если в "утвержденном документе" есть ссылки и свойства, которых нет в "проекте документа", то сущности уже не так похожи. А если 30% свойств будет отличаться? А если 50%? Тут вроде как здравый смысл рулит, но он у каждого свой. Вот интересно кто что по этому поводу думает. A>>Здравый смысл говорит, что сначала появляется проект, потом он обрастает свойствами, потом его обсуждают и в конце концов утверждают, а когда утвердили, то уже добавлять/убирать нельзя, только исполнять.
Мне кажется если сделать структуру таблиц более нормализованной то будет проще организовать все в одной схеме. Как уже сказали — ввести состояния.
Взять таблицу Documents, описывающую шапку документа. К ней прикрутить таблицу Versions — с состояниями — в ней же и хранится вся история состояний с документом. Свойства документа — тоже отдельная таблица — properties и таблица связей propToVersion в которой будет связь версии со свойством.
Это примерно в общем виде, наверное косячки есть, можно продумать подробнее. Правда я не знаю как такая структура ложится на ORM
Здравствуйте, Ocenochka, Вы писали:
O> Давайте не будем углубляться в предметную область, а рассмотрим гипотетический пример, где одна и та же сущность может находится в двух разных состояниях выраженным разным набором полей и разной логикой. Хотя при такой постановке задачи я сам вижу, что придется городить отдельные таблицы в базе и отдельные классы в домене. Другого варианта не вижу.
А вам по неутвержденнвм документам поиск по отдельным (любым) полям нужен? Если нет, то можно
попробовать неутвержденный документ в JSON- или XML-форме хранить, как одно большое текстовое поле.
Здравствуйте, bl-blx, Вы писали:
BB>А вам по неутвержденнвм документам поиск по отдельным (любым) полям нужен? Если нет, то можно попробовать неутвержденный документ в JSON- или XML-форме хранить, как одно большое текстовое поле.
Здравствуйте, hachik, Вы писали:
H>Мне кажется если сделать структуру таблиц более нормализованной то будет проще организовать все в одной схеме. Как уже сказали — ввести состояния. H>Взять таблицу Documents, описывающую шапку документа. К ней прикрутить таблицу Versions — с состояниями — в ней же и хранится вся история состояний с документом. Свойства документа — тоже отдельная таблица — properties и таблица связей propToVersion в которой будет связь версии со свойством.
Да примерно так и вышло. Иерархия таблиц для документа в единственном экземпляре + две таблицы ссылающиеся на документ: 1. проект, 2. утвержденные.
H>Это примерно в общем виде, наверное косячки есть, можно продумать подробнее. Правда я не знаю как такая структура ложится на ORM
Здравствуйте, akasoft, Вы писали:
A>Историю и отслеживание последовательности действий обеспечит наличие признака удаления. Перенос в др. таблицы оптимизирует работу БД, но логика при этом усложняется, т.к. одна и та же сущность будет лежать в нескольких таблицах.
Получаем таблицу документов document(id, state), где state == false, если документ не утверждён, и true, если утверждён. Как я понял, новые документы у тебя не различаются, а получают состояние неутверждённых.
Далее, у документа есть некий набор полей, зависящий от его состояния ихода работ с ним. Эти поля будем хранить в отдельной таблице field(id, doc_id, type, data). Проверять их наличие и обязательность будем в моменты перевода документа из одного состояния в другое, что на графе выше обозначено цифрами от 1 до 3. В каждый такой момент времени мы знаем, какие поля обязательны и какая информация в них допустима.
Для облегчения себе жизни некоторые поля из field можно "втянуть" в document, что упростит работу с документами и немного разгрузит БД, т.к. не понадобится обращаться ко второй таблице. Например, дату создания документа. Или дату последнего изменения.
До сих пор у нас хранится документ в текущем состоянии, а набор и содержание полей зависит от последнего перехода состояния.
Предыдущее состояние документа возникает в момент перехода через то, что обозначено стрелками с цифрами от 1 до 3.
Отсюда вопрос, в каком виде должна быть представлена история? В какие моменты мы должны её фиксировать? Должны ли мы иметь возможность получать т.н. полный документ для всех и каждого предыдущего состояния? Должны ли мы показывать наличие изменений тех или иных полей?
От ответов будет зависеть, где и как лучше хранить историю.
Например, мы можем завести доп. поле revision в document и field. Каждый раз при смене состояния мы будем формировать новую строку в этих таблицах, увеличивая на 1 ревизию, а содержание и состав полей будет определяться нашими правилами. Понятно, что для document это будет одна строка, а для field это будет M строк, в общем случае отличных от N строк, привязанных к документу на предыдущем состоянии.
Можно завести по две таблицы document и field. Первый набор — для хранения текущего состояния, как описано выше. Таким образом, мы его всегда быстро вытащим не напрягая БД. А вот второй набор полей будет содержать ревизию: document2 (id, state, revision) и field2 (id, doc_id, type, data, revision), куда строки будут заноситься копированием текущих значений из document/field с увеличением ревизии в моменты переходов состояния. Т.е. сначала спасём текущие данные из document/field в document2/field2 под новой ревизией, затем их поменяем в document/field и получем корректное новое состояние.
У второго подхода выгода в том, что если к истории мы обращаемся реже, чем к текущим данным, то БД будет куда легче обрабатывать "маленькую" таблицу чаще, чем "большую". Разделяй и властвуй.
Можно ещё попробовать хранить только изменения полей, для чего понадобится ещё одна промежуточная таблица между document и field.
Ну а если работа с историей заключается всего-лишь в строчке протокола "20.09.2011 документ отправлен на доработку зам. главы Администрации Сидоровым Г.А.", то дело совсем упрощается. Будем в моменты 1-3 просто заносить этот протокол в отдельную таблицу protocol (id, doc_id, text) и не морочить себе голову.
Ах да, что будет если мы внесём изменения в неутверждённый документ, но состояние при этом останется неутверждённым? А это зависит от ТЗ. Если надо отслеживать и такие изменения, то перерисуем наш граф, добавив связь 4, а написанное выше будет по прежнему иметь силу.
Здравствуйте, akasoft, Вы писали:
A>Здравствуйте, Ronaldo 9, Вы писали:
R9>>А как насчет сегментирования таблиц?
A>Оставлю его пока на совести БД. A>Или ты что-то конкретное имеешь ввиду?
Просто можно не делить актуальные и удаленные данные на разные таблицы, а сегментировать. Либо просто наложить индекс по флагу "удаленный".