ADR-006. Структура данных информационной карты
Как хранить содержимое информационной карты: фиксированными свойствами, иерархией видов или шаблоном вида ИК.
1. Контекст
Информационная карта фиксирует результаты обработки экземпляра фонда. Её содержимое — это поля бумажной формы, и вопрос в том, чем они являются для модели данных: свойствами сущности, известными на этапе проектирования, или данными, состав которых задаётся извне и меняется без правки кода.
Что известно из образцов заказчика (см. «Образцы бумажных информационных карт»):
- Состав полей различается по видам ИК. У формы на издание два листа, у формы на рукопись — пять. Пересечение между ними невелико: шифр, дата, ФИО составителя, вид документа. Всё остальное расходится — «Переписчик рукописи», «Основание атрибуции», «Филиграни», «Задание по хранению» есть только у рукописи; «Конволют», «Камерный каталог», «Первоначальный академический штамп» — только у издания.
- У одного вида ИК несколько редакций бланка. По изданиям видно три. Состав полей
у них почти совпадает, различаются заголовки и подписи полей:
Кол-во стр.противКоличество страниц,Ил. в текстепротивИллюстрации в тексте. Лист сохранности тоже существует в разных вёрстках. - Состав полей известен не для всех видов. Форма на графику передана в текстовом формате, образцов на листовой документ, свиток и вложение нет вовсе. Состав раздела «Мероприятия стабилизации» не определён (OQ-V-2).
- Большинство полей — отметки в клетках, а не текст: «Задано / Выполнено», «Дерево / Картон / Бумага», степень повреждения по шкале.
- Незаполненные поля — норма. Встречаются карты, где заполнены только шифр и инвентарный номер.
Ключевое ограничение: Сервис оцифровывает уже существующий архив. Любая схема, которая не даёт внести карту, лежащую в фонде, неприемлема — исправить бумагу нельзя.
2. Рассматриваемые варианты
2.1. Вариант 1. Фиксированные свойства сущности
Каждое поле формы — свойство класса информационной карты, с собственным типом.
За: типизация и валидация на уровне модели, поиск и фильтрация по полю средствами хранилища, ошибка в имени поля ловится компилятором.
Против: класс становится объединением полей всех видов ИК, где для конкретной карты большая часть свойств заведомо пуста и неизвестно, какая именно. Каждая новая редакция бланка и каждый новый вид документа требуют правки кода и миграции. Различие в подписи одного и того же поля между редакциями выразить нечем — придётся выбрать одно написание и потерять остальные.
2.2. Вариант 2. Иерархия видов ИК (наследование классов)
Базовый класс с общей частью, наследники по видам: ИК издания, ИК рукописи и далее.
За: расхождения между видами выражены явно, общая часть описана один раз, тип карты известен статически.
Против: по образцам общая часть — четыре поля, а расхождения — десятки и сотни полей. Получается почти пустой родитель и не пересекающиеся между собой наследники, то есть наследование без переиспользования. Редакция бланка иерархией не выражается вовсе: это версия формы, а не подвид карты, и добавлять по классу на редакцию бессмысленно. Наследование в модели данных дополнительно упирается в отображение на хранилище.
2.3. Вариант 3. Плоский список полей (как сейчас)
Карта хранит список значений «раздел + наименование поля + значение».
За: любой вид ИК и любая редакция укладываются без изменения схемы; экраны проверки и выгрузка пишутся один раз на все виды.
Против: состав полей ничем не задан и не контролируется — ничто не мешает записать
Заглавие и заглавие как разные поля; нельзя ответить на вопрос, какие поля вид ИК
обязан иметь; типизации нет, дата и число хранятся строками.
2.4. Вариант 4. Раздельное описание электронного документа и бумажной формы
Состав полей описывается данными, а не кодом. Ключевое отличие от вариантов 1–3: что хранится и как это напечатано — два разных описания, а не одно.
2.4.1. Электронный документ: что хранится
Конфигурация документа — состав: перечень атрибутов с обязательностью и допустимые вложенные документы с их кратностью. Отвечает на вопрос «из чего состоит ИК издания», без единого слова о том, как это напечатано.
Атрибут — поле как понятие: код, каноническое наименование, тип значения. Живёт в общем словаре и переиспользуется всеми конфигурациями.
Тип значения — правило, которому подчиняется значение: вид значения и ограничения.
вид = string; min = 1; max = 500 → длина значения
вид = number; min = 0; max = 14 → диапазон значения
вид = date; min = 900; max = 2026 → диапазон значения
вид = bool; → отметка в клетке: есть или нет
вид = choice [A: «Слепое»; B: «Золотое»; C: «Другие виды»]; min = 0; max = 3
вид = ref → справочник «Материалы»; min = 0; max = 1
Пара min/max есть у любого вида, но означает осмысленное именно для него: у строки —
длину, у числа и даты — границы, у выбора — количество выбранных значений. Этим же
задаётся кратность: max = 1 — выбор одного (степень повреждения), max = N —
множественный (материалы основы, украшения).
Для одиночной отметки в клетке заведён отдельный вид bool: выражать её выбором
из набора в одно значение можно, но читается это хуже, а таких полей на форме больше
всего.
choice перечисляет значения прямо в типе, ref ссылается на справочник. Критерий:
если один и тот же набор значений нужен двум и более атрибутам — это справочник.
Документ — экземпляр: docId равен собственному идентификатору, parentId пуст
у корня; ссылка на конфигурацию; значения атрибутов. Документ может быть составным:
у вложенного документа parentId указывает на непосредственного родителя, а docId —
на корень, поэтому весь документ достаётся одним запросом.
Корневой документ — агрегат: части адресуются только через него.
Вложенный документ — эксперты, замеры pH, авторы, переписчики, владельцы, биотесты. Это состав, а не вёрстка: список существует в данных, даже если на бланке под него отведена одна строка.
Значение атрибута — ссылка на атрибут и одно или несколько значений.
2.4.2. Бумажный документ: как это напечатано
Форма документа — описание бланка целиком; ссылается на конфигурацию документа. У одной конфигурации может быть несколько форм — по числу редакций бланка в обращении.
Страница формы — номер, заголовок, роль-заполнитель.
Раздел — заголовок, порядок; разделы вкладываются друг в друга.
Размещение атрибута — атрибут, подпись именно на этом бланке, место на листе. Область поля на изображении относится к форме, а не к конфигурации: конфигурация описывает состав, а не бумагу.
Версий у формы нет: изменился бланк — это другая форма. Версионирование не нужно потому, что значение опознаётся кодом атрибута и от места печати не зависит.
Страницы, разделы, подписи и координаты к электронному документу отношения не имеют.
2.4.3. Связь двух описаний
Форма ссылается на конфигурацию. В форму попадают не все атрибуты состава — служебные данные Сервиса на бумаге отсутствуют, — но атрибута, которого нет в составе, в форме быть не может. Обязательность — свойство состава, а не формы.
То, с какой формы оцифрована конкретная карта, — свойство пакета ввода: это факт ввода, а не свойство самой карты. Без него нельзя объяснить, почему поле распозналось так, а не иначе.
2.4.4. Ядро карты: типизированные свойства поверх состава
Информационная карта остаётся отдельной сущностью, а не растворяется в общем документе. У неё есть то, чего у абстрактного документа быть не может: связь с документом фонда, устанавливаемая на проверке экземпляров; связь с пакетом-первоисточником; дата карты как часть идентификации; факт выгрузки в АБИС; собственный жизненный цикл — карта создаётся командой пользователя по итогам проверки. Загнать это в атрибуты нельзя: «выгружена ли карта» не может быть строкой в общем хранилище значений.
Сверх этого карта несёт ядро — небольшой набор типизированных свойств: шифр, инвентарный номер, дата, заглавие, автор, год.
Критерий отбора в ядро — функциональный, а не структурный. В ядро идёт не то, что встречается на всех бланках, а то, по чему Сервис работает сам: ищет карту, строит реестр, отбирает выборку, подбирает экземпляр фонда, формирует выгрузку. Структурный критерий «общее для всех конфигураций» здесь не работает: у ИК издания одно поле «Заглавие», у ИК рукописи их три — унифицированное, полное и перевод.
Ядро — проекция, а не второй источник истины. Источник истины — значения атрибутов; свойства ядра заполняются из них и пересчитываются при правке значений. Какой атрибут даёт какое свойство ядра, задаёт конфигурация: у ИК рукописи заглавие берётся из унифицированного заглавия, и оно же, по-видимому, соответствует полю «Заглавие» на форме на издание.
Что это даёт: поиск и отбор по ядру идут по типизированным колонкам, то есть недостаток варианта 4 — «поиск через связь значения с атрибутом» — снимается для тех полей, по которым действительно ищут. Вопрос остаётся только для поиска за пределами ядра (OQ-A6-4).
Цена: правило отображения «атрибут → свойство ядра» для каждой конфигурации и обязанность пересчитывать ядро при изменении значений. Рассинхронизация ядра и значений — то, что здесь может сломаться.
2.4.5. Принятые решения
- Матрица «Задано / Выполнено» — два атрибута, а не один составной: коды вида
restoration_assignedиrestoration_done. Словарь длиннее, но тип значения остаётся простым, а на бумаге это две независимые клетки, заполняемые в разное время и разными людьми. - Повторяющиеся колонки — это разные атрибуты. «Материалы основы» с колонками 1 и 2 на форме на рукопись — не одно поле, заполняемое дважды, а два: основа переплёта и основа футляра. Тот же принцип дробности.
- Наследование конфигураций не вводится. Переиспользование обеспечивает словарь: общее поле изданий и рукописей — один и тот же атрибут в двух конфигурациях.
- Повторяющиеся группы — вложенные документы. Эксперты, замеры pH, авторы, переписчики, владельцы, биотесты. Таблицы с заранее известными строками — «Владельческие пометы», «Оформление экземпляра» — вложенными документами не являются: набор строк задан бланком, это просто пары атрибутов.
- Тип значения хранится как вид плюс JSON-ограничения. Вид (
kind) — отдельным полем, чтобы отвечать на вопрос «все атрибуты-даты» без разбора JSON в каждой записи; остальные параметры — одним JSON-полем, без отдельной таблицы на каждый вид значения.
2.4.6. Соглашения по переносу с бумаги
- Одно поле бланка против списка в составе. Если в составе список (авторы, замеры pH), а на бланке под него одно поле, распознанное значение добавляется в список. Соглашение временное: принято до проработки задачи распознавания и будет пересмотрено, когда станет понятно, что умеет модель.
- Рукописный текст вне клеток сохраняется как примечание к документу с областью
на изображении. При проверке пользователю предлагается отнести его к атрибуту, то есть
примечание может стать значением. В образцах такое есть:
5,83 перед форзацв поле величины pH, приписка «нет» рядом со шкалой степени повреждения.
2.4.7. За
- новая редакция бланка — новая форма из тех же атрибутов, без правки кода и миграции; карты разных редакций сравнимы, потому что опознаются по коду атрибута, а не по подписи;
- выгрузка в АБИС отображается с кодов атрибутов — одно правило на все виды и редакции;
- тип значения даёт и контроль ввода, и рамки распознавателю:
date 900–2026отсекает заведомый мусор в рукописном годе,choiceсводит задачу к выбору из списка; - различие «одна отметка» и «несколько отметок» выражено явно, поэтому две отметки там, где допустима одна, — видимая ошибка, а не молча принятые данные;
- разделение состава и формы снимает версионирование: бланк можно менять, не задевая созданные карты;
- состав документа перестаёт быть произвольным: значение без атрибута в конфигурации невозможно.
2.4.8. Против
- сущностей становится вдвое больше, чем в вариантах 1–3, и у них свой жизненный цикл плюс интерфейсы ведения; до наполнения словаря, конфигураций и форм Сервис бесполезен;
- поиск по полю за пределами ядра идёт через связь значения с атрибутом, а не по колонке таблицы; для полей ядра это снято проекцией, для остальных цена зависит от того, нужен ли такой поиск вообще (OQ-A6-4);
- ядро карты дублирует значения атрибутов и требует пересчёта при их правке — рассинхронизация ядра и состава возможна и заметна не сразу;
- словарь атрибутов надо чем-то наполнить: по образцам это сотни записей на вид ИК — отдельная работа до начала оцифровки;
- дерево документов и дерево разделов существуют параллельно и легко путаются: первое про данные, второе про бумагу. Ошибка в том, куда положить понятие, обнаружится поздно.
3. Вариант 5. Вид документа и атрибут документа карты
Раскладка, предложенная после принятия варианта 4. Отличается от него тремя вещами: видом документа вместо конфигурации, деревом видов наряду с деревом экземпляров и отдельной сущностью «атрибут документа карты» между составом и значением.
classDiagram
direction LR
class AttributeValueType["Тип значения"] {
<<Entity>>
+Guid Id
+String Code
+String Name
+ValueKind Kind
+String Constraints
}
class DocumentKind["Вид документа"] {
<<Entity>>
+Guid DocId
+Guid ParentId
+String DocKind
+String Name
+IReadOnlyList~DocumentKind~ NestedKinds
+IReadOnlyList~DocumentKindAttribute~ Attributes
}
class DocumentKindAttribute["Атрибут вида документа"] {
<<Entity>>
+DocumentKind DocumentKind
+String Code
+String Name
+AttributeValueType ValueType
+Boolean IsRequired
+Int32 Order
+CardCoreField CoreField
}
class InformationCard["Информационная карта"] {
<<Entity>>
+Guid Id
+DocumentKind Kind
+DateOnly Date
+IReadOnlyList~CardDocument~ Documents
}
class CardDocument["Документ информационной карты"] {
<<Entity>>
+Guid DocId
+Guid ParentId
+DocumentKind Kind
+IReadOnlyList~CardDocument~ NestedDocuments
+IReadOnlyList~CardDocumentAttribute~ Attributes
}
class CardDocumentAttribute["Атрибут документа карты"] {
<<Entity>>
+CardDocument Document
+DocumentKindAttribute KindAttribute
+IReadOnlyList~String~ Values
+ValueOrigin Origin
+Guid FilledByUserId
+DateTime FilledAt
}
class DocumentForm["Форма документа"] {
<<Entity>>
+Guid Id
+DocumentKind DocumentKind
+String Name
+IReadOnlyList~FormPage~ Pages
}
class FormPage["Страница формы"] {
<<Entity>>
+Int32 Number
+String Title
+IReadOnlyList~FormSection~ Sections
}
class FormSection["Раздел страницы"] {
<<Entity>>
+String Title
+Int32 Order
+IReadOnlyList~FormSection~ NestedSections
+IReadOnlyList~FormField~ Fields
}
class FormField["Поле формы"] {
<<Entity>>
+DocumentKindAttribute KindAttribute
+String Caption
+Int32 Order
+SourceArea Area
}
class SourceArea["Область на кадре"] {
<<ValueObject>>
+Double X
+Double Y
+Double Width
+Double Height
}
class InputPacket["Пакет ввода"] {
<<Entity>>
+Guid Id
+String Number
+DocumentForm DocumentForm
+DateTime QueuedAt
+Guid CreatedByUserId
+String RecognitionRequestJson
+String RecognitionResultJson
+DateTime RecognizedAt
+IReadOnlyList~ImageFrame~ Frames
+IReadOnlyList~PacketFieldValue~ Values
}
class ImageFrame["Кадр"] {
<<Entity>>
+Guid Id
+Int32 Order
+String StorageKey
+ImageSource Source
+FormPage FormPage
}
class PacketFieldValue["Значение поля пакета"] {
<<Entity>>
+DocumentKindAttribute KindAttribute
+FormField FormField
+IReadOnlyList~String~ Values
+IReadOnlyList~String~ RecognizedValues
+ValueOrigin Origin
+Double Confidence
+ImageFrame ImageFrame
+SourceArea Area
}
DocumentKind "1" *-- "0..*" DocumentKind : ParentId
DocumentKind "1" *-- "0..*" DocumentKindAttribute : Attributes
DocumentKindAttribute --> "1" AttributeValueType : ValueType
DocumentForm --> "1" DocumentKind : DocumentKind
DocumentForm "1" *-- "1..*" FormPage : Pages
FormPage "1" *-- "0..*" FormSection : Sections
FormSection "1" *-- "0..*" FormSection : Sections
FormSection "1" *-- "0..*" FormField : Fields
FormField --> "1" DocumentKindAttribute : KindAttribute
InputPacket --> "1" DocumentForm : DocumentForm
InputPacket "1" *-- "1..*" ImageFrame : Frames
InputPacket "1" *-- "0..*" PacketFieldValue : Values
ImageFrame --> "0..1" FormPage : FormPage
PacketFieldValue --> "1" DocumentKindAttribute : KindAttribute
PacketFieldValue --> "0..1" FormField : FormField
PacketFieldValue --> "0..1" ImageFrame : ImageFrame
PacketFieldValue "1" *-- "0..1" SourceArea : Area
FormField "1" *-- "0..1" SourceArea : Area
InformationCard --> "1" InputPacket : SourcePacket
InformationCard --> "1" DocumentKind : Kind
InformationCard "1" *-- "1..*" CardDocument : Documents
CardDocument --> "1" DocumentKind : Kind
CardDocument "1" *-- "0..*" CardDocument : ParentId
CardDocument "1" *-- "0..*" CardDocumentAttribute : Attributes
CardDocumentAttribute --> "1" DocumentKindAttribute : KindAttribute
style DocumentKind fill:#d6e4f7,stroke:#2f5f96
style DocumentKindAttribute fill:#d6e4f7,stroke:#2f5f96
style DocumentForm fill:#dcefdc,stroke:#3f7f3f
style FormPage fill:#dcefdc,stroke:#3f7f3f
style FormSection fill:#dcefdc,stroke:#3f7f3f
style FormField fill:#dcefdc,stroke:#3f7f3f
style InputPacket fill:#fbe6cd,stroke:#b5762a
style ImageFrame fill:#fbe6cd,stroke:#b5762a
style PacketFieldValue fill:#fbe6cd,stroke:#b5762a
style InformationCard fill:#f7dfe4,stroke:#a34457
style CardDocument fill:#f7dfe4,stroke:#a34457
style CardDocumentAttribute fill:#f7dfe4,stroke:#a34457
style AttributeValueType fill:#f2f2f2,stroke:#8c8c8c
style SourceArea fill:#f2f2f2,stroke:#8c8c8c
Цветом выделены четыре агрегата: вид документа — голубой, форма документа — зелёный, пакет ввода — оранжевый, информационная карта — розовый. Серым — то, что вне агрегатов: типы значений и область на кадре.
3.1. Как читается цепочка
Состав задаётся дважды параллельными парами. Вид документа и его атрибут вида — описание того, что бывает; документ карты и его атрибут документа — то, что заполнено на конкретной карте. Каждый уровень экземпляра ссылается на свой уровень описания: документ карты знает свой вид, атрибут документа карты знает атрибут вида.
Значение — поле атрибута документа карты, а не отдельная сущность: сначала «в этом документе есть такое поле», затем «у поля такие значения».
Отдельную сущность значения заводить не за чем: собственных реквизитов у неё нет — уверенность, источник и область остались в пакете ввода, потому что это данные о распознавании, а не о результате. Такая сущность была бы полем, записанным отдельной таблицей, и добавила бы третий переход на пути от документа к данным там, где второй уже введён осознанно — ради факта заполнения.
Множественные значения (материалы основы, украшения, физико-механические повреждения отмечаются несколькими клетками сразу) хранятся списком в том же поле. Цена: запрос «во всех картах, где среди материалов есть картон» становится поиском по содержимому списка, а не соединением таблиц, — и требует индекса соответствующего вида. Это приемлемо, потому что поиск за пределами ядра решено не поддерживать.
3.2. Бумажная форма
Форма документа — форма конкретного вида документа; у одного вида форм может быть несколько, по числу редакций бланка в обращении. Форма содержит страницы, страница — разделы, разделы вкладываются друг в друга. Поле формы входит в раздел, несёт подпись, как она напечатана на этом бланке, и связано с атрибутом вида документа — то есть с полем в составе конкретного вида.
Связь поля через атрибут вида означает, что поле привязано не просто к понятию, а к понятию в составе конкретного вида документа. Поэтому поле бланка не может сослаться на атрибут, которого в составе этого вида нет: то, что в варианте 4 приходилось объявлять инвариантом («атрибута, которого нет в конфигурации, в форме быть не может»), здесь обеспечено самой связью.
Явная связь формы с видом при этом не лишняя, хотя вид выводится и из полей. Она отвечает на вопрос «какие формы есть у этого вида» без обхода всех полей, позволяет завести форму до наполнения её полями и делает проверяемым инвариант: все поля формы ссылаются на атрибуты того же вида, что указан у формы.
Обратная сторона: если один и тот же атрибут входит в состав двух видов документа — например, дата и в карту, и в лист экспертизы, — это два разных атрибута вида, и форма обязана ссылаться на нужный. Ошибка тут возможна и обнаружится только при сверке с бумагой.
3.3. Ввод и распознавание
Пакет ввода собирается по форме документа: бланк известен заранее, значит известен и ожидаемый состав атрибутов. Поэтому отдельной сущности «идентификатор пакета» нет — шифр, инвентарный номер, место хранения и дата это обычные атрибуты вида документа, и их значения хранятся наравне с остальными.
Из этого следует устройство значения: оно ссылается на атрибут вида документа обязательно, а на поле формы — только если атрибут на бланке напечатан. Место хранения поля формы не имеет: его неоткуда распознать, оно всегда вводится вручную. Ссылка на поле нужна для показа — подпись, место, подсветка фрагмента.
Заполнил пользователь значение при формировании пакета или при проверке — разницы нет, это одно и то же значение одного и того же атрибута; различается только происхождение.
Кадр относится к странице формы, а не к «роли изображения»: страница 3 формы на рукопись точнее, чем абстрактная роль. Ссылка необязательна: кадр, который обработка ни к одной странице не отнесла, считается лишним и не распознаётся.
Запрос к модели и ответ хранятся полями пакета. Пакет сериализуется в JSON по структуре формы, ответ модели сохраняется в пакет и разбирается в значения; интерфейс рендерит их обратно на структуру формы — страницы, разделы, подписи полей. Повторная обработка переписывает результат прежней: история прогонов пока не нужна.
Ключом в JSON служит код атрибута, а не подпись поля: подпись меняется от бланка к бланку, и по ней результат обратно не разложить. Форма задаёт структуру запроса и порядок, состав вида — имена.
Исправление пользователя не затирает распознанное. У значения два набора: текущий и то, что вернула модель. Причина не в аудите — пары «что модель прочла / что там на самом деле» и есть материал для дообучения, который Сервис накапливает попутно. Если исправление затирает распознанное, материал теряется навсегда.
Источник значения — свойства самого значения: кадр и область, оба необязательные. У введённого вручную источника нет, у распознанного из клетки есть и кадр, и область, у отнесённого к странице целиком — кадр без области. Отдельная сущность понадобилась бы, если бы одно значение собиралось из нескольких мест; на бланке такого не бывает.
3.4. Атрибут вида документа и связующие сущности
Атрибут вида документа — самостоятельное понятие, а не связка. Он и есть поле в составе вида: код, каноническое наименование, тип значения, обязательность, порядок, отображение в ядро карты. Общего словаря атрибутов над видами нет — атрибут заводится в виде и принадлежит ему.
Ещё две сущности существуют ради связи, и вся их ценность — в том, что на этой связи можно хранить. Без реквизитов каждая вырождается в лишнюю таблицу.
| Связующая сущность | Что связывает | Что несёт |
|---|---|---|
| Поле формы | раздел страницы ↔ атрибут вида документа | подпись на этом бланке, порядок в разделе, область на странице |
| Атрибут документа карты | документ карты ↔ атрибут вида документа | значения, происхождение (распознано или введено), кем и когда заполнено |
Разнесены они не случайно: одно и то же понятие в трёх ролях требует разных реквизитов. Обязательность поля не зависит от бланка, поэтому живёт в составе. Подпись и место зависят только от бланка, поэтому живут в форме. Кем и когда заполнено — факт конкретной карты, поэтому живёт в документе карты.
3.5. Почему словаря атрибутов нет
Общий словарь давал переиспользование: одно поле, вошедшее в состав двух видов. Вместе с ним он давал и общие ограничения — правка диапазона ради одного вида молча меняла поведение остальных, и заметить это было неоткуда: обратной связи «где ещё используется» в модели нет.
Переиспользование переехало на тип значения. Тип именован, заводится осознанно и выбирается при описании атрибута: «степень повреждения» — один тип на шесть полей листа сохранности. Общее осталось общим там, где это решение, а не побочное следствие того, что два вида сослались на одну запись словаря.
Цена: код атрибута уникален внутри вида, а не на всю систему. Сравнимость карт разных видов от этого не страдает — её обеспечивает ядро карты, где шифр издания и шифр рукописи сводятся в одну колонку. Выгрузка в АБИС тоже задаётся по видам: поля формата для издания и рукописи разные, одного правила на все виды всё равно не выходило.
Взамен появляется то, чего не было: виды изолированы. Правка состава ради одного вида другой задеть не может, потому что общего между ними не осталось.
Это и есть главный выигрыш варианта 5 против варианта 4: там значение ссылалось прямо на атрибут, и вешать на него «кем заполнено» было некуда — пришлось бы либо раздувать само значение, либо хранить сведения о заполнении на документе целиком, теряя привязку к полю.
3.6. Отличия от варианта 4
| Вариант 4 | Вариант 5 |
|---|---|
| Конфигурация документа | Вид документа с признаком DocKind |
| Виды документов не связаны между собой | Вид документа входит в состав вида — дерево описаний |
| Значение ссылается прямо на атрибут | Между ними появился атрибут документа карты; значение — его поле |
| Карта содержит один корневой документ | Карта содержит документы карты, каждый своего вида |
| Экземпляр фонда | Экземпляр фонда |
| Дата карты — часть ядра | Дата вынесена как обязательный реквизит карты: дата передачи экземпляра в обработку |
3.7. Что даёт отдельный атрибут документа карты
Появляется место, где живёт факт заполнения, отдельный и от описания, и от значения. Туда естественно ложится то, для чего в варианте 4 места не было: кем и когда заполнено, распознано или введено вручную, подтверждено ли пользователем. В варианте 4 это пришлось бы вешать либо на значение, либо на документ целиком.
Цена — лишний уровень косвенности: чтобы добраться от документа до значения, нужно пройти через две сущности вместо одной, и это же удваивает число записей при хранении.
3.8. Что даёт дерево видов
Вид документа, входящий в состав вида, позволяет описать вложение на уровне описания: «в ИК издания входит эксперт, а в эксперта — ничего». В варианте 4 то же выражено списком допустимых вложенных конфигураций с кратностью.
Разница в том, что дерево видов не хранит кратность: сказать «экспертов может быть сколько угодно, а лист сохранности ровно один» этой связью нельзя, если не добавить границы отдельно.
3.9. Вопросы к варианту
| № | Вопрос |
|---|---|
| 1 | Карта «является документом вида» и при этом содержит документы карты. Карта — корневой документ дерева или отдельная сущность над деревом? От этого зависит, есть ли у неё собственные атрибуты |
| 2 | Кратность вложения: где хранится «сколько экземпляров вложенного вида допустимо», если дерево видов её не несёт |
| 3 | Нужен ли DocKind при наличии дерева видов — что он различает сверх самого вида |
| 4 | Экземпляр фонда вместо экземпляра фонда: это переименование или другая сущность (экземпляр против издания) |
| 5 | Дата карты — дата передачи экземпляра в обработку. На бумажной форме в поле «Дата» пишут именно её или дату заполнения карты |
| 6 | Что происходит с исправлениями пользователя при повторной обработке: машина меняет только незатронутые значения или переписывает все |
| 7 | Может ли форма размещать поля вложенных видов — например, печатать эксперта прямо на листе карты, — или у каждого вида своя форма |
4. Решение
Принят вариант 5. Состав информационной карты описывается данными, причём что хранится и как это напечатано — два раздельных описания; между описанием и значением стоит факт заполнения.
- Вид документа задаёт состав: атрибуты вида и виды документов, входящие в него. Виды образуют дерево описаний, парное дереву заполненных документов.
- Атрибут вида документа — узловая сущность и есть поле в составе вида: код, каноническое наименование, тип значения, обязательность, порядок и отображение в ядро карты. На него ссылаются и поле формы, и значение в пакете, и атрибут документа карты.
- Форма документа описывает бланк: страницы, разделы, поля с подписями и местом на листе. У одного вида может быть несколько форм, версий у формы нет.
- Общего словаря атрибутов нет: атрибут принадлежит виду, и код уникален внутри вида. Тип значения — самостоятельная именованная сущность, которую выбирают при описании атрибута; переиспользование обеспечивает она. Хранится как вид значения плюс JSON-ограничения.
- Документ карты — дерево заполненных документов; атрибут документа карты несёт значения, происхождение, кем и когда заполнено.
- Информационная карта стоит над деревом: вид, дата передачи экземпляра в обработку, документы карты, типизированное ядро и собственные связи. Собственных атрибутов у неё нет — всё, что на бланке, живёт в документах.
- Значения пакета ввода ссылаются на атрибут вида обязательно, на поле формы — если атрибут напечатан. Отдельной сущности «идентификатор пакета» нет: шифр, инвентарный номер, место хранения и дата — обычные атрибуты вида.
Чем вариант 5 отличается от принятого прежде варианта 4 и что даёт взамен — в разборе варианта выше. Коротко: появилось место для факта заполнения, которого в варианте 4 не было, и связь поля формы с атрибутом в составе конкретного вида вместо инварианта, который приходилось проверять.
Варианты 1–3 отклонены. Фиксированные свойства и наследование классов не выдерживают расхождения видов ИК и редакций бланка: по образцам заказчика формы на издание и на рукопись почти не пересекаются по составу, а редакций бланка на издание уже три. Плоский список полей допускает произвольный состав и не даёт ни контроля, ни типов.
Решающий довод — свойство задачи, а не удобство реализации: Сервис оцифровывает уже существующий архив. Схема, не позволяющая внести карту, которая лежит в фонде, неприемлема, потому что исправить бумагу нельзя.
5. Рукописный текст вне клеток
На бланках встречается текст, не помещающийся ни в одно поле: приписка «нет» справа от строки степени повреждения, уточнение «перед форзацем» рядом со значением pH. Терять его нельзя — это сведения о памятнике, внесённые хранителем.
Предлагаемое решение: примечание — такой же вложенный вид документа, как эксперт. В состав вида ИК входит вид «примечание» с атрибутом текста; на каждый распознанный кусок рукописного текста заводится отдельный вложенный документ. Числа их заранее не знает никто — и не должен: вложение не ограничено по количеству.
Кадр и область при этом не нужны примечанию как таковому: это свойства значения
в пакете ввода, где у распознанного текста уже есть и кадр, и прямоугольник. Модель
распознавания возвращает такие куски отдельным списком (notes в ответе), и разбор
превращает каждый в примечание.
Что это даёт: примечание можно при проверке превратить в значение атрибута — пользователь указывает, к какому полю относится текст, — и наоборот. Отдельной механики для этого не нужно, обе стороны суть документы одной модели.
Решение помечено как предложенное: обсуждается вместе с составом видов документов.
6. Следствия
6.1. Что придётся сделать до запуска
Наполнить виды документов с их атрибутами, типы значений и формы: по образцам это сотни записей на вид ИК. Работа выполняется до начала оцифровки — без видов и форм Сервис неработоспособен. Это самостоятельный объём, который стоит спланировать отдельно от разработки.
Атрибуты, совпадающие у издания и рукописи, заводятся дважды: словаря, из которого их можно было бы взять готовыми, больше нет. По образцам совпадений немного — шифр, дата, ФИО составителя, — но при заведении новых видов объём стоит держать в уме (OQ-A6-14).
6.2. Что становится дешевле
Новая редакция бланка — новая форма из тех же атрибутов вида: ни правки кода, ни миграции, ни версионирования. Новый вид документа — новая запись дерева видов. Экраны проверки и выгрузка пишутся один раз на все виды карт.
6.3. За чем следить
Рассинхронизация ядра и значений. Ядро — денормализованная проекция; при правке значения его надо пересчитывать. Ошибка здесь тихая: реестр показывает одно, карта содержит другое.
Путаница двух деревьев. Дерево документов описывает данные, дерево разделов — бумагу. Понятие, положенное не в то дерево, обнаружится поздно и будет стоить дорого.
Соглашение о списках временное. Правило «значение из единственного поля бланка добавляется в список» принято до проработки распознавания и подлежит пересмотру.
Правка типа значения задевает всех, кто им пользуется. Это и есть смысл общего типа, но интерфейс ведения обязан отвечать на вопрос «где используется» — иначе общность возвращает ту же тихую поломку, от которой ушли, только этажом выше.
Лишний уровень косвенности. Путь от документа до значения проходит через две сущности вместо одной, и это удваивает число записей при хранении. Плата за то, чтобы факту заполнения было где жить.
6.4. Что решение не закрывает
Отнесение аллигат — к карте или к экземпляру фонда — остаётся открытым (см. ниже).
7. Вопросы, закрытые решением
| Вопрос | Ответ |
|---|---|
| Различать ли редакции бланка одного вида ИК | Различать нечего: версий нет, другая редакция — просто другая форма документа |
| Нужен ли отдельный тип значения под отметку в клетке | Да, добавлен вид bool; выражать отметку выбором из набора в одно значение было бы менее наглядно |
| Кто ведёт виды документов, типы значений и формы | Администратор Сервиса; на первом этапе фактически разработчик |
| Как не дать правке ради одного вида сломать другой | Атрибут принадлежит виду; общим остаётся только тип значения, и он выбирается осознанно |
| Чем обеспечена сравнимость карт разных видов без общего словаря | Ядром карты: шифр, инвентарный номер, заглавие, автор и год сводятся в типизированные колонки |
| Где хранятся значения справочных наборов — тиснение, материалы покрытия, степень повреждения | В ограничениях типа значения, парами «код — значение». Отдельное хранилище понадобится только для больших наборов: первый кандидат — язык издания, 589 значений |
| Нужен ли поиск по значению за пределами ядра | Нет. Понадобился поиск по полю — поле переносится в ядро |
| Как версионируются виды документов | Не версионируются. Изменение вида на созданные документы не влияет: значения опознаются кодом атрибута |
| Хранить ли в виде документа область поля на изображении | Нет. Вид описывает состав; область относится к форме документа |
| Как выразить «экспертов сколько угодно» | Самим вложением: вид «эксперт» входит в состав вида ИК, и число вложенных документов этого вида не ограничено. Отдельная кратность не нужна |
| Как называется единица фонда | Экземпляр фонда. Слово «документ» занято абстрактным документом модели, и оставлять «документ фонда» рядом с «документом карты» нельзя |
8. Открытые вопросы
| № | Вопрос |
|---|---|
| OQ-A6-1 | Аллигаты — часть информационной карты или часть экземпляра фонда; от этого зависит, в какое дерево они попадают |
| OQ-A6-11 | Нужен ли код вида (DocKind) при наличии дерева видов — что он различает сверх самого вида |
| OQ-A6-12 | Дата карты определена как дата передачи экземпляра в обработку. Проверить по бумаге: в поле «Дата» пишут её или дату заполнения карты |
| OQ-A6-13 | Может ли форма размещать поля вложенных видов — печатать эксперта прямо на листе карты — или у каждого вида своя форма |
| OQ-A6-14 | Нужно ли при заведении вида копировать состав атрибутов с существующего вида, чтобы не набивать сотни записей заново |
Структурные связи
Используется
- Запрос на изменение CR 2026-08-14. Ввод без плана ввода
- Запрос на изменение CR 2026-08-25. Обязательные поля информационной карты